Compare commits

..

1 Commits

Author SHA1 Message Date
Amol
f5c91f82b6 DEVTOOLS-162: This is a capability / simulator device type for Beacon. 2016-08-15 18:41:23 -05:00
303 changed files with 6886 additions and 7907 deletions

View File

@@ -1,10 +1,10 @@
# SmartThings Public GitHub Repo # SmartThings Public Github Repo
An official list of SmartApps and Device Types from SmartThings. An official list of SmartApps and Device Types from SmartThings.
Here are some links to help you get started coding right away: Here are some links to help you get started coding right away:
* [GitHub-specific Documentation](http://docs.smartthings.com/en/latest/tools-and-ide/github-integration.html) * [Github-specific Documentation](http://docs.smartthings.com/en/latest/tools-and-ide/github-integration.html)
* [Full Documentation](http://docs.smartthings.com) * [Full Documentation](http://docs.smartthings.com)
* [IDE & Simulator](http://ide.smartthings.com) * [IDE & Simulator](http://ide.smartthings.com)
* [Community Forums](http://community.smartthings.com) * [Community Forums](http://community.smartthings.com)

View File

@@ -9,7 +9,7 @@ apply plugin: 'smartthings-slack'
buildscript { buildscript {
dependencies { dependencies {
classpath "com.smartthings.deployment:executable-deployment-scripts:1.0.11" classpath "com.smartthings.deployment:executable-deployment-scripts:1.0.8"
} }
repositories { repositories {
mavenLocal() mavenLocal()
@@ -19,7 +19,7 @@ buildscript {
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,37 +27,9 @@ 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 { slackSendMessage {

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:

View File

@@ -23,8 +23,8 @@ metadata {
tiles { tiles {
standardTile("acceleration", "device.acceleration", width: 2, height: 2) { standardTile("acceleration", "device.acceleration", width: 2, height: 2) {
state("inactive", label:'${name}', icon:"st.motion.acceleration.inactive", backgroundColor:"#cccccc") state("inactive", label:'${name}', icon:"st.motion.acceleration.inactive", backgroundColor:"#ffffff")
state("active", label:'${name}', icon:"st.motion.acceleration.active", backgroundColor:"#00A0DC") state("active", label:'${name}', icon:"st.motion.acceleration.active", backgroundColor:"#53a7c0")
} }
main "acceleration" main "acceleration"

View File

@@ -0,0 +1,37 @@
/**
* Copyright 2015 SmartThings
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under the License.
*
*/
metadata {
definition (name: "Beacon Capability", namespace: "capabilities", author: "SmartThings") {
capability "Beacon"
}
simulator {
status "present": "beacon: present"
status "not present": "beacon: not present"
}
tiles {
standardTile("beacon", "device.beacon", width: 2, height: 2) {
state("not present", label:'not present', icon:"st.presence.tile.not-present", backgroundColor:"#ffffff")
state("present", label:'present', icon:"st.presence.tile.present", backgroundColor:"#53a7c0")
}
main "beacon"
details "beacon"
}
}
def parse(String description) {
def pair = description.split(":")
createEvent(name: pair[0].trim(), value: pair[1].trim())
}

View File

@@ -23,8 +23,8 @@ metadata {
tiles { tiles {
standardTile("contact", "device.contact", width: 2, height: 2) { standardTile("contact", "device.contact", width: 2, height: 2) {
state("closed", label:'${name}', icon:"st.contact.contact.closed", backgroundColor:"#00A0DC") state("closed", label:'${name}', icon:"st.contact.contact.closed", backgroundColor:"#79b821")
state("open", label:'${name}', icon:"st.contact.contact.open", backgroundColor:"#e86d13") state("open", label:'${name}', icon:"st.contact.contact.open", backgroundColor:"#ffa81e")
} }
main "contact" main "contact"
details "contact" details "contact"

View File

@@ -27,7 +27,7 @@ metadata {
tiles { tiles {
standardTile("toggle", "device.lock", width: 2, height: 2) { standardTile("toggle", "device.lock", width: 2, height: 2) {
state "unlocked", label:'unlocked', action:"lock.lock", icon:"st.locks.lock.unlocked", backgroundColor:"#ffffff" state "unlocked", label:'unlocked', action:"lock.lock", icon:"st.locks.lock.unlocked", backgroundColor:"#ffffff"
state "locked", label:'locked', action:"lock.unlock", icon:"st.locks.lock.locked", backgroundColor:"#00A0DC" state "locked", label:'locked', action:"lock.unlock", icon:"st.locks.lock.locked", backgroundColor:"#79b821"
} }
standardTile("lock", "device.lock", inactiveLabel: false, decoration: "flat") { standardTile("lock", "device.lock", inactiveLabel: false, decoration: "flat") {
state "default", label:'lock', action:"lock.lock", icon:"st.locks.lock.locked" state "default", label:'lock', action:"lock.lock", icon:"st.locks.lock.locked"

View File

@@ -29,7 +29,7 @@ metadata {
tiles { tiles {
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) { standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
state "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff", nextState: "on" state "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff", nextState: "on"
state "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#00A0DC" state "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821"
} }
main "switch" main "switch"
details "switch" details "switch"

View File

@@ -24,7 +24,7 @@ metadata {
tiles { tiles {
standardTile("motion", "device.motion", width: 2, height: 2) { standardTile("motion", "device.motion", width: 2, height: 2) {
state("inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#ffffff") state("inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#ffffff")
state("active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#00A0DC") state("active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#53a7c0")
} }
main "motion" main "motion"
details "motion" details "motion"

View File

@@ -24,7 +24,7 @@ metadata {
tiles { tiles {
standardTile("presence", "device.presence", width: 2, height: 2) { standardTile("presence", "device.presence", width: 2, height: 2) {
state("not present", label:'not present', icon:"st.presence.tile.not-present", backgroundColor:"#ffffff") state("not present", label:'not present', icon:"st.presence.tile.not-present", backgroundColor:"#ffffff")
state("present", label:'present', icon:"st.presence.tile.present", backgroundColor:"#00A0DC") state("present", label:'present', icon:"st.presence.tile.present", backgroundColor:"#53a7c0")
} }
main "presence" main "presence"
details "presence" details "presence"

View File

@@ -31,7 +31,7 @@ metadata {
tiles { tiles {
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) { standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
state "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff" state "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff"
state "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#00A0DC" state "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821"
} }
main "switch" main "switch"
details "switch" details "switch"

View File

@@ -35,8 +35,8 @@ metadata {
tiles { tiles {
standardTile("switch", "device.switch", width: 2, height: 2) { standardTile("switch", "device.switch", width: 2, height: 2) {
state "off", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn" state "off", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn"
state "on", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#00A0DC", nextState:"turningOff" state "on", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#79b821", nextState:"turningOff"
state "turningOn", label:'${name}', icon:"st.switches.switch.on", backgroundColor:"#00A0DC" state "turningOn", label:'${name}', icon:"st.switches.switch.on", backgroundColor:"#79b821"
state "turningOff", label:'${name}', icon:"st.switches.switch.off", backgroundColor:"#ffffff" state "turningOff", label:'${name}', icon:"st.switches.switch.off", backgroundColor:"#ffffff"
} }
controlTile("levelSliderControl", "device.level", "slider", height: 2, width: 1, inactiveLabel: false) { controlTile("levelSliderControl", "device.level", "slider", height: 2, width: 1, inactiveLabel: false) {

View File

@@ -79,8 +79,8 @@ metadata {
standardTile("mode", "device.thermostatMode", inactiveLabel: false, decoration: "flat") { standardTile("mode", "device.thermostatMode", inactiveLabel: false, decoration: "flat") {
state "off", label:'${name}', action:"thermostat.emergencyHeat", backgroundColor:"#ffffff" state "off", label:'${name}', action:"thermostat.emergencyHeat", backgroundColor:"#ffffff"
state "emergencyHeat", label:'${name}', action:"thermostat.heat", backgroundColor:"#e86d13" state "emergencyHeat", label:'${name}', action:"thermostat.heat", backgroundColor:"#e86d13"
state "heat", label:'${name}', action:"thermostat.cool", backgroundColor:"#e86d13" state "heat", label:'${name}', action:"thermostat.cool", backgroundColor:"#ffc000"
state "cool", label:'${name}', action:"thermostat.off", backgroundColor:"#00A0DC" state "cool", label:'${name}', action:"thermostat.off", backgroundColor:"#269bd2"
} }
standardTile("fanMode", "device.thermostatFanMode", inactiveLabel: false, decoration: "flat") { standardTile("fanMode", "device.thermostatFanMode", inactiveLabel: false, decoration: "flat") {
state "fanAuto", label:'${name}', action:"thermostat.fanOn", backgroundColor:"#ffffff" state "fanAuto", label:'${name}', action:"thermostat.fanOn", backgroundColor:"#ffffff"

View File

@@ -24,7 +24,7 @@ metadata {
tiles { tiles {
standardTile("water", "device.water", width: 2, height: 2) { standardTile("water", "device.water", width: 2, height: 2) {
state "dry", icon:"st.alarm.water.dry", backgroundColor:"#ffffff" state "dry", icon:"st.alarm.water.dry", backgroundColor:"#ffffff"
state "wet", icon:"st.alarm.water.wet", backgroundColor:"#00A0DC" state "wet", icon:"st.alarm.water.wet", backgroundColor:"#53a7c0"
} }
main "water" main "water"

View File

@@ -37,7 +37,7 @@ metadata {
tiles { tiles {
standardTile("presence", "device.presence", width: 2, height: 2, canChangeBackground: true) { standardTile("presence", "device.presence", width: 2, height: 2, canChangeBackground: true) {
state("present", labelIcon:"st.presence.tile.present", backgroundColor:"#00A0DC") state("present", labelIcon:"st.presence.tile.present", backgroundColor:"#53a7c0")
state("not present", labelIcon:"st.presence.tile.not-present", backgroundColor:"#ffffff") state("not present", labelIcon:"st.presence.tile.not-present", backgroundColor:"#ffffff")
} }
valueTile("inRange", "device.inRangeFriendly", inactiveLabel: true, height:1, width:3, decoration: "flat") { valueTile("inRange", "device.inRangeFriendly", inactiveLabel: true, height:1, width:3, decoration: "flat") {

View File

@@ -38,7 +38,7 @@ metadata {
// Main // Main
standardTile("main", "device.status", width: 1, height: 1, canChangeIcon: true) { standardTile("main", "device.status", width: 1, height: 1, canChangeIcon: true) {
state "paused", label:'Paused', action:"music Player.play", icon:"st.Electronics.electronics19", nextState:"playing", backgroundColor:"#ffffff" state "paused", label:'Paused', action:"music Player.play", icon:"st.Electronics.electronics19", nextState:"playing", backgroundColor:"#ffffff"
state "playing", label:'Playing', action:"music Player.pause", icon:"st.Electronics.electronics19", nextState:"paused", backgroundColor:"#00A0DC" state "playing", label:'Playing', action:"music Player.pause", icon:"st.Electronics.electronics19", nextState:"paused", backgroundColor:"#79b821"
} }
// Row 1 // Row 1

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

@@ -34,8 +34,8 @@ metadata {
tiles(scale: 2) { tiles(scale: 2) {
multiAttributeTile(name:"FGK", type:"lighting", width:6, height:4) {//with generic type secondary control text is not displayed in Android app 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") { tileAttribute("device.contact", key:"PRIMARY_CONTROL") {
attributeState("open", icon:"st.contact.contact.open", backgroundColor:"#e86d13") attributeState("open", icon:"st.contact.contact.open", backgroundColor:"#ffa81e")
attributeState("closed", icon:"st.contact.contact.closed", backgroundColor:"#00a0dc") attributeState("closed", icon:"st.contact.contact.closed", backgroundColor:"#79b821")
} }
tileAttribute("device.tamper", key:"SECONDARY_CONTROL") { tileAttribute("device.tamper", key:"SECONDARY_CONTROL") {
@@ -44,7 +44,7 @@ metadata {
} }
} }
valueTile("battery", "device.battery", inactiveLabel: false, width: 2, height: 2, decoration: "flat") { valueTile("battery", "device.battery", inactiveLabel: false, , width: 2, height: 2, decoration: "flat") {
state "battery", label:'${currentValue}% battery', unit:"" state "battery", label:'${currentValue}% battery', unit:""
} }

View File

@@ -31,8 +31,8 @@ metadata {
tiles(scale: 2) { tiles(scale: 2) {
multiAttributeTile(name:"FGK", type:"lighting", width:6, height:4) {//with generic type secondary control text is not displayed in Android app 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") { tileAttribute("device.contact", key:"PRIMARY_CONTROL") {
attributeState("open", icon:"st.contact.contact.open", backgroundColor:"#e86d13") attributeState("open", icon:"st.contact.contact.open", backgroundColor:"#ffa81e")
attributeState("closed", icon:"st.contact.contact.closed", backgroundColor:"#00a0dc") attributeState("closed", icon:"st.contact.contact.closed", backgroundColor:"#79b821")
} }
tileAttribute("device.tamper", key:"SECONDARY_CONTROL") { tileAttribute("device.tamper", key:"SECONDARY_CONTROL") {
@@ -41,7 +41,7 @@ metadata {
} }
} }
valueTile("battery", "device.battery", inactiveLabel: false, width: 2, height: 2, decoration: "flat") { valueTile("battery", "device.battery", inactiveLabel: false, , width: 2, height: 2, decoration: "flat") {
state "battery", label:'${currentValue}% battery', unit:"" state "battery", label:'${currentValue}% battery', unit:""
} }

View File

@@ -21,7 +21,6 @@ metadata {
capability "Tamper Alert" capability "Tamper Alert"
capability "Temperature Measurement" capability "Temperature Measurement"
capability "Water Sensor" capability "Water Sensor"
capability "Health Check"
fingerprint deviceId: "0x0701", inClusters: "0x5E, 0x22, 0x85, 0x59, 0x20, 0x80, 0x70, 0x56, 0x5A, 0x7A, 0x72, 0x8E, 0x71, 0x73, 0x98, 0x9C, 0x31, 0x86", outClusters: "" fingerprint deviceId: "0x0701", inClusters: "0x5E, 0x22, 0x85, 0x59, 0x20, 0x80, 0x70, 0x56, 0x5A, 0x7A, 0x72, 0x8E, 0x71, 0x73, 0x98, 0x9C, 0x31, 0x86", outClusters: ""
} }
@@ -33,13 +32,13 @@ metadata {
tiles(scale: 2) { tiles(scale: 2) {
multiAttributeTile(name:"FGFS", type:"lighting", width:6, height:4) {//with generic type secondary control text is not displayed in Android app 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") { tileAttribute("device.water", key:"PRIMARY_CONTROL") {
attributeState("dry", icon:"st.alarm.water.dry", backgroundColor:"#00a0dc") attributeState("dry", icon:"st.alarm.water.dry", backgroundColor:"#79b821")
attributeState("wet", icon:"st.alarm.water.wet", backgroundColor:"#e86d13") attributeState("wet", icon:"st.alarm.water.wet", backgroundColor:"#ffa81e")
} }
tileAttribute("device.tamper", key:"SECONDARY_CONTROL") { tileAttribute("device.tamper", key:"SECONDARY_CONTROL") {
attributeState("active", label:'tamper active', backgroundColor:"#00a0dc") attributeState("active", label:'tamper active', backgroundColor:"#53a7c0")
attributeState("inactive", label:'tamper inactive', backgroundColor:"#cccccc") attributeState("inactive", label:'tamper inactive', backgroundColor:"#ffffff")
} }
} }
@@ -229,8 +228,6 @@ def zwaveEvent(physicalgraph.zwave.commands.deviceresetlocallyv1.DeviceResetLoca
def configure() { def configure() {
log.debug "Executing 'configure'" log.debug "Executing 'configure'"
// Device-Watch simply pings if no device events received for 8 hrs & 2 minutes
sendEvent(name: "checkInterval", value: 8 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
def cmds = [] def cmds = []

View File

@@ -22,7 +22,6 @@ metadata {
capability "Sensor" capability "Sensor"
capability "Tamper Alert" capability "Tamper Alert"
capability "Temperature Measurement" capability "Temperature Measurement"
capability "Health Check"
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: "" 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: ""
} }
@@ -34,13 +33,13 @@ metadata {
tiles(scale: 2) { tiles(scale: 2) {
multiAttributeTile(name:"FGMS", type:"lighting", width:6, height:4) {//with generic type secondary control text is not displayed in Android app 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") { tileAttribute("device.motion", key:"PRIMARY_CONTROL") {
attributeState("inactive", label:"no motion", icon:"st.motion.motion.inactive", backgroundColor:"#79b821") attributeState("inactive", icon:"st.motion.motion.inactive", backgroundColor:"#79b821")
attributeState("active", label:"motion", icon:"st.motion.motion.active", backgroundColor:"#ffa81e") attributeState("active", icon:"st.motion.motion.active", backgroundColor:"#ffa81e")
} }
tileAttribute("device.tamper", key:"SECONDARY_CONTROL") { tileAttribute("device.tamper", key:"SECONDARY_CONTROL") {
attributeState("active", label:'tamper active', backgroundColor:"#00a0dc") attributeState("active", label:'tamper active', backgroundColor:"#53a7c0")
attributeState("inactive", label:'tamper inactive', backgroundColor:"#cccccc") attributeState("inactive", label:'tamper inactive', backgroundColor:"#ffffff")
} }
} }
@@ -128,10 +127,9 @@ def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelR
def map = [ displayed: true ] def map = [ displayed: true ]
switch (cmd.sensorType) { switch (cmd.sensorType) {
case 1: case 1:
def cmdScale = cmd.scale == 1 ? "F" : "C" map.name = "temperature"
map.name = "temperature" map.unit = cmd.scale == 1 ? "F" : "C"
map.unit = getTemperatureScale() map.value = convertTemperatureIfNeeded(cmd.scaledSensorValue, map.unit, cmd.precision)
map.value = convertTemperatureIfNeeded(cmd.scaledSensorValue, cmdScale, cmd.precision)
break break
case 3: case 3:
map.name = "illuminance" map.name = "illuminance"
@@ -241,8 +239,6 @@ def zwaveEvent(physicalgraph.zwave.commands.deviceresetlocallyv1.DeviceResetLoca
def configure() { def configure() {
log.debug "Executing 'configure'" log.debug "Executing 'configure'"
// Device-Watch simply pings if no device events received for 8 hrs & 2 minutes
sendEvent(name: "checkInterval", value: 8 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
def cmds = [] def cmds = []

View File

@@ -112,10 +112,10 @@ metadata {
multiAttributeTile(name:"dtlsDoorCtrl", type:"generic", width:6, height:4) {tileAttribute("device.doorState", key: "PRIMARY_CONTROL") 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 "unknown", label: '${name}', action:"openDoor", icon: "st.secondary.tools", nextState:"Sent"
attributeState "open", label: '${name}', action:"closeDoor", icon: "st.doors.garage.garage-open", backgroundColor: "#00A0DC", 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: "#00A0DC" 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: "#ffffff", nextState:"Sent" 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: "#ffffff" 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 "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 "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 "fault", label: 'FAULT', action:"openDoor", icon: "st.secondary.tools", backgroundColor: "#ff0000", nextState:"Sent"

View File

@@ -24,7 +24,7 @@ metadata {
tiles { tiles {
standardTile("sleeping", "device.sleeping", width: 1, height: 1, canChangeIcon: false, canChangeBackground: false) { standardTile("sleeping", "device.sleeping", width: 1, height: 1, canChangeIcon: false, canChangeBackground: false) {
state("sleeping", label: "Sleeping", icon:"st.Bedroom.bedroom12", backgroundColor:"#ffffff") state("sleeping", label: "Sleeping", icon:"st.Bedroom.bedroom12", backgroundColor:"#ffffff")
state("not sleeping", label: "Awake", icon:"st.Health & Wellness.health12", backgroundColor:"#00A0DC") state("not sleeping", label: "Awake", icon:"st.Health & Wellness.health12", backgroundColor:"#79b821")
} }
standardTile("steps", "device.steps", width: 2, height: 2, canChangeIcon: false, canChangeBackground: false) { standardTile("steps", "device.steps", width: 2, height: 2, canChangeIcon: false, canChangeBackground: false) {
state("steps", label: '${currentValue} Steps', icon:"st.Health & Wellness.health11", backgroundColor:"#ffffff") state("steps", label: '${currentValue} Steps', icon:"st.Health & Wellness.health11", backgroundColor:"#ffffff")

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

@@ -122,13 +122,13 @@ metadata {
state "raintoday", label: 'Rain Today', icon: "st.custom.wuk.nt_chancerain" state "raintoday", label: 'Rain Today', icon: "st.custom.wuk.nt_chancerain"
state "rainy", label: 'Previous Rain', icon: "st.custom.wuk.nt_chancerain" state "rainy", label: 'Previous Rain', icon: "st.custom.wuk.nt_chancerain"
state "raintom", label: 'Rain Tomorrow', icon: "st.custom.wuk.nt_chancerain" state "raintom", label: 'Rain Tomorrow', icon: "st.custom.wuk.nt_chancerain"
state "donewweek", label: 'Spruce Finished', icon: "st.Outdoor.outdoor5", backgroundColor: "#00A0DC" state "donewweek", label: 'Spruce Finished', icon: "st.Outdoor.outdoor5", backgroundColor: "#52c435"
state "skipping", label: 'Skip Today', icon: "st.Outdoor.outdoor20", backgroundColor: "#36cfe3" state "skipping", label: 'Skip Today', icon: "st.Outdoor.outdoor20", backgroundColor: "#36cfe3"
state "moisture", label: '', icon: "st.Weather.weather2", backgroundColor: "#36cfe3" state "moisture", label: '', icon: "st.Weather.weather2", backgroundColor: "#36cfe3"
state "pause", label: 'PAUSE', icon: "st.contact.contact.open", backgroundColor: "#e86d13" state "pause", label: 'PAUSE', icon: "st.contact.contact.open", backgroundColor: "#f2a51f"
state "active", label: 'Active', icon: "st.Outdoor.outdoor12", backgroundColor: "#3DC72E" state "active", label: 'Active', icon: "st.Outdoor.outdoor12", backgroundColor: "#3DC72E"
state "season", label: 'Seasonal Adjustment', icon: "st.Outdoor.outdoor17", backgroundColor: "#ffb900" state "season", label: 'Seasonal Adjustment', icon: "st.Outdoor.outdoor17", backgroundColor: "#ffb900"
state "disable", label: 'Disabled', icon: "st.secondary.off", backgroundColor: "#cccccc" state "disable", label: 'Disabled', icon: "st.secondary.off", backgroundColor: "#888888"
state "warning", label: '', icon: "st.categories.damageAndDanger", backgroundColor: "#ffff7f" state "warning", label: '', icon: "st.categories.damageAndDanger", backgroundColor: "#ffff7f"
state "alarm", label: 'Alarm', icon: "st.categories.damageAndDanger", backgroundColor: "#f9240c" state "alarm", label: 'Alarm', icon: "st.categories.damageAndDanger", backgroundColor: "#f9240c"
} }
@@ -144,67 +144,67 @@ metadata {
} }
standardTile("switch1", "device.switch1") { standardTile("switch1", "device.switch1") {
state "z1off", label: '1', action: "z1on", icon: "st.valves.water.closed", backgroundColor: "#ffffff" state "z1off", label: '1', action: "z1on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
state "z1on", label: '1', action: "z1off", icon: "st.valves.water.open", backgroundColor: "#00A0DC" state "z1on", label: '1', action: "z1off", icon: "st.valves.water.open", backgroundColor: "#46c2e8"
} }
standardTile("switch2", "device.switch2") { standardTile("switch2", "device.switch2") {
state "z2off", label: '2', action: "z2on", icon: "st.valves.water.closed", backgroundColor: "#ffffff" state "z2off", label: '2', action: "z2on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
state "z2on", label: '2', action: "z2off", icon: "st.valves.water.open", backgroundColor: "#00A0DC" state "z2on", label: '2', action: "z2off", icon: "st.valves.water.open", backgroundColor: "#46c2e8"
} }
standardTile("switch3", "device.switch3", inactiveLabel: false) { standardTile("switch3", "device.switch3", inactiveLabel: false) {
state "z3off", label: '3', action: "z3on", icon: "st.valves.water.closed", backgroundColor: "#ffffff" state "z3off", label: '3', action: "z3on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
state "z3on", label: '3', action: "z3off", icon: "st.valves.water.open", backgroundColor: "#00A0DC" state "z3on", label: '3', action: "z3off", icon: "st.valves.water.open", backgroundColor: "#46c2e8"
} }
standardTile("switch4", "device.switch4", inactiveLabel: false) { standardTile("switch4", "device.switch4", inactiveLabel: false) {
state "z4off", label: '4', action: "z4on", icon: "st.valves.water.closed", backgroundColor: "#ffffff" state "z4off", label: '4', action: "z4on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
state "z4on", label: '4', action: "z4off", icon: "st.valves.water.open", backgroundColor: "#00A0DC" state "z4on", label: '4', action: "z4off", icon: "st.valves.water.open", backgroundColor: "#46c2e8"
} }
standardTile("switch5", "device.switch5", inactiveLabel: false) { standardTile("switch5", "device.switch5", inactiveLabel: false) {
state "z5off", label: '5', action: "z5on", icon: "st.valves.water.closed", backgroundColor: "#ffffff" state "z5off", label: '5', action: "z5on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
state "z5on", label: '5', action: "z5off", icon: "st.valves.water.open", backgroundColor: "#00A0DC" state "z5on", label: '5', action: "z5off", icon: "st.valves.water.open", backgroundColor: "#46c2e8"
} }
standardTile("switch6", "device.switch6", inactiveLabel: false) { standardTile("switch6", "device.switch6", inactiveLabel: false) {
state "z6off", label: '6', action: "z6on", icon: "st.valves.water.closed", backgroundColor: "#ffffff" state "z6off", label: '6', action: "z6on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
state "z6on", label: '6', action: "z6off", icon: "st.valves.water.open", backgroundColor: "#00A0DC" state "z6on", label: '6', action: "z6off", icon: "st.valves.water.open", backgroundColor: "#46c2e8"
} }
standardTile("switch7", "device.switch7", inactiveLabel: false) { standardTile("switch7", "device.switch7", inactiveLabel: false) {
state "z7off", label: '7', action: "z7on", icon: "st.valves.water.closed", backgroundColor: "#ffffff" state "z7off", label: '7', action: "z7on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
state "z7on", label: '7', action: "z7off", icon: "st.valves.water.open", backgroundColor: "#00A0DC" state "z7on", label: '7', action: "z7off", icon: "st.valves.water.open", backgroundColor: "#46c2e8"
} }
standardTile("switch8", "device.switch8", inactiveLabel: false) { standardTile("switch8", "device.switch8", inactiveLabel: false) {
state "z8off", label: '8', action: "z8on", icon: "st.valves.water.closed", backgroundColor: "#ffffff" state "z8off", label: '8', action: "z8on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
state "z8on", label: '8', action: "z8off", icon: "st.valves.water.open", backgroundColor: "#00A0DC" state "z8on", label: '8', action: "z8off", icon: "st.valves.water.open", backgroundColor: "#46c2e8"
} }
standardTile("switch9", "device.switch9", inactiveLabel: false) { standardTile("switch9", "device.switch9", inactiveLabel: false) {
state "z9off", label: '9', action: "z9on", icon: "st.valves.water.closed", backgroundColor: "#ffffff" state "z9off", label: '9', action: "z9on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
state "z9on", label: '9', action: "z9off", icon: "st.valves.water.open", backgroundColor: "#00A0DC" state "z9on", label: '9', action: "z9off", icon: "st.valves.water.open", backgroundColor: "#46c2e8"
} }
standardTile("switch10", "device.switch10", inactiveLabel: false) { standardTile("switch10", "device.switch10", inactiveLabel: false) {
state "z10off", label: '10', action: "z10on", icon: "st.valves.water.closed", backgroundColor: "#ffffff" state "z10off", label: '10', action: "z10on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
state "z10on", label: '10', action: "z10off", icon: "st.valves.water.open", backgroundColor: "#00A0DC" state "z10on", label: '10', action: "z10off", icon: "st.valves.water.open", backgroundColor: "#46c2e8"
} }
standardTile("switch11", "device.switch11", inactiveLabel: false) { standardTile("switch11", "device.switch11", inactiveLabel: false) {
state "z11off", label: '11', action: "z11on", icon: "st.valves.water.closed", backgroundColor: "#ffffff" state "z11off", label: '11', action: "z11on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
state "z11on", label: '11', action: "z11off", icon: "st.valves.water.open", backgroundColor: "#00A0DC" state "z11on", label: '11', action: "z11off", icon: "st.valves.water.open", backgroundColor: "#46c2e8"
} }
standardTile("switch12", "device.switch12", inactiveLabel: false) { standardTile("switch12", "device.switch12", inactiveLabel: false) {
state "z12off", label: '12', action: "z12on", icon: "st.valves.water.closed", backgroundColor: "#ffffff" state "z12off", label: '12', action: "z12on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
state "z12on", label: '12', action: "z12off", icon: "st.valves.water.open", backgroundColor: "#00A0DC" state "z12on", label: '12', action: "z12off", icon: "st.valves.water.open", backgroundColor: "#46c2e8"
} }
standardTile("switch13", "device.switch13", inactiveLabel: false) { standardTile("switch13", "device.switch13", inactiveLabel: false) {
state "z13off", label: '13', action: "z13on", icon: "st.valves.water.closed", backgroundColor: "#ffffff" state "z13off", label: '13', action: "z13on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
state "z13on", label: '13', action: "z13off", icon: "st.valves.water.open", backgroundColor: "#00A0DC" state "z13on", label: '13', action: "z13off", icon: "st.valves.water.open", backgroundColor: "#46c2e8"
} }
standardTile("switch14", "device.switch14", inactiveLabel: false) { standardTile("switch14", "device.switch14", inactiveLabel: false) {
state "z14off", label: '14', action: "z14on", icon: "st.valves.water.closed", backgroundColor: "#ffffff" state "z14off", label: '14', action: "z14on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
state "z14on", label: '14', action: "z14off", icon: "st.valves.water.open", backgroundColor: "#00A0DC" state "z14on", label: '14', action: "z14off", icon: "st.valves.water.open", backgroundColor: "#46c2e8"
} }
standardTile("switch15", "device.switch15", inactiveLabel: false) { standardTile("switch15", "device.switch15", inactiveLabel: false) {
state "z15off", label: '15', action: "z15on", icon: "st.valves.water.closed", backgroundColor: "#ffffff" state "z15off", label: '15', action: "z15on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
state "z15on", label: '15', action: "z15off", icon: "st.valves.water.open", backgroundColor: "#00A0DC" state "z15on", label: '15', action: "z15off", icon: "st.valves.water.open", backgroundColor: "#46c2e8"
} }
standardTile("switch16", "device.switch16", inactiveLabel: false) { standardTile("switch16", "device.switch16", inactiveLabel: false) {
state "z16off", label: '16', action: "z16on", icon: "st.valves.water.closed", backgroundColor: "#ffffff" state "z16off", label: '16', action: "z16on", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
state "z16on", label: '16', action: "z16off", icon: "st.valves.water.open", backgroundColor: "#00A0DC" state "z16on", label: '16', action: "z16off", icon: "st.valves.water.open", backgroundColor: "#46c2e8"
} }
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") { standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") {
state "default", action: "refresh", icon:"st.secondary.refresh" state "default", action: "refresh", icon:"st.secondary.refresh"

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

@@ -47,9 +47,9 @@ metadata {
tiles { tiles {
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) { standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
state "on", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#00A0DC", nextState:"turningOff" state "on", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#79b821", nextState:"turningOff"
state "off", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn" state "off", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn"
state "turningOn", label:'${name}', icon:"st.switches.switch.on", backgroundColor:"#00A0DC" state "turningOn", label:'${name}', icon:"st.switches.switch.on", backgroundColor:"#79b821"
state "turningOff", label:'${name}', icon:"st.switches.switch.off", backgroundColor:"#ffffff" state "turningOff", label:'${name}', icon:"st.switches.switch.off", backgroundColor:"#ffffff"
} }
controlTile("levelSliderControl", "device.level", "slider", height: 2, width: 1, inactiveLabel: false) { controlTile("levelSliderControl", "device.level", "slider", height: 2, width: 1, inactiveLabel: false) {

View File

@@ -15,7 +15,6 @@ metadata {
definition (name: "Aeon Key Fob", namespace: "smartthings", author: "SmartThings") { definition (name: "Aeon Key Fob", namespace: "smartthings", author: "SmartThings") {
capability "Actuator" capability "Actuator"
capability "Button" capability "Button"
capability "Holdable Button"
capability "Configuration" capability "Configuration"
capability "Sensor" capability "Sensor"
capability "Battery" capability "Battery"
@@ -37,14 +36,14 @@ metadata {
tiles { tiles {
standardTile("button", "device.button", width: 2, height: 2) { standardTile("button", "device.button", width: 2, height: 2) {
state "default", label: "", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#ffffff" 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: "#00A0DC" state "button 1 pushed", label: "pushed #1", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#79b821"
state "button 2 pushed", label: "pushed #2", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#00A0DC" state "button 2 pushed", label: "pushed #2", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#79b821"
state "button 3 pushed", label: "pushed #3", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#00A0DC" state "button 3 pushed", label: "pushed #3", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#79b821"
state "button 4 pushed", label: "pushed #4", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#00A0DC" state "button 4 pushed", label: "pushed #4", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#79b821"
state "button 1 held", label: "held #1", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#e86d13" state "button 1 held", label: "held #1", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#ffa81e"
state "button 2 held", label: "held #2", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#e86d13" state "button 2 held", label: "held #2", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#ffa81e"
state "button 3 held", label: "held #3", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#e86d13" state "button 3 held", label: "held #3", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#ffa81e"
state "button 4 held", label: "held #4", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#e86d13" state "button 4 held", label: "held #4", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#ffa81e"
} }
valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat") { valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat") {
state "battery", label:'${currentValue}% battery', unit:"" state "battery", label:'${currentValue}% battery', unit:""
@@ -119,16 +118,3 @@ def configure() {
log.debug("Sending configuration: $cmd") log.debug("Sending configuration: $cmd")
return cmd return cmd
} }
def installed() {
initialize()
}
def updated() {
initialize()
}
def initialize() {
sendEvent(name: "numberOfButtons", value: 4)
}

View File

@@ -37,9 +37,9 @@ 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:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#00a0dc", nextState:"turningOff" 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 "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:"#00a0dc", nextState:"turningOff" 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" state "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
} }
standardTile("reset", "device.reset", inactiveLabel: false, decoration: "flat") { standardTile("reset", "device.reset", inactiveLabel: false, decoration: "flat") {

View File

@@ -15,7 +15,6 @@ metadata {
definition (name: "Aeon Minimote", namespace: "smartthings", author: "SmartThings") { definition (name: "Aeon Minimote", namespace: "smartthings", author: "SmartThings") {
capability "Actuator" capability "Actuator"
capability "Button" capability "Button"
capability "Holdable Button"
capability "Configuration" capability "Configuration"
capability "Sensor" capability "Sensor"
@@ -108,16 +107,3 @@ def configure() {
log.debug("Sending configuration: $cmds") log.debug("Sending configuration: $cmds")
return cmds return cmds
} }
def installed() {
initialize()
}
def updated() {
initialize()
}
def initialize() {
sendEvent(name: "numberOfButtons", value: 4)
}

View File

@@ -22,6 +22,7 @@ metadata {
capability "Configuration" capability "Configuration"
capability "Sensor" capability "Sensor"
capability "Battery" capability "Battery"
capability "Health Check"
attribute "tamper", "enum", ["detected", "clear"] attribute "tamper", "enum", ["detected", "clear"]
attribute "batteryStatus", "string" attribute "batteryStatus", "string"
@@ -327,6 +328,9 @@ def zwaveEvent(physicalgraph.zwave.Command cmd) {
} }
def configure() { def configure() {
// allow device user configured or default 16 min to check in; double the periodic reporting interval
sendEvent(name: "checkInterval", value: 2* (timeOptionValueMap[reportInterval] ?: (2*8*60)), displayed: false)
// This sensor joins as a secure device if you double-click the button to include it // This sensor joins as a secure device if you double-click the button to include it
log.debug "${device.displayName} is configuring its settings" log.debug "${device.displayName} is configuring its settings"
def request = [] def request = []

View File

@@ -20,6 +20,7 @@ metadata {
capability "Configuration" capability "Configuration"
capability "Sensor" capability "Sensor"
capability "Battery" capability "Battery"
capability "Health Check"
command "configureAfterSecure" command "configureAfterSecure"
@@ -247,6 +248,8 @@ def configureAfterSecure() {
def configure() { def configure() {
// log.debug "configure()" // log.debug "configure()"
//["delay 30000"] + secure(zwave.securityV1.securityCommandsSupportedGet()) //["delay 30000"] + secure(zwave.securityV1.securityCommandsSupportedGet())
// allow device 16 min to check in; double the periodic reporting interval
sendEvent(name: "checkInterval", value: 2*8*60, displayed: false)
} }
private setConfigured() { private setConfigured() {

View File

@@ -20,6 +20,7 @@ metadata {
capability "Illuminance Measurement" capability "Illuminance Measurement"
capability "Sensor" capability "Sensor"
capability "Battery" capability "Battery"
capability "Health Check"
fingerprint deviceId: "0x2001", inClusters: "0x30,0x31,0x80,0x84,0x70,0x85,0x72,0x86" fingerprint deviceId: "0x2001", inClusters: "0x30,0x31,0x80,0x84,0x70,0x85,0x72,0x86"
} }
@@ -180,6 +181,9 @@ def zwaveEvent(physicalgraph.zwave.Command cmd) {
} }
def configure() { def configure() {
// allow device 10 min to check in; double the periodic reporting interval
sendEvent(name: "checkInterval", value: 2*5*60, displayed: false)
delayBetween([ delayBetween([
// send binary sensor report instead of basic set for motion // send binary sensor report instead of basic set for motion
zwave.configurationV1.configurationSet(parameterNumber: 5, size: 1, scaledConfigurationValue: 2).format(), zwave.configurationV1.configurationSet(parameterNumber: 5, size: 1, scaledConfigurationValue: 2).format(),

View File

@@ -45,7 +45,7 @@ metadata {
// tile definitions // tile definitions
tiles { tiles {
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) { standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
state "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#00a0dc" state "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821"
state "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff" state "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff"
} }
valueTile("energy", "device.energy", decoration: "flat") { valueTile("energy", "device.energy", decoration: "flat") {

View File

@@ -60,7 +60,7 @@ metadata {
// tile definitions // tile definitions
tiles { tiles {
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) { standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
state "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#00a0dc" state "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821"
state "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff" state "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff"
} }
valueTile("power", "device.power", decoration: "flat") { valueTile("power", "device.power", decoration: "flat") {
@@ -78,7 +78,7 @@ metadata {
(1..4).each { n -> (1..4).each { n ->
standardTile("switch$n", "switch$n", canChangeIcon: true) { standardTile("switch$n", "switch$n", canChangeIcon: true) {
state "on", label: '${name}', action: "off$n", icon: "st.switches.switch.on", backgroundColor: "#00a0dc" state "on", label: '${name}', action: "off$n", icon: "st.switches.switch.on", backgroundColor: "#79b821"
state "off", label: '${name}', action: "on$n", icon: "st.switches.switch.off", backgroundColor: "#ffffff" state "off", label: '${name}', action: "on$n", icon: "st.switches.switch.off", backgroundColor: "#ffffff"
} }
valueTile("power$n", "power$n", decoration: "flat") { valueTile("power$n", "power$n", decoration: "flat") {

View File

@@ -11,6 +11,7 @@
* 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"
@@ -39,7 +40,7 @@ metadata {
tiles { tiles {
standardTile("presence", "device.presence", width: 2, height: 2, canChangeBackground: true) { standardTile("presence", "device.presence", width: 2, height: 2, canChangeBackground: true) {
state "present", labelIcon:"st.presence.tile.present", backgroundColor:"#00a0dc" state "present", labelIcon:"st.presence.tile.present", backgroundColor:"#53a7c0"
state "not present", labelIcon:"st.presence.tile.not-present", backgroundColor:"#ffffff" state "not present", labelIcon:"st.presence.tile.not-present", backgroundColor:"#ffffff"
} }
standardTile("beep", "device.beep", decoration: "flat") { standardTile("beep", "device.beep", decoration: "flat") {
@@ -59,7 +60,7 @@ def updated() {
} }
def configure() { def configure() {
def cmds = zigbee.batteryConfig(20, 20, 0x01) def cmds = zigbee.configureReporting(0x0001, 0x0020, 0x20, 20, 20, 0x01)
log.debug "configure -- cmds: ${cmds}" log.debug "configure -- cmds: ${cmds}"
return cmds return cmds
} }
@@ -151,7 +152,7 @@ private handlePresenceEvent(present) {
private startTimer() { private startTimer() {
log.debug "Scheduling periodic timer" log.debug "Scheduling periodic timer"
runEvery1Minute("checkPresenceCallback") schedule("0 * * * * ?", checkPresenceCallback)
} }
private stopTimer() { private stopTimer() {

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

@@ -27,7 +27,7 @@ metadata {
capability "Switch" capability "Switch"
capability "Refresh" capability "Refresh"
capability "Music Player" capability "Music Player"
capability "Health Check" capability "Polling"
/** /**
* Define all commands, ie, if you have a custom action not * Define all commands, ie, if you have a custom action not
@@ -47,9 +47,6 @@ metadata {
command "everywhereJoin" command "everywhereJoin"
command "everywhereLeave" command "everywhereLeave"
command "forceOff"
command "forceOn"
} }
/** /**
@@ -67,9 +64,9 @@ 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 "on", label: '${name}', action: "switch.off", icon: "st.Electronics.electronics16", backgroundColor: "#79b821", nextState:"turningOff"
state "turningOff", label:'TURNING OFF', icon:"st.Electronics.electronics16", backgroundColor:"#ffffff" state "turningOff", label:'TURNING OFF', icon:"st.Electronics.electronics16", backgroundColor:"#ffffff"
state "off", label: '${name}', action: "forceOn", icon: "st.Electronics.electronics16", backgroundColor: "#ffffff", nextState:"turningOn" state "off", label: '${name}', action: "switch.on", icon: "st.Electronics.electronics16", backgroundColor: "#ffffff", nextState:"turningOn"
state "turningOn", label:'TURNING ON', icon:"st.Electronics.electronics16", backgroundColor:"#79b821" 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) {
@@ -143,22 +140,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") }
@@ -236,33 +219,7 @@ def parse(String event) {
* @return action(s) to take or null * @return action(s) to take or null
*/ */
def installed() { def installed() {
// Notify health check about this device with timeout interval 12 minutes onAction("refresh")
sendEvent(name: "checkInterval", value: 12 * 60, data: [protocol: "lan", hubHardwareId: device.hub.hardwareID], displayed: false)
startPoll()
}
/**
* Called by health check if no events been generated in the last 12 minutes
* If device doesn't respond it will be marked offline (not available)
*/
def ping() {
TRACE("ping")
boseSendGetNowPlaying()
}
/**
* Schedule a 2 minute poll of the device to refresh the
* tiles so the user gets the correct information.
*/
def startPoll() {
TRACE("startPoll")
unschedule()
// Schedule 2 minute polling of speaker status (song average length is 3-4 minutes)
def sec = Math.round(Math.floor(Math.random() * 60))
//def cron = "$sec 0/5 * * * ?" // every 5 min
def cron = "$sec 0/2 * * * ?" // every 2 min
log.debug "schedule('$cron', boseSendGetNowPlaying)"
schedule(cron, boseSendGetNowPlaying)
} }
/** /**
@@ -283,11 +240,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)
@@ -342,6 +299,14 @@ def onAction(String user, data=null) {
return actions return actions
} }
/**
* Called every so often (every 5 minutes actually) to refresh the
* tiles so the user gets the correct information.
*/
def poll() {
return boseRefreshNowPlaying()
}
/** /**
* Joins this speaker into the everywhere zone * Joins this speaker into the everywhere zone
*/ */
@@ -855,10 +820,6 @@ def boseRefreshNowPlaying(delay=0) {
return boseGET("/now_playing") return boseGET("/now_playing")
} }
def boseSendGetNowPlaying() {
sendHubCommand(boseGET("/now_playing"))
}
/** /**
* Requests the list of presets * Requests the list of presets
* *
@@ -1037,7 +998,3 @@ def boseGetDeviceID() {
def getDeviceIP() { def getDeviceIP() {
return parent.resolveDNI2Address(device.deviceNetworkId) return parent.resolveDNI2Address(device.deviceNetworkId)
} }
def TRACE(text) {
log.trace "${text}"
}

View File

@@ -0,0 +1,146 @@
/**
* Copyright 2015 SmartThings
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under the License.
*
* CentraLite Dimmer
*
* Author: SmartThings
* Date: 2013-12-04
*/
//DEPRECATED - Using the generic DTH for this device. Users need to be moved before deleting this DTH
metadata {
definition (name: "CentraLite Dimmer", namespace: "smartthings", author: "SmartThings") {
capability "Switch Level"
capability "Actuator"
capability "Switch"
capability "Power Meter"
capability "Configuration"
capability "Refresh"
capability "Sensor"
}
// simulator metadata
simulator {
// status messages
status "on": "on/off: 1"
status "off": "on/off: 0"
// reply messages
reply "zcl on-off on": "on/off: 1"
reply "zcl on-off off": "on/off: 0"
}
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.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"
}
tileAttribute ("device.level", key: "SLIDER_CONTROL") {
attributeState "level", action:"switch level.setLevel"
}
tileAttribute ("power", key: "SECONDARY_CONTROL") {
attributeState "power", label:'${currentValue} W'
}
}
standardTile("refresh", "device.power", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
}
main "switch"
details(["switch","refresh"])
}
}
// Parse incoming device messages to generate events
def parse(String description) {
log.debug "Parse description $description"
def name = null
def value = null
if (description?.startsWith("catchall:")) {
def msg = zigbee.parse(description)
log.trace msg
log.trace "data: $msg.data"
} else if (description?.startsWith("read attr -")) {
def descMap = parseDescriptionAsMap(description)
log.debug "Read attr: $description"
if (descMap.cluster == "0006" && descMap.attrId == "0000") {
name = "switch"
value = descMap.value.endsWith("01") ? "on" : "off"
} else {
def reportValue = description.split(",").find {it.split(":")[0].trim() == "value"}?.split(":")[1].trim()
name = "power"
// assume 16 bit signed for encoding and power divisor is 10
value = Integer.parseInt(reportValue, 16) / 10
}
} else if (description?.startsWith("on/off:")) {
log.debug "Switch command"
name = "switch"
value = description?.endsWith(" 1") ? "on" : "off"
}
def result = createEvent(name: name, value: value)
log.debug "Parse returned ${result?.descriptionText}"
return result
}
def parseDescriptionAsMap(description) {
(description - "read attr - ").split(",").inject([:]) { map, param ->
def nameAndValue = param.split(":")
map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
}
}
// Commands to device
def on() {
'zcl on-off on'
}
def off() {
'zcl on-off off'
}
def setLevel(value) {
log.trace "setLevel($value)"
sendEvent(name: "level", value: value)
def level = hexString(Math.round(value * 255/100))
def cmd = "st cmd 0x${device.deviceNetworkId} 1 8 4 {${level} 2000}"
log.debug cmd
cmd
}
def meter() {
"st rattr 0x${device.deviceNetworkId} 1 0xB04 0x50B"
}
def refresh() {
"st rattr 0x${device.deviceNetworkId} 1 0xB04 0x50B"
}
def configure() {
[
"zdo bind 0x${device.deviceNetworkId} 1 1 8 {${device.zigbeeId}} {}", "delay 200",
"zdo bind 0x${device.deviceNetworkId} 1 1 6 {${device.zigbeeId}} {}", "delay 200",
"zdo bind 0x${device.deviceNetworkId} 1 1 0xB04 {${device.zigbeeId}} {}"
]
}
private hex(value, width=2) {
def s = new BigInteger(Math.round(value).toString()).toString(16)
while (s.size() < width) {
s = "0" + s
}
s
}

View File

@@ -55,13 +55,13 @@ metadata {
state "fanOn", label:'${name}', action:"thermostat.setThermostatFanMode" state "fanOn", label:'${name}', action:"thermostat.setThermostatFanMode"
} }
controlTile("heatSliderControl", "device.heatingSetpoint", "slider", height: 1, width: 2, inactiveLabel: false) { controlTile("heatSliderControl", "device.heatingSetpoint", "slider", height: 1, width: 2, inactiveLabel: false) {
state "setHeatingSetpoint", action:"thermostat.setHeatingSetpoint", backgroundColor:"#e86d13" state "setHeatingSetpoint", action:"thermostat.setHeatingSetpoint", backgroundColor:"#d04e00"
} }
valueTile("heatingSetpoint", "device.heatingSetpoint", inactiveLabel: false, decoration: "flat") { valueTile("heatingSetpoint", "device.heatingSetpoint", inactiveLabel: false, decoration: "flat") {
state "heat", label:'${currentValue}° heat', unit:"F", backgroundColor:"#ffffff" state "heat", label:'${currentValue}° heat', unit:"F", backgroundColor:"#ffffff"
} }
controlTile("coolSliderControl", "device.coolingSetpoint", "slider", height: 1, width: 2, inactiveLabel: false) { controlTile("coolSliderControl", "device.coolingSetpoint", "slider", height: 1, width: 2, inactiveLabel: false) {
state "setCoolingSetpoint", action:"thermostat.setCoolingSetpoint", backgroundColor: "#00a0dc" state "setCoolingSetpoint", action:"thermostat.setCoolingSetpoint", backgroundColor: "#1e9cbb"
} }
valueTile("coolingSetpoint", "device.coolingSetpoint", inactiveLabel: false, decoration: "flat") { valueTile("coolingSetpoint", "device.coolingSetpoint", inactiveLabel: false, decoration: "flat") {
state "cool", label:'${currentValue}° cool', unit:"F", backgroundColor:"#ffffff" state "cool", label:'${currentValue}° cool', unit:"F", backgroundColor:"#ffffff"
@@ -81,47 +81,48 @@ metadata {
// parse events into attributes // parse events into attributes
def parse(String description) { def parse(String description) {
log.debug "Parse description $description" log.debug "Parse description $description"
List result = [] def map = [:]
def descMap = zigbee.parseDescriptionAsMap(description) if (description?.startsWith("read attr -")) {
log.debug "Desc Map: $descMap" def descMap = parseDescriptionAsMap(description)
List attrData = [[cluster: descMap.cluster ,attrId: descMap.attrId, value: descMap.value]] log.debug "Desc Map: $descMap"
descMap.additionalAttrs.each { if (descMap.cluster == "0201" && descMap.attrId == "0000") {
attrData << [cluster: descMap.cluster, attrId: it.attrId, value: it.value]
}
attrData.each {
def map = [:]
if (it.cluster == "0201" && it.attrId == "0000") {
log.debug "TEMP" log.debug "TEMP"
map.name = "temperature" map.name = "temperature"
map.value = getTemperature(it.value) map.value = getTemperature(descMap.value)
map.unit = temperatureScale } else if (descMap.cluster == "0201" && descMap.attrId == "0011") {
} else if (it.cluster == "0201" && it.attrId == "0011") {
log.debug "COOLING SETPOINT" log.debug "COOLING SETPOINT"
map.name = "coolingSetpoint" map.name = "coolingSetpoint"
map.value = getTemperature(it.value) map.value = getTemperature(descMap.value)
map.unit = temperatureScale } else if (descMap.cluster == "0201" && descMap.attrId == "0012") {
} else if (it.cluster == "0201" && it.attrId == "0012") {
log.debug "HEATING SETPOINT" log.debug "HEATING SETPOINT"
map.name = "heatingSetpoint" map.name = "heatingSetpoint"
map.value = getTemperature(it.value) map.value = getTemperature(descMap.value)
map.unit = temperatureScale } else if (descMap.cluster == "0201" && descMap.attrId == "001c") {
} else if (it.cluster == "0201" && it.attrId == "001c") {
log.debug "MODE" log.debug "MODE"
map.name = "thermostatMode" map.name = "thermostatMode"
map.value = getModeMap()[it.value] map.value = getModeMap()[descMap.value]
} else if (it.cluster == "0202" && it.attrId == "0000") { } else if (descMap.cluster == "0202" && descMap.attrId == "0000") {
log.debug "FAN MODE" log.debug "FAN MODE"
map.name = "thermostatFanMode" map.name = "thermostatFanMode"
map.value = getFanModeMap()[it.value] map.value = getFanModeMap()[descMap.value]
} }
if (map) {
result << createEvent(map)
}
log.debug "Parse returned $map"
} }
def result = null
if (map) {
result = createEvent(map)
}
log.debug "Parse returned $map"
return result return result
} }
def parseDescriptionAsMap(description) {
(description - "read attr - ").split(",").inject([:]) { map, param ->
def nameAndValue = param.split(":")
map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
}
}
def getModeMap() { [ def getModeMap() { [
"00":"off", "00":"off",
"03":"cool", "03":"cool",
@@ -168,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) + "}"
@@ -179,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

@@ -16,7 +16,7 @@ metadata {
capability "Switch" capability "Switch"
capability "Switch Level" capability "Switch Level"
capability "Button" capability "Button"
capability "Actuator" capability "Actuator"
//fingerprint deviceId: "0x1200", inClusters: "0x77 0x86 0x75 0x73 0x85 0x72 0xEF", outClusters: "0x26" //fingerprint deviceId: "0x1200", inClusters: "0x77 0x86 0x75 0x73 0x85 0x72 0xEF", outClusters: "0x26"
} }
@@ -36,7 +36,7 @@ metadata {
tiles { tiles {
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) { standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
state "off", label: '${name}', action: "switch.on", icon: "st.Home.home30", backgroundColor: "#ffffff" state "off", label: '${name}', action: "switch.on", icon: "st.Home.home30", backgroundColor: "#ffffff"
state "on", label: '${name}', action: "switch.off", icon: "st.Home.home30", backgroundColor: "#00a0dc" state "on", label: '${name}', action: "switch.off", icon: "st.Home.home30", backgroundColor: "#79b821"
} }
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") { standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") {
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh" state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
@@ -182,16 +182,3 @@ def updateState(String name, String value) {
state[name] = value state[name] = value
device.updateDataValue(name, value) device.updateDataValue(name, value)
} }
def installed() {
initialize()
}
def updated() {
initialize()
}
def initialize() {
sendEvent(name: "numberOfButtons", value: 3)
}

View File

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

View File

@@ -1,36 +0,0 @@
# Connected Cree LED Bulb
Cloud Execution
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
Connected Cree LED Bulb with cloud polling it every __5min__
SmartThings platform will ping the device after `checkInterval` seconds of inactivity in last attempt to reach the device before marking it `OFFLINE`
* __12min__ checkInterval
## 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

@@ -19,11 +19,10 @@ metadata {
capability "Actuator" capability "Actuator"
capability "Configuration" capability "Configuration"
capability "Polling"
capability "Refresh" capability "Refresh"
capability "Switch" capability "Switch"
capability "Switch Level" capability "Switch Level"
capability "Health Check"
capability "Light"
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"
} }
@@ -43,9 +42,9 @@ 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:'${name}', action:"switch.off", icon:"st.switches.light.on", backgroundColor:"#00A0DC", nextState:"turningOff" attributeState "on", label:'${name}', action:"switch.off", icon:"st.switches.light.on", backgroundColor:"#79b821", nextState:"turningOff"
attributeState "off", label:'${name}', action:"switch.on", icon:"st.switches.light.off", backgroundColor:"#ffffff", nextState:"turningOn" attributeState "off", label:'${name}', action:"switch.on", icon:"st.switches.light.off", backgroundColor:"#ffffff", nextState:"turningOn"
attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.switches.light.on", backgroundColor:"#00A0DC", nextState:"turningOff" attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.switches.light.on", backgroundColor:"#79b821", nextState:"turningOff"
attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.switches.light.off", backgroundColor:"#ffffff", nextState:"turningOn" attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.switches.light.off", backgroundColor:"#ffffff", nextState:"turningOn"
} }
tileAttribute ("device.level", key: "SLIDER_CONTROL") { tileAttribute ("device.level", key: "SLIDER_CONTROL") {
@@ -83,30 +82,18 @@ def on() {
} }
def setLevel(value) { def setLevel(value) {
zigbee.setLevel(value) + zigbee.onOffRefresh() + 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() { def poll() {
log.debug "healthPoll()" zigbee.onOffRefresh() + zigbee.levelRefresh()
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 + ping
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
zigbee.onOffRefresh() + zigbee.levelRefresh()
} }

View File

@@ -33,10 +33,10 @@ metadata {
tiles { tiles {
standardTile("toggle", "device.lock", width: 2, height: 2) { standardTile("toggle", "device.lock", width: 2, height: 2) {
state "locked", label:'locked', action:"lock.unlock", icon:"st.locks.lock.locked", backgroundColor:"#00a0dc", nextState:"unlocking" state "locked", label:'locked', action:"lock.unlock", icon:"st.locks.lock.locked", backgroundColor:"#79b821", nextState:"unlocking"
state "unlocked", label:'unlocked', action:"lock.lock", icon:"st.locks.lock.unlocked", backgroundColor:"#ffffff", nextState:"locking" state "unlocked", label:'unlocked', action:"lock.lock", icon:"st.locks.lock.unlocked", backgroundColor:"#ffffff", nextState:"locking"
state "unknown", label:"unknown", action:"lock.lock", icon:"st.locks.lock.unknown", backgroundColor:"#ffffff", nextState:"locking" state "unknown", label:"unknown", action:"lock.lock", icon:"st.locks.lock.unknown", backgroundColor:"#ffffff", nextState:"locking"
state "locking", label:'locking', icon:"st.locks.lock.locked", backgroundColor:"#00a0dc" state "locking", label:'locking', icon:"st.locks.lock.locked", backgroundColor:"#79b821"
state "unlocking", label:'unlocking', icon:"st.locks.lock.unlocked", backgroundColor:"#ffffff" state "unlocking", label:'unlocking', icon:"st.locks.lock.unlocked", backgroundColor:"#ffffff"
} }
standardTile("lock", "device.lock", inactiveLabel: false, decoration: "flat") { standardTile("lock", "device.lock", inactiveLabel: false, decoration: "flat") {

View File

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

View File

@@ -1,51 +0,0 @@
# Z-wave Dimmer Switch
Local Execution on V2 Hubs
Works with:
* [GE Z-Wave In-Wall Smart Dimmer (GE 12724)](http://products.z-wavealliance.org/products/1197)
* [GE Z-Wave In-Wall Smart Dimmer (Toggle) (GE 12729)](http://products.z-wavealliance.org/products/1201)
* [GE Z-Wave Plug-in Smart Dimmer (GE 12718)](http://products.z-wavealliance.org/products/1191)
* [GE 1,000-Watt In-Wall Smart Dimmer Switch (GE 12725)](http://products.z-wavealliance.org/products/1198)
* [GE In-Wall Smart Fan Control (GE 12730)](http://products.z-wavealliance.org/products/1202)
## Table of contents
* [Capabilities](#capabilities)
* [Health](#device-health)
* [Troubleshooting](#Troubleshooting)
## Capabilities
* **Switch Level** - it's defined to accept two parameters, the level and the rate of dimming
* **Actuator** - represents that a Device has commands
* **Indicator** - gives you the ability to set the indicator LED light on a Z-Wave switch
* **Switch** - can detect state (possible values: on/off)
* **Polling** - represents that poll() can be implemented for the device
* **Refresh** - _refresh()_ command for status updates
* **Sensor** - detects sensor events
* **Health Check** - indicates ability to get device health notifications
## Device Health
Z-Wave Smart Dimmers (In-Wall, In-Wall(Toggle), Plug-In), 1000-watt In-Wall Smart Dimmer Switch and In-Wall Smart Fan Control are polled by the hub.
As of hubCore version 0.14.38 the hub sends up reports every 15 minutes regardless of whether the state changed.
Check-in interval = 32 mins.
Not to mention after going OFFLINE when the device is plugged back in, it might take a considerable amount of time for
the device to appear as ONLINE again. This is because if this listening device does not respond to two poll requests in a row,
it is not polled for 5 minutes by the hub. This can delay up the process of being marked ONLINE by quite some time.
* __32min__ checkInterval
## 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:
* [General Z-Wave Dimmer/Switch Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/200955890-Troubleshooting-GE-in-wall-switch-or-dimmer-won-t-respond-to-commands-or-automations-Z-Wave-)
* [GE Z-Wave In-Wall Smart Dimmer (GE 12724) Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/200902600-GE-In-Wall-Paddle-Dimmer-Switch-GE-12724-Z-Wave-)
* [GE Z-Wave In-Wall Smart Dimmer (Toggle) (GE 12729) Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/207568463-GE-In-Wall-Smart-Toggle-Dimmer-GE-12729-Z-Wave-)
* [GE Z-Wave Plug-in Smart Dimmer (GE 12718) Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/202088474-GE-Plug-In-Smart-Dimmer-GE-12718-Z-Wave-)
* [GE 1,000-Watt In-Wall Smart Dimmer Switch (GE 12725) Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/200879274)
* [GE In-Wall Smart Fan Control (GE 12730) Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/200879274)

View File

@@ -20,13 +20,10 @@ metadata {
capability "Polling" capability "Polling"
capability "Refresh" capability "Refresh"
capability "Sensor" capability "Sensor"
capability "Health Check"
capability "Light"
fingerprint mfr:"0063", prod:"4457", deviceJoinName: "GE In-Wall Smart Dimmer " fingerprint mfr:"0063", prod:"4457", deviceJoinName: "Z-Wave Wall Dimmer"
fingerprint mfr:"0063", prod:"4944", deviceJoinName: "GE In-Wall Smart Dimmer " fingerprint mfr:"0063", prod:"4944", deviceJoinName: "Z-Wave Wall Dimmer"
fingerprint mfr:"0063", prod:"5044", deviceJoinName: "GE Plug-In Smart Dimmer " fingerprint mfr:"0063", prod:"5044", deviceJoinName: "Z-Wave Plug-In Dimmer"
fingerprint mfr:"0063", prod:"4944", model:"3034", deviceJoinName: "GE In-Wall Smart Fan Control"
} }
simulator { simulator {
@@ -54,9 +51,9 @@ 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:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#00a0dc", nextState:"turningOff" 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 "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:"#00a0dc", nextState:"turningOff" 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" attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn"
} }
tileAttribute ("device.level", key: "SLIDER_CONTROL") { tileAttribute ("device.level", key: "SLIDER_CONTROL") {
@@ -85,8 +82,6 @@ metadata {
} }
def updated(){ def updated(){
// Device-Watch simply pings if no device events received for 32min(checkInterval)
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
switch (ledIndicator) { switch (ledIndicator) {
case "on": case "on":
indicatorWhenOn() indicatorWhenOn()
@@ -220,13 +215,6 @@ def poll() {
zwave.switchMultilevelV1.switchMultilevelGet().format() zwave.switchMultilevelV1.switchMultilevelGet().format()
} }
/**
* PING is used by Device-Watch in attempt to reach the Device
* */
def ping() {
refresh()
}
def refresh() { def refresh() {
log.debug "refresh() is called" log.debug "refresh() is called"
def commands = [] def commands = []
@@ -238,17 +226,17 @@ def refresh() {
} }
void indicatorWhenOn() { void indicatorWhenOn() {
sendEvent(name: "indicatorStatus", value: "when on", displayed: false) sendEvent(name: "indicatorStatus", value: "when on", display: false)
sendHubCommand(new physicalgraph.device.HubAction(zwave.configurationV1.configurationSet(configurationValue: [1], parameterNumber: 3, size: 1).format())) sendHubCommand(new physicalgraph.device.HubAction(zwave.configurationV1.configurationSet(configurationValue: [1], parameterNumber: 3, size: 1).format()))
} }
void indicatorWhenOff() { void indicatorWhenOff() {
sendEvent(name: "indicatorStatus", value: "when off", displayed: false) sendEvent(name: "indicatorStatus", value: "when off", display: false)
sendHubCommand(new physicalgraph.device.HubAction(zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 3, size: 1).format())) sendHubCommand(new physicalgraph.device.HubAction(zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 3, size: 1).format()))
} }
void indicatorNever() { void indicatorNever() {
sendEvent(name: "indicatorStatus", value: "never", displayed: false) sendEvent(name: "indicatorStatus", value: "never", display: false)
sendHubCommand(new physicalgraph.device.HubAction(zwave.configurationV1.configurationSet(configurationValue: [2], parameterNumber: 3, size: 1).format())) sendHubCommand(new physicalgraph.device.HubAction(zwave.configurationV1.configurationSet(configurationValue: [2], parameterNumber: 3, size: 1).format()))
} }

View File

@@ -47,8 +47,8 @@ metadata {
} }
standardTile("motion", "device.motion") { standardTile("motion", "device.motion") {
state("inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#cccccc") state("inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#ffffff")
state("active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#00A0DC") state("active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#53a7c0")
} }
standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat") { standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat") {

View File

@@ -23,7 +23,6 @@ metadata {
capability "Sensor" capability "Sensor"
capability "Refresh" capability "Refresh"
capability "Relative Humidity Measurement" capability "Relative Humidity Measurement"
capability "Health Check"
command "generateEvent" command "generateEvent"
command "raiseSetpoint" command "raiseSetpoint"
@@ -32,14 +31,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"
attribute "deviceAlive", "enum", ["true", "false"]
} }
tiles { tiles {
@@ -122,21 +120,6 @@ metadata {
} }
void installed() {
// The device refreshes every 5 minutes by default so if we miss 2 refreshes we can consider it offline
// Using 12 minutes because in testing, device health team found that there could be "jitter"
sendEvent(name: "checkInterval", value: 60 * 12, data: [protocol: "cloud"], displayed: false)
}
// Device Watch will ping the device to proactively determine if the device has gone offline
// If the device was online the last time we refreshed, trigger another refresh as part of the ping.
def ping() {
def isAlive = device.currentValue("deviceAlive") == "true" ? true : false
if (isAlive) {
refresh()
}
}
// parse events into attributes // parse events into attributes
def parse(String description) { def parse(String description) {
log.debug "Parsing '${description}'" log.debug "Parsing '${description}'"
@@ -165,13 +148,15 @@ def generateEvent(Map results) {
handlerName: name] handlerName: name]
if (name=="temperature" || name=="heatingSetpoint" || name=="coolingSetpoint" ) { if (name=="temperature" || name=="heatingSetpoint" || name=="coolingSetpoint" ) {
def sendValue = location.temperatureScale == "C"? roundC(convertFtoC(value.toDouble())) : value.toInteger() def sendValue = convertTemperatureIfNeeded(value.toDouble(), "F", 1) //API return temperature value in F
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 = location.temperatureScale == "C"? roundC(convertFtoC(value.toDouble())) : value.toInteger() def sendValue = convertTemperatureIfNeeded(value.toDouble(), "F", 1) //API return temperature value in F
event << [value: sendValue, unit: temperatureScale, displayed: false] sendValue = location.temperatureScale == "C"? roundC(sendValue) : sendValue
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]
@@ -181,11 +166,7 @@ def generateEvent(Map results) {
} else if (name=="humidity") { } else if (name=="humidity") {
isChange = isStateChange(device, name, value.toString()) isChange = isStateChange(device, name, value.toString())
event << [value: value.toString(), isStateChange: isChange, displayed: false, unit: "%"] event << [value: value.toString(), isStateChange: isChange, displayed: false, unit: "%"]
} else if (name == "deviceAlive") { } else {
isChange = isStateChange(device, name, value.toString())
event['isStateChange'] = isChange
event['displayed'] = false
} else {
isChange = isStateChange(device, name, value.toString()) isChange = isStateChange(device, name, value.toString())
isDisplayed = isChange isDisplayed = isChange
event << [value: value.toString(), isStateChange: isChange, displayed: isDisplayed] event << [value: value.toString(), isStateChange: isChange, displayed: isDisplayed]
@@ -253,9 +234,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()
@@ -272,6 +253,7 @@ void setCoolingSetpoint(setpoint) {
def maxCoolingSetpoint = device.currentValue("maxCoolingSetpoint") def maxCoolingSetpoint = device.currentValue("maxCoolingSetpoint")
def minCoolingSetpoint = device.currentValue("minCoolingSetpoint") def minCoolingSetpoint = device.currentValue("minCoolingSetpoint")
if (coolingSetpoint > maxCoolingSetpoint) { if (coolingSetpoint > maxCoolingSetpoint) {
coolingSetpoint = maxCoolingSetpoint coolingSetpoint = maxCoolingSetpoint
} else if (coolingSetpoint < minCoolingSetpoint) { } else if (coolingSetpoint < minCoolingSetpoint) {
@@ -289,9 +271,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()
@@ -301,17 +283,18 @@ void setCoolingSetpoint(setpoint) {
} }
void resumeProgram() { 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)"
} }
} }
@@ -371,6 +354,7 @@ def switchFanMode() {
} }
def switchToFanMode(nextMode) { def switchToFanMode(nextMode) {
log.debug "switching to fan mode: $nextMode" log.debug "switching to fan mode: $nextMode"
def returnCommand def returnCommand
@@ -422,7 +406,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."
@@ -436,7 +420,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."
@@ -454,7 +438,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."
@@ -468,7 +452,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."
@@ -482,7 +466,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."
@@ -505,7 +489,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."
@@ -526,7 +510,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."
@@ -536,56 +520,63 @@ def fanAuto() {
} }
def generateSetpointEvent() { def generateSetpointEvent() {
log.debug "Generate SetPoint Event" log.debug "Generate SetPoint Event"
def mode = device.currentValue("thermostatMode") def mode = device.currentValue("thermostatMode")
log.debug "Current Mode = ${mode}"
def heatingSetpoint = device.currentValue("heatingSetpoint") def heatingSetpoint = device.currentValue("heatingSetpoint")
log.debug "Heating Setpoint = ${heatingSetpoint}"
def coolingSetpoint = device.currentValue("coolingSetpoint") def coolingSetpoint = device.currentValue("coolingSetpoint")
log.debug "Cooling Setpoint = ${coolingSetpoint}"
def maxHeatingSetpoint = device.currentValue("maxHeatingSetpoint") def maxHeatingSetpoint = device.currentValue("maxHeatingSetpoint")
def maxCoolingSetpoint = device.currentValue("maxCoolingSetpoint") def maxCoolingSetpoint = device.currentValue("maxCoolingSetpoint")
def minHeatingSetpoint = device.currentValue("minHeatingSetpoint") def minHeatingSetpoint = device.currentValue("minHeatingSetpoint")
def minCoolingSetpoint = device.currentValue("minCoolingSetpoint") def minCoolingSetpoint = device.currentValue("minCoolingSetpoint")
if(location.temperatureScale == "C") { if(location.temperatureScale == "C")
maxHeatingSetpoint = maxHeatingSetpoint > 40 ? roundC(convertFtoC(maxHeatingSetpoint)) : roundC(maxHeatingSetpoint) {
maxCoolingSetpoint = maxCoolingSetpoint > 40 ? roundC(convertFtoC(maxCoolingSetpoint)) : roundC(maxCoolingSetpoint) maxHeatingSetpoint = roundC(maxHeatingSetpoint)
minHeatingSetpoint = minHeatingSetpoint > 40 ? roundC(convertFtoC(minHeatingSetpoint)) : roundC(minHeatingSetpoint) maxCoolingSetpoint = roundC(maxCoolingSetpoint)
minCoolingSetpoint = minCoolingSetpoint > 40 ? roundC(convertFtoC(minCoolingSetpoint)) : roundC(minCoolingSetpoint) minHeatingSetpoint = roundC(minHeatingSetpoint)
heatingSetpoint = heatingSetpoint > 40 ? roundC(convertFtoC(heatingSetpoint)) : roundC(heatingSetpoint) minCoolingSetpoint = roundC(minCoolingSetpoint)
coolingSetpoint = coolingSetpoint > 40 ? roundC(convertFtoC(coolingSetpoint)) : roundC(coolingSetpoint) heatingSetpoint = roundC(heatingSetpoint)
} else { coolingSetpoint = roundC(coolingSetpoint)
maxHeatingSetpoint = maxHeatingSetpoint < 40 ? roundC(convertCtoF(maxHeatingSetpoint)) : maxHeatingSetpoint
maxCoolingSetpoint = maxCoolingSetpoint < 40 ? roundC(convertCtoF(maxCoolingSetpoint)) : maxCoolingSetpoint
minHeatingSetpoint = minHeatingSetpoint < 40 ? roundC(convertCtoF(minHeatingSetpoint)) : minHeatingSetpoint
minCoolingSetpoint = minCoolingSetpoint < 40 ? roundC(convertCtoF(minCoolingSetpoint)) : minCoolingSetpoint
heatingSetpoint = heatingSetpoint < 40 ? roundC(convertCtoF(heatingSetpoint)) : heatingSetpoint
coolingSetpoint = coolingSetpoint < 40 ? roundC(convertCtoF(coolingSetpoint)) : coolingSetpoint
} }
log.debug "Current Mode = ${mode}"
log.debug "Heating Setpoint = ${heatingSetpoint}"
log.debug "Cooling Setpoint = ${coolingSetpoint}"
sendEvent("name":"maxHeatingSetpoint", "value":maxHeatingSetpoint, "unit":location.temperatureScale) sendEvent("name":"maxHeatingSetpoint", "value":maxHeatingSetpoint, "unit":location.temperatureScale)
sendEvent("name":"maxCoolingSetpoint", "value":maxCoolingSetpoint, "unit":location.temperatureScale) sendEvent("name":"maxCoolingSetpoint", "value":maxCoolingSetpoint, "unit":location.temperatureScale)
sendEvent("name":"minHeatingSetpoint", "value":minHeatingSetpoint, "unit":location.temperatureScale) sendEvent("name":"minHeatingSetpoint", "value":minHeatingSetpoint, "unit":location.temperatureScale)
sendEvent("name":"minCoolingSetpoint", "value":minCoolingSetpoint, "unit":location.temperatureScale) sendEvent("name":"minCoolingSetpoint", "value":minCoolingSetpoint, "unit":location.temperatureScale)
sendEvent("name":"heatingSetpoint", "value":heatingSetpoint, "unit":location.temperatureScale)
sendEvent("name":"coolingSetpoint", "value":coolingSetpoint, "unit":location.temperatureScale)
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") {
sendEvent("name":"thermostatSetpoint", "value":"Auto") sendEvent("name":"thermostatSetpoint", "value":"Auto")
} else if (mode == "off") { } else if (mode == "off") {
sendEvent("name":"thermostatSetpoint", "value":"Off") sendEvent("name":"thermostatSetpoint", "value":"Off")
} else if (mode == "auxHeatOnly") { } else if (mode == "auxHeatOnly") {
sendEvent("name":"thermostatSetpoint", "value":heatingSetpoint, "unit":location.temperatureScale)
sendEvent("name":"thermostatSetpoint", "value":heatingSetpoint)
} }
} }
void raiseSetpoint() { void raiseSetpoint() {
@@ -594,31 +585,21 @@ void raiseSetpoint() {
def maxHeatingSetpoint = device.currentValue("maxHeatingSetpoint") def maxHeatingSetpoint = device.currentValue("maxHeatingSetpoint")
def maxCoolingSetpoint = device.currentValue("maxCoolingSetpoint") def maxCoolingSetpoint = device.currentValue("maxCoolingSetpoint")
if (mode == "off" || mode == "auto") { if (mode == "off" || mode == "auto") {
log.warn "this mode: $mode does not allow raiseSetpoint" log.warn "this mode: $mode does not allow raiseSetpoint"
} else { } else {
def heatingSetpoint = device.currentValue("heatingSetpoint") def heatingSetpoint = device.currentValue("heatingSetpoint")
def coolingSetpoint = device.currentValue("coolingSetpoint") def coolingSetpoint = device.currentValue("coolingSetpoint")
def thermostatSetpoint = device.currentValue("thermostatSetpoint") def thermostatSetpoint = device.currentValue("thermostatSetpoint")
if (location.temperatureScale == "C") {
maxHeatingSetpoint = maxHeatingSetpoint > 40 ? convertFtoC(maxHeatingSetpoint) : maxHeatingSetpoint
maxCoolingSetpoint = maxCoolingSetpoint > 40 ? convertFtoC(maxCoolingSetpoint) : maxCoolingSetpoint
heatingSetpoint = heatingSetpoint > 40 ? convertFtoC(heatingSetpoint) : heatingSetpoint
coolingSetpoint = coolingSetpoint > 40 ? convertFtoC(coolingSetpoint) : coolingSetpoint
thermostatSetpoint = thermostatSetpoint > 40 ? convertFtoC(thermostatSetpoint) : thermostatSetpoint
} else {
maxHeatingSetpoint = maxHeatingSetpoint < 40 ? convertCtoF(maxHeatingSetpoint) : maxHeatingSetpoint
maxCoolingSetpoint = maxCoolingSetpoint < 40 ? convertCtoF(maxCoolingSetpoint) : maxCoolingSetpoint
heatingSetpoint = heatingSetpoint < 40 ? convertCtoF(heatingSetpoint) : heatingSetpoint
coolingSetpoint = coolingSetpoint < 40 ? convertCtoF(coolingSetpoint) : coolingSetpoint
thermostatSetpoint = thermostatSetpoint < 40 ? convertCtoF(thermostatSetpoint) : thermostatSetpoint
}
log.debug "raiseSetpoint() mode = ${mode}, heatingSetpoint: ${heatingSetpoint}, coolingSetpoint:${coolingSetpoint}, thermostatSetpoint:${thermostatSetpoint}" log.debug "raiseSetpoint() mode = ${mode}, heatingSetpoint: ${heatingSetpoint}, coolingSetpoint:${coolingSetpoint}, thermostatSetpoint:${thermostatSetpoint}"
targetvalue = thermostatSetpoint ? thermostatSetpoint : 0 if (device.latestState('thermostatSetpoint')) {
targetvalue = device.latestState('thermostatSetpoint').value
targetvalue = location.temperatureScale == "F"? targetvalue.toInteger() : targetvalue.toDouble()
} else {
targetvalue = 0
}
targetvalue = location.temperatureScale == "F"? targetvalue + 1 : targetvalue + 0.5 targetvalue = location.temperatureScale == "F"? targetvalue + 1 : targetvalue + 0.5
if ((mode == "heat" || mode == "auxHeatOnly") && targetvalue > maxHeatingSetpoint) { if ((mode == "heat" || mode == "auxHeatOnly") && targetvalue > maxHeatingSetpoint) {
@@ -627,7 +608,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
@@ -641,29 +622,20 @@ void lowerSetpoint() {
def minHeatingSetpoint = device.currentValue("minHeatingSetpoint") def minHeatingSetpoint = device.currentValue("minHeatingSetpoint")
def minCoolingSetpoint = device.currentValue("minCoolingSetpoint") def minCoolingSetpoint = device.currentValue("minCoolingSetpoint")
if (mode == "off" || mode == "auto") { if (mode == "off" || mode == "auto") {
log.warn "this mode: $mode does not allow lowerSetpoint" log.warn "this mode: $mode does not allow lowerSetpoint"
} else { } else {
def heatingSetpoint = device.currentValue("heatingSetpoint") def heatingSetpoint = device.currentValue("heatingSetpoint")
def coolingSetpoint = device.currentValue("coolingSetpoint") def coolingSetpoint = device.currentValue("coolingSetpoint")
def thermostatSetpoint = device.currentValue("thermostatSetpoint") def thermostatSetpoint = device.currentValue("thermostatSetpoint")
if (location.temperatureScale == "C") {
minHeatingSetpoint = minHeatingSetpoint > 40 ? convertFtoC(minHeatingSetpoint) : minHeatingSetpoint
minCoolingSetpoint = minCoolingSetpoint > 40 ? convertFtoC(minCoolingSetpoint) : minCoolingSetpoint
heatingSetpoint = heatingSetpoint > 40 ? convertFtoC(heatingSetpoint) : heatingSetpoint
coolingSetpoint = coolingSetpoint > 40 ? convertFtoC(coolingSetpoint) : coolingSetpoint
thermostatSetpoint = thermostatSetpoint > 40 ? convertFtoC(thermostatSetpoint) : thermostatSetpoint
} else {
minHeatingSetpoint = minHeatingSetpoint < 40 ? convertCtoF(minHeatingSetpoint) : minHeatingSetpoint
minCoolingSetpoint = minCoolingSetpoint < 40 ? convertCtoF(minCoolingSetpoint) : minCoolingSetpoint
heatingSetpoint = heatingSetpoint < 40 ? convertCtoF(heatingSetpoint) : heatingSetpoint
coolingSetpoint = coolingSetpoint < 40 ? convertCtoF(coolingSetpoint) : coolingSetpoint
thermostatSetpoint = thermostatSetpoint < 40 ? convertCtoF(thermostatSetpoint) : thermostatSetpoint
}
log.debug "lowerSetpoint() mode = ${mode}, heatingSetpoint: ${heatingSetpoint}, coolingSetpoint:${coolingSetpoint}, thermostatSetpoint:${thermostatSetpoint}" log.debug "lowerSetpoint() mode = ${mode}, heatingSetpoint: ${heatingSetpoint}, coolingSetpoint:${coolingSetpoint}, thermostatSetpoint:${thermostatSetpoint}"
if (device.latestState('thermostatSetpoint')) {
targetvalue = thermostatSetpoint ? thermostatSetpoint : 0 targetvalue = device.latestState('thermostatSetpoint').value
targetvalue = location.temperatureScale == "F"? targetvalue.toInteger() : targetvalue.toDouble()
} else {
targetvalue = 0
}
targetvalue = location.temperatureScale == "F"? targetvalue - 1 : targetvalue - 0.5 targetvalue = location.temperatureScale == "F"? targetvalue - 1 : targetvalue - 0.5
if ((mode == "heat" || mode == "auxHeatOnly") && targetvalue < minHeatingSetpoint) { if ((mode == "heat" || mode == "auxHeatOnly") && targetvalue < minHeatingSetpoint) {
@@ -672,7 +644,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
@@ -681,83 +653,66 @@ void lowerSetpoint() {
//called by raiseSetpoint() and lowerSetpoint() //called by raiseSetpoint() and 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){
def temperatureScaleHasChanged = false targetHeatingSetpoint = temp.value
targetCoolingSetpoint = temp.value
if (location.temperatureScale == "C") {
if ( heatingSetpoint > 40.0 || coolingSetpoint > 40.0 ) {
temperatureScaleHasChanged = true
}
} else { } else {
if ( heatingSetpoint < 40.0 || coolingSetpoint < 40.0 ) { targetHeatingSetpoint = temp.value
temperatureScaleHasChanged = true targetCoolingSetpoint = coolingSetpoint
}
} }
} else if (mode == "cool") {
//step1: check thermostatMode, enforce limits before sending request to cloud //enforce limits before sending request to cloud
if (mode == "heat" || mode == "auxHeatOnly"){ if (temp.value < heatingSetpoint){
if (temp.value > coolingSetpoint){ targetHeatingSetpoint = temp.value
targetHeatingSetpoint = temp.value targetCoolingSetpoint = 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 = heatingSetpoint
if (mode == "heat" || mode == "auxHeatOnly"){ targetCoolingSetpoint = temp.value
sendEvent("name": "thermostatSetpoint", "value": heatingSetpoint.toString(), displayed: false)
} else if (mode == "cool") {
sendEvent("name": "thermostatSetpoint", "value": coolingSetpoint.toString(), displayed: false)
}
} }
if ( temperatureScaleHasChanged )
generateSetpointEvent()
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() {
def mode = device.currentValue("thermostatMode") def mode = device.currentValue("thermostatMode")
def heatingSetpoint = device.currentValue("heatingSetpoint") def heatingSetpoint = device.currentValue("heatingSetpoint")
def coolingSetpoint = device.currentValue("coolingSetpoint") def coolingSetpoint = device.currentValue("coolingSetpoint")
def temperature = device.currentValue("temperature") def temperature = device.currentValue("temperature")
def statusText def statusText
log.debug "Generate Status Event for Mode = ${mode}" log.debug "Generate Status Event for Mode = ${mode}"
@@ -767,25 +722,36 @@ def generateStatusEvent() {
log.debug "HVAC Mode = ${mode}" log.debug "HVAC Mode = ${mode}"
if (mode == "heat") { if (mode == "heat") {
if (temperature >= heatingSetpoint) if (temperature >= heatingSetpoint)
statusText = "Right Now: Idle" statusText = "Right Now: Idle"
else else
statusText = "Heating to ${heatingSetpoint} ${location.temperatureScale}" statusText = "Heating to ${heatingSetpoint} ${location.temperatureScale}"
} else if (mode == "cool") { } else if (mode == "cool") {
if (temperature <= coolingSetpoint) if (temperature <= coolingSetpoint)
statusText = "Right Now: Idle" statusText = "Right Now: Idle"
else else
statusText = "Cooling to ${coolingSetpoint} ${location.temperatureScale}" statusText = "Cooling to ${coolingSetpoint} ${location.temperatureScale}"
} else if (mode == "auto") {
statusText = "Right Now: Auto"
} else if (mode == "off") {
statusText = "Right Now: Off"
} else if (mode == "auxHeatOnly") {
statusText = "Emergency Heat"
} else {
statusText = "?"
}
} else if (mode == "auto") {
statusText = "Right Now: Auto"
} else if (mode == "off") {
statusText = "Right Now: Off"
} else if (mode == "auxHeatOnly") {
statusText = "Emergency Heat"
} else {
statusText = "?"
}
log.debug "Generate Status Event = ${statusText}" log.debug "Generate Status Event = ${statusText}"
sendEvent("name":"thermostatStatus", "value":statusText, "description":statusText, displayed: true) sendEvent("name":"thermostatStatus", "value":statusText, "description":statusText, displayed: true)
} }
@@ -799,7 +765,7 @@ def roundC (tempC) {
} }
def convertFtoC (tempF) { def convertFtoC (tempF) {
return ((Math.round(((tempF - 32)*(5/9)) * 2))/2).toDouble() return String.format("%.1f", (Math.round(((tempF - 32)*(5/9)) * 2))/2)
} }
def convertCtoF (tempC) { def convertCtoF (tempC) {

View File

@@ -33,7 +33,7 @@ metadata {
multiAttributeTile(name:"water", type: "generic", width: 6, height: 4){ multiAttributeTile(name:"water", type: "generic", width: 6, height: 4){
tileAttribute ("device.water", key: "PRIMARY_CONTROL") { tileAttribute ("device.water", key: "PRIMARY_CONTROL") {
attributeState "dry", icon:"st.alarm.water.dry", backgroundColor:"#ffffff" attributeState "dry", icon:"st.alarm.water.dry", backgroundColor:"#ffffff"
attributeState "wet", icon:"st.alarm.water.wet", backgroundColor:"#00a0dc" attributeState "wet", icon:"st.alarm.water.wet", backgroundColor:"#53a7c0"
} }
} }
valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false, width: 2, height: 2) { valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false, width: 2, height: 2) {

View File

@@ -39,7 +39,6 @@ metadata {
capability "Temperature Measurement" capability "Temperature Measurement"
capability "Configuration" capability "Configuration"
capability "Battery" capability "Battery"
capability "Health Check"
command "resetParams2StDefaults" command "resetParams2StDefaults"
command "listCurrentParams" command "listCurrentParams"
@@ -305,9 +304,6 @@ def lateConfigure(setConf = False) {
*/ */
def configure() { def configure() {
log.debug "Configuring Device..." log.debug "Configuring Device..."
// Device-Watch simply pings if no device events received for 8 hrs & 2 minutes
sendEvent(name: "checkInterval", value: 8 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
def cmds = [] def cmds = []
// send associate to group 2 to get alarm data // send associate to group 2 to get alarm data

View File

@@ -46,7 +46,6 @@
capability "Illuminance Measurement" capability "Illuminance Measurement"
capability "Sensor" capability "Sensor"
capability "Battery" capability "Battery"
capability "Health Check"
command "resetParams2StDefaults" command "resetParams2StDefaults"
command "listCurrentParams" command "listCurrentParams"
@@ -126,9 +125,6 @@
*/ */
def configure() { def configure() {
log.debug "Configuring Device For SmartThings Use" log.debug "Configuring Device For SmartThings Use"
// Device-Watch simply pings if no device events received for 8 hrs & 2 minutes
sendEvent(name: "checkInterval", value: 8 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
def cmds = [] def cmds = []
// send associate to group 3 to get sensor data reported only to hub // send associate to group 3 to get sensor data reported only to hub

View File

@@ -89,9 +89,9 @@
state "whiteLevel", action:"setWhiteLevel", label:'White Level' state "whiteLevel", action:"setWhiteLevel", label:'White Level'
} }
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:"switch.off", icon:"st.illuminance.illuminance.bright", backgroundColor:"#00A0DC", nextState:"turningOff" state "on", label:'${name}', action:"switch.off", icon:"st.illuminance.illuminance.bright", backgroundColor:"#79b821", nextState:"turningOff"
state "off", label:'${name}', action:"switch.on", icon:"st.illuminance.illuminance.dark", backgroundColor:"#ffffff", nextState:"turningOn" state "off", label:'${name}', action:"switch.on", icon:"st.illuminance.illuminance.dark", backgroundColor:"#ffffff", nextState:"turningOn"
state "turningOn", label:'${name}', icon:"st.illuminance.illuminance.bright", backgroundColor:"#00A0DC" state "turningOn", label:'${name}', icon:"st.illuminance.illuminance.bright", backgroundColor:"#79b821"
state "turningOff", label:'${name}', icon:"st.illuminance.illuminance.dark", backgroundColor:"#ffffff" state "turningOff", label:'${name}', icon:"st.illuminance.illuminance.dark", backgroundColor:"#ffffff"
} }
valueTile("power", "device.power", decoration: "flat") { valueTile("power", "device.power", decoration: "flat") {
@@ -546,7 +546,7 @@ def zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport
def value = "when off" def value = "when off"
if (cmd.configurationValue[0] == 1) {value = "when on"} if (cmd.configurationValue[0] == 1) {value = "when on"}
if (cmd.configurationValue[0] == 2) {value = "never"} if (cmd.configurationValue[0] == 2) {value = "never"}
[name: "indicatorStatus", value: value, displayed: false] [name: "indicatorStatus", value: value, display: false]
} }
*/ */
def createEvent(physicalgraph.zwave.Command cmd, Map map) { def createEvent(physicalgraph.zwave.Command cmd, Map map) {
@@ -829,7 +829,7 @@ def toggleTiles(color) {
state.colorTiles.each({ state.colorTiles.each({
if ( it == color ) { if ( it == color ) {
log.debug "Turning ${it} on" log.debug "Turning ${it} on"
cmds << sendEvent(name: it, value: "on${it}", displayed: True, descriptionText: "${device.displayName} ${color} is 'ON'", isStateChange: true) cmds << sendEvent(name: it, value: "on${it}", display: True, descriptionText: "${device.displayName} ${color} is 'ON'", isStateChange: true)
} else { } else {
//log.debug "Turning ${it} off" //log.debug "Turning ${it} off"
cmds << sendEvent(name: it, value: "off${it}", displayed: false) cmds << sendEvent(name: it, value: "off${it}", displayed: false)

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

@@ -34,10 +34,10 @@ metadata {
// tile definitions // tile definitions
tiles { tiles {
standardTile("contact", "device.contact", width: 2, height: 2, canChangeIcon: true) { standardTile("contact", "device.contact", width: 2, height: 2, canChangeIcon: true) {
state "open", label: '${name}', action: "valve.close", icon: "st.valves.water.open", backgroundColor: "#00A0DC", nextState:"closing" state "open", label: '${name}', action: "valve.close", icon: "st.valves.water.open", backgroundColor: "#53a7c0", nextState:"closing"
state "closed", label: '${name}', action: "valve.open", icon: "st.valves.water.closed", backgroundColor: "#ffffff", nextState:"opening" state "closed", label: '${name}', action: "valve.open", icon: "st.valves.water.closed", backgroundColor: "#e86d13", nextState:"opening"
state "opening", label: '${name}', action: "valve.close", icon: "st.valves.water.open", backgroundColor: "#00A0DC" state "opening", label: '${name}', action: "valve.close", icon: "st.valves.water.open", backgroundColor: "#ffe71e"
state "closing", label: '${name}', action: "valve.open", icon: "st.valves.water.closed", backgroundColor: "#ffffff" state "closing", label: '${name}', action: "valve.open", icon: "st.valves.water.closed", backgroundColor: "#ffe71e"
} }
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") { standardTile("refresh", "device.switch", 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

@@ -41,7 +41,7 @@
standardTile("take", "device.image", width: 1, height: 1, canChangeIcon: false, inactiveLabel: true, canChangeBackground: false) { standardTile("take", "device.image", width: 1, height: 1, canChangeIcon: false, inactiveLabel: true, canChangeBackground: false) {
state "take", label: "Take", action: "Image Capture.take", icon: "st.camera.dropcam", backgroundColor: "#FFFFFF", nextState:"taking" state "take", label: "Take", action: "Image Capture.take", icon: "st.camera.dropcam", backgroundColor: "#FFFFFF", nextState:"taking"
state "taking", label:'Taking', action: "", icon: "st.camera.dropcam", backgroundColor: "#00A0DC" state "taking", label:'Taking', action: "", icon: "st.camera.dropcam", backgroundColor: "#53a7c0"
state "image", label: "Take", action: "Image Capture.take", icon: "st.camera.dropcam", backgroundColor: "#FFFFFF", nextState:"taking" state "image", label: "Take", action: "Image Capture.take", icon: "st.camera.dropcam", backgroundColor: "#FFFFFF", nextState:"taking"
} }

View File

@@ -61,9 +61,9 @@ 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:'${name}', action:"switch.off", icon:"st.switches.light.on", backgroundColor:"#00a0dc", nextState:"turningOff" attributeState "on", label:'${name}', action:"switch.off", icon:"st.switches.light.on", backgroundColor:"#79b821", nextState:"turningOff"
attributeState "off", label:'${name}', action:"switch.on", icon:"st.switches.light.off", backgroundColor:"#ffffff", nextState:"turningOn" attributeState "off", label:'${name}', action:"switch.on", icon:"st.switches.light.off", backgroundColor:"#ffffff", nextState:"turningOn"
attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.switches.light.on", backgroundColor:"#00a0dc", nextState:"turningOff" attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.switches.light.on", backgroundColor:"#79b821", nextState:"turningOff"
attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.switches.light.off", backgroundColor:"#ffffff", nextState:"turningOn" attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.switches.light.off", backgroundColor:"#ffffff", nextState:"turningOn"
} }
tileAttribute ("device.level", key: "SLIDER_CONTROL") { tileAttribute ("device.level", key: "SLIDER_CONTROL") {
@@ -87,7 +87,7 @@ metadata {
def parse(String description) { def parse(String description) {
def resultMap = zigbee.getEvent(description) def resultMap = zigbee.getEvent(description)
if (resultMap) { if (resultMap) {
if (resultMap.name != "level" || resultMap.value != 0) { // Ignore level reports of 0 sent when bulb turns off if ((resultMap.name == "level" && state.trigger == "setLevel") || resultMap.name != "level") { //doing this to account for weird level reporting bug with GE Link Bulbs
sendEvent(resultMap) sendEvent(resultMap)
} }
} }
@@ -99,7 +99,7 @@ def parse(String description) {
def poll() { def poll() {
def refreshCmds = [ def refreshCmds = [
"st wattr 0x${device.deviceNetworkId} 1 8 0x10 0x21 {${state?.dOnOff ?: '0000'}}", "delay 2000" "st wattr 0x${device.deviceNetworkId} 1 8 0x10 0x21 {${state?.dOnOff ?: '0000'}}", "delay 500"
] ]
return refreshCmds + zigbee.onOffRefresh() + zigbee.levelRefresh() return refreshCmds + zigbee.onOffRefresh() + zigbee.levelRefresh()
@@ -188,22 +188,25 @@ def updated() {
} }
def on() { def on() {
state.trigger = "on/off"
zigbee.on() zigbee.on()
} }
def off() { def off() {
state.trigger = "on/off"
zigbee.off() zigbee.off()
} }
def refresh() { def refresh() {
def refreshCmds = [ def refreshCmds = [
"st wattr 0x${device.deviceNetworkId} 1 8 0x10 0x21 {${state?.dOnOff ?: '0000'}}", "delay 2000" "st wattr 0x${device.deviceNetworkId} 1 8 0x10 0x21 {${state?.dOnOff ?: '0000'}}", "delay 500"
] ]
return refreshCmds + zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.onOffConfig() return refreshCmds + zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.onOffConfig()
} }
def setLevel(value) { def setLevel(value) {
state.trigger = "setLevel"
def cmd def cmd
def delayForRefresh = 500 def delayForRefresh = 500
if (dimRate && (state?.rate != null)) { if (dimRate && (state?.rate != null)) {

View File

@@ -0,0 +1,351 @@
/**
* Copyright 2015 SmartThings
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under the License.
*
* GE/Jasco ZigBee Dimmer
*
* Author: SmartThings
* Date: 2015-07-01
*/
metadata {
definition (name: "GE ZigBee Dimmer", namespace: "smartthings", author: "SmartThings") {
capability "Switch"
capability "Switch Level"
capability "Power Meter"
capability "Configuration"
capability "Refresh"
capability "Actuator"
capability "Sensor"
}
// simulator metadata
simulator {
// status messages
status "on": "on/off: 1"
status "off": "on/off: 0"
// reply messages
reply "zcl on-off on": "on/off: 1"
reply "zcl on-off off": "on/off: 0"
}
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.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"
}
tileAttribute ("device.level", key: "SLIDER_CONTROL") {
attributeState "level", action:"switch level.setLevel"
}
}
valueTile("level", "device.level", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "level", label: 'Level ${currentValue}%'
}
valueTile("power", "device.power", decoration: "flat", width: 2, height: 2) {
state "power", label:'${currentValue} W'
}
standardTile("refresh", "device.power", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
}
main "switch"
details(["switch", "level", "power","levelSliderControl","refresh"])
}
}
// Parse incoming device messages to generate events
def parse(String description) {
log.debug "description is $description"
def finalResult = isKnownDescription(description)
if (finalResult != "false") {
log.info finalResult
if (finalResult.type == "update") {
log.info "$device updates: ${finalResult.value}"
}
else if (finalResult.type == "power") {
def powerValue = (finalResult.value as Integer)/10
sendEvent(name: "power", value: powerValue)
/*
Dividing by 10 as the Divisor is 10000 and unit is kW for the device. AttrId: 0302 and 0300. Simplifying to 10
power level is an integer. The exact power level with correct units needs to be handled in the device type
to account for the different Divisor value (AttrId: 0302) and POWER Unit (AttrId: 0300). CLUSTER for simple metering is 0702
*/
}
else {
sendEvent(name: finalResult.type, value: finalResult.value)
}
}
else {
log.warn "DID NOT PARSE MESSAGE for description : $description"
log.debug parseDescriptionAsMap(description)
}
}
// Commands to device
def zigbeeCommand(cluster, attribute){
["st cmd 0x${device.deviceNetworkId} ${endpointId} ${cluster} ${attribute} {}"]
}
def off() {
zigbeeCommand("6", "0")
}
def on() {
zigbeeCommand("6", "1")
}
def setLevel(value) {
value = value as Integer
if (value == 0) {
off()
}
else {
sendEvent(name: "level", value: value)
setLevelWithRate(value, "0000") + ["delay 1000"] + on() //value is between 0 to 100; GE does NOT switch on if OFF
}
}
def refresh() {
[
"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} 0x0702 0x0400", "delay 500"
]
}
def configure() {
onOffConfig() + levelConfig() + powerConfig() + refresh()
}
private getEndpointId() {
new BigInteger(device.endpointId, 16).toString()
}
private hex(value, width=2) {
def s = new BigInteger(Math.round(value).toString()).toString(16)
while (s.size() < width) {
s = "0" + s
}
s
}
private String swapEndianHex(String hex) {
reverseArray(hex.decodeHex()).encodeHex()
}
private Integer convertHexToInt(hex) {
Integer.parseInt(hex,16)
}
//Need to reverse array of size 2
private byte[] reverseArray(byte[] array) {
byte tmp;
tmp = array[1];
array[1] = array[0];
array[0] = tmp;
return array
}
def parseDescriptionAsMap(description) {
if (description?.startsWith("read attr -")) {
(description - "read attr - ").split(",").inject([:]) { map, param ->
def nameAndValue = param.split(":")
map += [(nameAndValue[0].trim()): nameAndValue[1].trim()]
}
}
else if (description?.startsWith("catchall: ")) {
def seg = (description - "catchall: ").split(" ")
def zigbeeMap = [:]
zigbeeMap += [raw: (description - "catchall: ")]
zigbeeMap += [profileId: seg[0]]
zigbeeMap += [clusterId: seg[1]]
zigbeeMap += [sourceEndpoint: seg[2]]
zigbeeMap += [destinationEndpoint: seg[3]]
zigbeeMap += [options: seg[4]]
zigbeeMap += [messageType: seg[5]]
zigbeeMap += [dni: seg[6]]
zigbeeMap += [isClusterSpecific: Short.valueOf(seg[7], 16) != 0]
zigbeeMap += [isManufacturerSpecific: Short.valueOf(seg[8], 16) != 0]
zigbeeMap += [manufacturerId: seg[9]]
zigbeeMap += [command: seg[10]]
zigbeeMap += [direction: seg[11]]
zigbeeMap += [data: seg.size() > 12 ? seg[12].split("").findAll { it }.collate(2).collect {
it.join('')
} : []]
zigbeeMap
}
}
def isKnownDescription(description) {
if ((description?.startsWith("catchall:")) || (description?.startsWith("read attr -"))) {
def descMap = parseDescriptionAsMap(description)
if (descMap.cluster == "0006" || descMap.clusterId == "0006") {
isDescriptionOnOff(descMap)
}
else if (descMap.cluster == "0008" || descMap.clusterId == "0008"){
isDescriptionLevel(descMap)
}
else if (descMap.cluster == "0702" || descMap.clusterId == "0702"){
isDescriptionPower(descMap)
}
else {
return "false"
}
}
else if(description?.startsWith("on/off:")) {
def switchValue = description?.endsWith("1") ? "on" : "off"
return [type: "switch", value : switchValue]
}
else {
return "false"
}
}
def isDescriptionOnOff(descMap) {
def switchValue = "undefined"
if (descMap.cluster == "0006") { //cluster info from read attr
value = descMap.value
if (value == "01"){
switchValue = "on"
}
else if (value == "00"){
switchValue = "off"
}
}
else if (descMap.clusterId == "0006") {
//cluster info from catch all
//command 0B is Default response and the last two bytes are [on/off][success]. on/off=00, success=00
//command 01 is Read attr response. the last two bytes are [datatype][value]. boolean datatype=10; on/off value = 01/00
if ((descMap.command=="0B" && descMap.raw.endsWith("0100")) || (descMap.command=="01" && descMap.raw.endsWith("1001"))){
switchValue = "on"
}
else if ((descMap.command=="0B" && descMap.raw.endsWith("0000")) || (descMap.command=="01" && descMap.raw.endsWith("1000"))){
switchValue = "off"
}
else if(descMap.command=="07"){
return [type: "update", value : "switch (0006) capability configured successfully"]
}
}
if (switchValue != "undefined"){
return [type: "switch", value : switchValue]
}
else {
return "false"
}
}
//@return - false or "success" or level [0-100]
def isDescriptionLevel(descMap) {
def dimmerValue = -1
if (descMap.cluster == "0008"){
//TODO: the message returned with catchall is command 0B with clusterId 0008. That is just a confirmation message
def value = convertHexToInt(descMap.value)
dimmerValue = Math.round(value * 100 / 255)
if(dimmerValue==0 && value > 0) {
dimmerValue = 1 //handling for non-zero hex value less than 3
}
}
else if(descMap.clusterId == "0008") {
if(descMap.command=="0B"){
return [type: "update", value : "level updated successfully"] //device updating the level change was successful. no value sent.
}
else if(descMap.command=="07"){
return [type: "update", value : "level (0008) capability configured successfully"]
}
}
if (dimmerValue != -1){
return [type: "level", value : dimmerValue]
}
else {
return "false"
}
}
def isDescriptionPower(descMap) {
def powerValue = "undefined"
if (descMap.cluster == "0702") {
if (descMap.attrId == "0400") {
powerValue = convertHexToInt(descMap.value)
}
}
else if (descMap.clusterId == "0702") {
if(descMap.command=="07"){
return [type: "update", value : "power (0702) capability configured successfully"]
}
}
if (powerValue != "undefined"){
return [type: "power", value : powerValue]
}
else {
return "false"
}
}
def onOffConfig() {
[
"zdo bind 0x${device.deviceNetworkId} 1 ${endpointId} 6 {${device.zigbeeId}} {}", "delay 200",
"zcl global send-me-a-report 6 0 0x10 0 600 {01}",
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 1500"
]
}
//level config for devices with min reporting interval as 5 seconds and reporting interval if no activity as 1hour (3600s)
//min level change is 01
def levelConfig() {
[
"zdo bind 0x${device.deviceNetworkId} 1 ${endpointId} 8 {${device.zigbeeId}} {}", "delay 200",
"zcl global send-me-a-report 8 0 0x20 1 3600 {01}",
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 1500"
]
}
//power config for devices with min reporting interval as 1 seconds and reporting interval if no activity as 10min (600s)
//min change in value is 05
def powerConfig() {
[
//Meter (Power) Reporting
"zdo bind 0x${device.deviceNetworkId} 1 ${endpointId} 0x0702 {${device.zigbeeId}} {}", "delay 200",
"zcl global send-me-a-report 0x0702 0x0400 0x2A 1 600 {05}",
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 1500"
]
}
def setLevelWithRate(level, rate) {
if(rate == null){
rate = "0000"
}
level = convertToHexString(level * 255 / 100) //Converting the 0-100 range to 0-FF range in hex
["st cmd 0x${device.deviceNetworkId} ${endpointId} 8 4 {$level $rate}"]
}
String convertToHexString(value, width=2) {
def s = new BigInteger(Math.round(value).toString()).toString(16)
while (s.size() < width) {
s = "0" + s
}
s
}

View File

@@ -0,0 +1,284 @@
/**
* Copyright 2015 SmartThings
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under the License.
*
* GE/Jasco ZigBee Switch
*
* Author: SmartThings
* Date: 2015-07-01
*/
metadata {
// Automatically generated. Make future change here.
definition (name: "GE ZigBee Switch", namespace: "smartthings", author: "SmartThings") {
capability "Switch"
capability "Power Meter"
capability "Configuration"
capability "Refresh"
capability "Actuator"
capability "Sensor"
}
// simulator metadata
simulator {
// status messages
status "on": "on/off: 1"
status "off": "on/off: 0"
// reply messages
reply "zcl on-off on": "on/off: 1"
reply "zcl on-off off": "on/off: 0"
}
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.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"
}
}
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
}
valueTile("power", "device.power", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "power", label:'${currentValue} Watts'
}
main "switch"
details(["switch", "power", "refresh"])
}
}
// Parse incoming device messages to generate events
def parse(String description) {
log.debug "description is $description"
def finalResult = isKnownDescription(description)
if (finalResult != "false") {
log.info finalResult
if (finalResult.type == "update") {
log.info "$device updates: ${finalResult.value}"
}
else if (finalResult.type == "power") {
def powerValue = (finalResult.value as Integer)/10
sendEvent(name: "power", value: powerValue)
/*
Dividing by 10 as the Divisor is 10000 and unit is kW for the device. AttrId: 0302 and 0300. Simplifying to 10
power level is an integer. The exact power level with correct units needs to be handled in the device type
to account for the different Divisor value (AttrId: 0302) and POWER Unit (AttrId: 0300). CLUSTER for simple metering is 0702
*/
}
else {
sendEvent(name: finalResult.type, value: finalResult.value)
}
}
else {
log.warn "DID NOT PARSE MESSAGE for description : $description"
log.debug parseDescriptionAsMap(description)
}
}
// Commands to device
def zigbeeCommand(cluster, attribute){
"st cmd 0x${device.deviceNetworkId} ${endpointId} ${cluster} ${attribute} {}"
}
def off() {
zigbeeCommand("6", "0")
}
def on() {
zigbeeCommand("6", "1")
}
def refresh() {
[
"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} 0x0702 0x0400", "delay 500"
]
}
def configure() {
onOffConfig() + powerConfig() + refresh()
}
private getEndpointId() {
new BigInteger(device.endpointId, 16).toString()
}
private hex(value, width=2) {
def s = new BigInteger(Math.round(value).toString()).toString(16)
while (s.size() < width) {
s = "0" + s
}
s
}
private String swapEndianHex(String hex) {
reverseArray(hex.decodeHex()).encodeHex()
}
private Integer convertHexToInt(hex) {
Integer.parseInt(hex,16)
}
//Need to reverse array of size 2
private byte[] reverseArray(byte[] array) {
byte tmp;
tmp = array[1];
array[1] = array[0];
array[0] = tmp;
return array
}
def parseDescriptionAsMap(description) {
if (description?.startsWith("read attr -")) {
(description - "read attr - ").split(",").inject([:]) { map, param ->
def nameAndValue = param.split(":")
map += [(nameAndValue[0].trim()): nameAndValue[1].trim()]
}
}
else if (description?.startsWith("catchall: ")) {
def seg = (description - "catchall: ").split(" ")
def zigbeeMap = [:]
zigbeeMap += [raw: (description - "catchall: ")]
zigbeeMap += [profileId: seg[0]]
zigbeeMap += [clusterId: seg[1]]
zigbeeMap += [sourceEndpoint: seg[2]]
zigbeeMap += [destinationEndpoint: seg[3]]
zigbeeMap += [options: seg[4]]
zigbeeMap += [messageType: seg[5]]
zigbeeMap += [dni: seg[6]]
zigbeeMap += [isClusterSpecific: Short.valueOf(seg[7], 16) != 0]
zigbeeMap += [isManufacturerSpecific: Short.valueOf(seg[8], 16) != 0]
zigbeeMap += [manufacturerId: seg[9]]
zigbeeMap += [command: seg[10]]
zigbeeMap += [direction: seg[11]]
zigbeeMap += [data: seg.size() > 12 ? seg[12].split("").findAll { it }.collate(2).collect {
it.join('')
} : []]
zigbeeMap
}
}
def isKnownDescription(description) {
if ((description?.startsWith("catchall:")) || (description?.startsWith("read attr -"))) {
def descMap = parseDescriptionAsMap(description)
if (descMap.cluster == "0006" || descMap.clusterId == "0006") {
isDescriptionOnOff(descMap)
}
else if (descMap.cluster == "0702" || descMap.clusterId == "0702"){
isDescriptionPower(descMap)
}
else {
return "false"
}
}
else if(description?.startsWith("on/off:")) {
def switchValue = description?.endsWith("1") ? "on" : "off"
return [type: "switch", value : switchValue]
}
else {
return "false"
}
}
def isDescriptionOnOff(descMap) {
def switchValue = "undefined"
if (descMap.cluster == "0006") { //cluster info from read attr
value = descMap.value
if (value == "01"){
switchValue = "on"
}
else if (value == "00"){
switchValue = "off"
}
}
else if (descMap.clusterId == "0006") {
//cluster info from catch all
//command 0B is Default response and the last two bytes are [on/off][success]. on/off=00, success=00
//command 01 is Read attr response. the last two bytes are [datatype][value]. boolean datatype=10; on/off value = 01/00
if ((descMap.command=="0B" && descMap.raw.endsWith("0100")) || (descMap.command=="01" && descMap.raw.endsWith("1001"))){
switchValue = "on"
}
else if ((descMap.command=="0B" && descMap.raw.endsWith("0000")) || (descMap.command=="01" && descMap.raw.endsWith("1000"))){
switchValue = "off"
}
else if(descMap.command=="07"){
return [type: "update", value : "switch (0006) capability configured successfully"]
}
}
if (switchValue != "undefined"){
return [type: "switch", value : switchValue]
}
else {
return "false"
}
}
def isDescriptionPower(descMap) {
def powerValue = "undefined"
if (descMap.cluster == "0702") {
if (descMap.attrId == "0400") {
powerValue = convertHexToInt(descMap.value)
}
}
else if (descMap.clusterId == "0702") {
if(descMap.command=="07"){
return [type: "update", value : "power (0702) capability configured successfully"]
}
}
if (powerValue != "undefined"){
return [type: "power", value : powerValue]
}
else {
return "false"
}
}
def onOffConfig() {
[
"zdo bind 0x${device.deviceNetworkId} 1 ${endpointId} 6 {${device.zigbeeId}} {}", "delay 200",
"zcl global send-me-a-report 6 0 0x10 0 600 {01}",
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 1500"
]
}
//power config for devices with min reporting interval as 1 seconds and reporting interval if no activity as 10min (600s)
//min change in value is 05
def powerConfig() {
[
//Meter (Power) Reporting
"zdo bind 0x${device.deviceNetworkId} 1 ${endpointId} 0x0702 {${device.zigbeeId}} {}", "delay 200",
"zcl global send-me-a-report 0x0702 0x0400 0x2A 1 600 {05}",
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 1500"
]
}
String convertToHexString(value, width=2) {
def s = new BigInteger(Math.round(value).toString()).toString(16)
while (s.size() < width) {
s = "0" + s
}
s
}

View File

@@ -31,7 +31,7 @@ metadata {
tileAttribute("sessionStatus", key: "PRIMARY_CONTROL") { tileAttribute("sessionStatus", key: "PRIMARY_CONTROL") {
attributeState "cancelled", action: "timed session.start", icon: "http://f.cl.ly/items/322n181j2K3f281r2s0A/playbutton.png", backgroundColor: "#ffffff", nextState: "running" attributeState "cancelled", action: "timed session.start", icon: "http://f.cl.ly/items/322n181j2K3f281r2s0A/playbutton.png", backgroundColor: "#ffffff", nextState: "running"
attributeState "stopped", action: "timed session.start", icon: "http://f.cl.ly/items/322n181j2K3f281r2s0A/playbutton.png", backgroundColor: "#ffffff", nextState: "cancelled" attributeState "stopped", action: "timed session.start", icon: "http://f.cl.ly/items/322n181j2K3f281r2s0A/playbutton.png", backgroundColor: "#ffffff", nextState: "cancelled"
attributeState "running", action: "timed session.stop", icon: "http://f.cl.ly/items/0B3y3p2V3X2l3P3y3W09/stopbutton.png", backgroundColor: "#00A0DC", nextState: "cancelled" attributeState "running", action: "timed session.stop", icon: "http://f.cl.ly/items/0B3y3p2V3X2l3P3y3W09/stopbutton.png", backgroundColor: "#79b821", nextState: "cancelled"
} }
tileAttribute("timeRemaining", key: "SECONDARY_CONTROL") { tileAttribute("timeRemaining", key: "SECONDARY_CONTROL") {
attributeState "timeRemaining", label:'${currentValue} remaining' attributeState "timeRemaining", label:'${currentValue} remaining'
@@ -45,7 +45,7 @@ metadata {
standardTile("sessionStatusTile", "sessionStatus", width: 1, height: 1, canChangeIcon: true) { standardTile("sessionStatusTile", "sessionStatus", width: 1, height: 1, canChangeIcon: true) {
state "cancelled", label: "Stopped", action: "timed session.start", backgroundColor: "#ffffff", icon: "http://f.cl.ly/items/1J1g0H2P0S1G1f2O1s1s/icon.png" state "cancelled", label: "Stopped", action: "timed session.start", backgroundColor: "#ffffff", icon: "http://f.cl.ly/items/1J1g0H2P0S1G1f2O1s1s/icon.png"
state "stopped", label: "Stopped", action: "timed session.start", backgroundColor: "#ffffff", icon: "http://f.cl.ly/items/1J1g0H2P0S1G1f2O1s1s/icon.png" state "stopped", label: "Stopped", action: "timed session.start", backgroundColor: "#ffffff", icon: "http://f.cl.ly/items/1J1g0H2P0S1G1f2O1s1s/icon.png"
state "running", label: "Running", action: "timed session.stop", backgroundColor: "#00A0DC", icon: "http://f.cl.ly/items/1J1g0H2P0S1G1f2O1s1s/icon.png" state "running", label: "Running", action: "timed session.stop", backgroundColor: "#79b821", icon: "http://f.cl.ly/items/1J1g0H2P0S1G1f2O1s1s/icon.png"
} }
// duration // duration

View File

@@ -32,7 +32,7 @@ metadata {
tiles { tiles {
standardTile("button", "device.switch", width: 2, height: 2, canChangeIcon: true) { standardTile("button", "device.switch", width: 2, height: 2, canChangeIcon: true) {
state "off", label: 'Off', action: "switch.on", icon: "st.harmony.harmony-hub-icon", backgroundColor: "#ffffff", nextState: "on" state "off", label: 'Off', action: "switch.on", icon: "st.harmony.harmony-hub-icon", backgroundColor: "#ffffff", nextState: "on"
state "on", label: 'On', action: "switch.off", icon: "st.harmony.harmony-hub-icon", backgroundColor: "#00A0DC", nextState: "off" state "on", label: 'On', action: "switch.off", icon: "st.harmony.harmony-hub-icon", backgroundColor: "#79b821", nextState: "off"
} }
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") { standardTile("refresh", "device.switch", 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

@@ -16,8 +16,6 @@ metadata {
capability "Switch" capability "Switch"
capability "Refresh" capability "Refresh"
capability "Sensor" capability "Sensor"
capability "Health Check"
capability "Light"
command "setAdjustedColor" command "setAdjustedColor"
command "reset" command "reset"
@@ -31,9 +29,9 @@ metadata {
tiles (scale: 2){ tiles (scale: 2){
multiAttributeTile(name:"rich-control", type: "lighting", width: 6, height: 4, canChangeIcon: true){ multiAttributeTile(name:"rich-control", type: "lighting", width: 6, height: 4, canChangeIcon: true){
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") { tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
attributeState "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#00A0DC", nextState:"turningOff" 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 "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:"#00A0DC", nextState:"turningOff" attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn" attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
} }
tileAttribute ("device.level", key: "SLIDER_CONTROL") { tileAttribute ("device.level", key: "SLIDER_CONTROL") {
@@ -45,7 +43,7 @@ metadata {
} }
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.refresh", height: 2, width: 2, inactiveLabel: false, decoration: "flat") { standardTile("refresh", "device.refresh", height: 2, width: 2, inactiveLabel: false, decoration: "flat") {
@@ -53,14 +51,10 @@ metadata {
} }
main(["rich-control"]) main(["rich-control"])
details(["rich-control", "reset", "refresh"]) details(["rich-control", "colorTempSliderControl", "colorTemp", "reset", "refresh"])
} }
} }
void installed() {
sendEvent(name: "DeviceWatch-Enroll", value: "{\"protocol\": \"LAN\", \"scheme\":\"untracked\", \"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"
@@ -81,78 +75,118 @@ 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)) { if (verifyPercent(percent)) {
log.trace parent.setLevel(this, percent) parent.setLevel(this, percent)
sendEvent(name: "level", value: percent, descriptionText: "Level has changed to ${percent}%")
sendEvent(name: "switch", value: "on")
} }
} }
void setSaturation(percent) { void setSaturation(percent) {
log.debug "Executing 'setSaturation'" log.debug "Executing 'setSaturation'"
if (verifyPercent(percent)) { if (verifyPercent(percent)) {
log.trace parent.setSaturation(this, percent) parent.setSaturation(this, percent)
sendEvent(name: "saturation", value: percent, displayed: false)
} }
} }
void setHue(percent) { void setHue(percent) {
log.debug "Executing 'setHue'" log.debug "Executing 'setHue'"
if (verifyPercent(percent)) { if (verifyPercent(percent)) {
log.trace parent.setHue(this, percent) parent.setHue(this, percent)
sendEvent(name: "hue", value: percent, displayed: false)
} }
} }
void setColor(value) { void setColor(value) {
log.debug "setColor: ${value}, $this"
def events = [] def events = []
def validValues = [:] def validValues = [:]
if (verifyPercent(value.hue)) { if (verifyPercent(value.hue)) {
events << createEvent(name: "hue", value: value.hue, displayed: false)
validValues.hue = value.hue validValues.hue = value.hue
} }
if (verifyPercent(value.saturation)) { if (verifyPercent(value.saturation)) {
events << createEvent(name: "saturation", value: value.saturation, displayed: false)
validValues.saturation = value.saturation validValues.saturation = value.saturation
} }
if (value.hex != null) { if (value.hex != null) {
if (value.hex ==~ /^\#([A-Fa-f0-9]){6}$/) { if (value.hex ==~ /^\#([A-Fa-f0-9]){6}$/) {
events << createEvent(name: "color", value: value.hex)
validValues.hex = value.hex validValues.hex = value.hex
} else { } else {
log.warn "$value.hex is not a valid color" log.warn "$value.hex is not a valid color"
} }
} }
if (verifyPercent(value.level)) { if (verifyPercent(value.level)) {
events << createEvent(name: "level", value: value.level, descriptionText: "Level has changed to ${value.level}%")
validValues.level = value.level validValues.level = value.level
} }
if (value.switch == "off" || (value.level != null && value.level <= 0)) { if (value.switch == "off" || (value.level != null && value.level <= 0)) {
events << createEvent(name: "switch", value: "off")
validValues.switch = "off" validValues.switch = "off"
} else { } else {
events << createEvent(name: "switch", value: "on")
validValues.switch = "on" validValues.switch = "on"
} }
if (!validValues.isEmpty()) { if (!events.isEmpty()) {
log.trace parent.setColor(this, validValues) parent.setColor(this, validValues)
}
events.each {
sendEvent(it)
} }
} }
void reset() { void reset() {
log.debug "Executing 'reset'" log.debug "Executing 'reset'"
def value = [hue:20, saturation:2] def value = [level:100, saturation:56, hue:23]
setAdjustedColor(value) 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 { } else {
log.warn "Invalid color input $value" log.warn "Invalid color input"
}
}
void setColorTemperature(value) {
if (value) {
log.trace "setColorTemperature: ${value}k"
parent.setColorTemperature(this, value)
sendEvent(name: "colorTemperature", value: value)
sendEvent(name: "switch", value: "on")
} else {
log.warn "Invalid color temperature"
} }
} }
@@ -161,6 +195,22 @@ void refresh() {
parent.manualRefresh() parent.manualRefresh()
} }
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
}
def verifyPercent(percent) { def verifyPercent(percent) {
if (percent == null) if (percent == null)
@@ -172,4 +222,3 @@ def verifyPercent(percent) {
return false return false
} }
} }

View File

@@ -7,16 +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 "Bridge" attribute "serialNumber", "string"
capability "Health Check"
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 {
@@ -25,30 +17,25 @@ metadata {
tiles(scale: 2) { tiles(scale: 2) {
multiAttributeTile(name:"rich-control"){ multiAttributeTile(name:"rich-control"){
tileAttribute ("device.status", key: "PRIMARY_CONTROL") { tileAttribute ("", 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: "#00A0DC"
} }
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' valueTile("serialNumber", "device.serialNumber", decoration: "flat", height: 1, width: 2, inactiveLabel: false) {
state "default", label:'SN: ${currentValue}'
} }
valueTile("idNumber", "device.idNumber", decoration: "flat", height: 2, width: 6, inactiveLabel: false) { valueTile("networkAddress", "device.networkAddress", decoration: "flat", height: 2, width: 4, inactiveLabel: false) {
state "default", label:'ID: ${currentValue}' state "default", label:'${currentValue}', height: 1, width: 2, inactiveLabel: false
}
valueTile("networkAddress", "device.networkAddress", decoration: "flat", height: 2, width: 6, inactiveLabel: false) {
state "default", label:'IP: ${currentValue}'
} }
main (["rich-control"]) main (["rich-control"])
details(["rich-control", "doNotRemove", "idNumber", "networkAddress"]) details(["rich-control", "networkAddress"])
} }
} }
void installed() {
sendEvent(name: "DeviceWatch-Enroll", value: "{\"protocol\": \"LAN\", \"scheme\":\"untracked\", \"hubHardwareId\": \"${device.hub.hardwareID}\"}", displayed: false)
}
// parse events into attributes // parse events into attributes
def parse(description) { def parse(description) {
log.debug "Parsing '${description}'" log.debug "Parsing '${description}'"
@@ -69,7 +56,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"]
@@ -77,14 +64,18 @@ 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) parent.hubVerification(device.hub.id, msg.body)
} }
} }
} }
} }
results results
} }

View File

@@ -17,8 +17,6 @@ metadata {
capability "Switch" capability "Switch"
capability "Refresh" capability "Refresh"
capability "Sensor" capability "Sensor"
capability "Health Check"
capability "Light"
command "setAdjustedColor" command "setAdjustedColor"
command "reset" command "reset"
@@ -32,9 +30,9 @@ metadata {
tiles (scale: 2){ tiles (scale: 2){
multiAttributeTile(name:"rich-control", type: "lighting", width: 6, height: 4, canChangeIcon: true){ multiAttributeTile(name:"rich-control", type: "lighting", width: 6, height: 4, canChangeIcon: true){
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") { tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
attributeState "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#00A0DC", nextState:"turningOff" 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 "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:"#00A0DC", nextState:"turningOff" attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn" attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
} }
tileAttribute ("device.level", key: "SLIDER_CONTROL") { tileAttribute ("device.level", key: "SLIDER_CONTROL") {
@@ -45,16 +43,16 @@ metadata {
} }
} }
controlTile("colorTempSliderControl", "device.colorTemperature", "slider", width: 4, height: 2, inactiveLabel: false, range:"(2000..6500)") { controlTile("colorTempSliderControl", "device.colorTemperature", "slider", width: 4, height: 2, inactiveLabel: false, range:"(2000..6500)") {
state "colorTemperature", action:"color temperature.setColorTemperature" state "colorTemperature", action:"color temperature.setColorTemperature"
} }
valueTile("colorTemp", "device.colorTemperature", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { valueTile("colorTemp", "device.colorTemperature", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "colorTemperature", label: 'WHITES' 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.refresh", height: 2, width: 2, inactiveLabel: false, decoration: "flat") { standardTile("refresh", "device.refresh", height: 2, width: 2, inactiveLabel: false, decoration: "flat") {
@@ -66,10 +64,6 @@ metadata {
} }
} }
void installed() {
sendEvent(name: "DeviceWatch-Enroll", value: "{\"protocol\": \"LAN\", \"scheme\":\"untracked\", \"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"
@@ -90,92 +84,141 @@ 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)) { if (verifyPercent(percent)) {
log.trace parent.setLevel(this, percent) parent.setLevel(this, percent)
sendEvent(name: "level", value: percent, descriptionText: "Level has changed to ${percent}%")
sendEvent(name: "switch", value: "on")
} }
} }
void setSaturation(percent) { void setSaturation(percent) {
log.debug "Executing 'setSaturation'" log.debug "Executing 'setSaturation'"
if (verifyPercent(percent)) { if (verifyPercent(percent)) {
log.trace parent.setSaturation(this, percent) parent.setSaturation(this, percent)
sendEvent(name: "saturation", value: percent, displayed: false)
} }
} }
void setHue(percent) { void setHue(percent) {
log.debug "Executing 'setHue'" log.debug "Executing 'setHue'"
if (verifyPercent(percent)) { if (verifyPercent(percent)) {
log.trace parent.setHue(this, percent) parent.setHue(this, percent)
sendEvent(name: "hue", value: percent, displayed: false)
} }
} }
void setColor(value) { void setColor(value) {
log.debug "setColor: ${value}, $this"
def events = [] def events = []
def validValues = [:] def validValues = [:]
if (verifyPercent(value.hue)) { if (verifyPercent(value.hue)) {
events << createEvent(name: "hue", value: value.hue, displayed: false)
validValues.hue = value.hue validValues.hue = value.hue
} }
if (verifyPercent(value.saturation)) { if (verifyPercent(value.saturation)) {
events << createEvent(name: "saturation", value: value.saturation, displayed: false)
validValues.saturation = value.saturation validValues.saturation = value.saturation
} }
if (value.hex != null) { if (value.hex != null) {
if (value.hex ==~ /^\#([A-Fa-f0-9]){6}$/) { if (value.hex ==~ /^\#([A-Fa-f0-9]){6}$/) {
events << createEvent(name: "color", value: value.hex)
validValues.hex = value.hex validValues.hex = value.hex
} else { } else {
log.warn "$value.hex is not a valid color" log.warn "$value.hex is not a valid color"
} }
} }
if (verifyPercent(value.level)) { if (verifyPercent(value.level)) {
events << createEvent(name: "level", value: value.level, descriptionText: "Level has changed to ${value.level}%")
validValues.level = value.level validValues.level = value.level
} }
if (value.switch == "off" || (value.level != null && value.level <= 0)) { if (value.switch == "off" || (value.level != null && value.level <= 0)) {
events << createEvent(name: "switch", value: "off")
validValues.switch = "off" validValues.switch = "off"
} else { } else {
events << createEvent(name: "switch", value: "on")
validValues.switch = "on" validValues.switch = "on"
} }
if (!validValues.isEmpty()) { if (!events.isEmpty()) {
log.trace parent.setColor(this, validValues) parent.setColor(this, validValues)
}
events.each {
sendEvent(it)
} }
} }
void reset() { void reset() {
log.debug "Executing 'reset'" log.debug "Executing 'reset'"
setColorTemperature(4000) def value = [level:100, 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 { } else {
log.warn "Invalid color input $value" log.warn "Invalid color input"
} }
} }
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)
sendEvent(name: "colorTemperature", value: value)
sendEvent(name: "switch", value: "on")
} else { } else {
log.warn "Invalid color temperature $value" log.warn "Invalid color temperature"
} }
} }
void refresh() { void refresh() {
log.debug "Executing 'refresh'" log.debug "Executing 'refresh'"
parent?.manualRefresh() parent.manualRefresh()
}
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
} }
def verifyPercent(percent) { def verifyPercent(percent) {
@@ -188,4 +231,3 @@ def verifyPercent(percent) {
return false return false
} }
} }

View File

@@ -14,8 +14,6 @@ metadata {
capability "Switch" capability "Switch"
capability "Refresh" capability "Refresh"
capability "Sensor" capability "Sensor"
capability "Health Check"
capability "Light"
command "refresh" command "refresh"
} }
@@ -27,9 +25,9 @@ metadata {
tiles(scale: 2) { tiles(scale: 2) {
multiAttributeTile(name:"rich-control", type: "lighting", canChangeIcon: true){ multiAttributeTile(name:"rich-control", type: "lighting", canChangeIcon: true){
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") { tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
attributeState "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#00A0DC", nextState:"turningOff" 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 "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:"#00A0DC", nextState:"turningOff" attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn" attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
} }
tileAttribute ("device.level", key: "SLIDER_CONTROL") { tileAttribute ("device.level", key: "SLIDER_CONTROL") {
@@ -50,10 +48,6 @@ metadata {
} }
} }
void installed() {
sendEvent(name: "DeviceWatch-Enroll", value: "{\"protocol\": \"LAN\", \"scheme\":\"untracked\", \"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"
@@ -74,16 +68,20 @@ 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 setLevel(percent) { void setLevel(percent) {
log.debug "Executing 'setLevel'" log.debug "Executing 'setLevel'"
if (percent != null && percent >= 0 && percent <= 100) { if (percent != null && percent >= 0 && percent <= 100) {
parent.setLevel(this, percent) parent.setLevel(this, percent)
sendEvent(name: "level", value: percent)
sendEvent(name: "switch", value: "on")
} else { } else {
log.warn "$percent is not 0-100" log.warn "$percent is not 0-100"
} }
@@ -93,4 +91,3 @@ void refresh() {
log.debug "Executing 'refresh'" log.debug "Executing 'refresh'"
parent.manualRefresh() parent.manualRefresh()
} }

View File

@@ -15,8 +15,6 @@ metadata {
capability "Color Temperature" capability "Color Temperature"
capability "Switch" capability "Switch"
capability "Refresh" capability "Refresh"
capability "Health Check"
capability "Light"
command "refresh" command "refresh"
} }
@@ -28,9 +26,9 @@ metadata {
tiles (scale: 2){ tiles (scale: 2){
multiAttributeTile(name:"rich-control", type: "lighting", width: 6, height: 4, canChangeIcon: true){ multiAttributeTile(name:"rich-control", type: "lighting", width: 6, height: 4, canChangeIcon: true){
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") { tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
attributeState "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#00A0DC", nextState:"turningOff" 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 "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:"#00A0DC", nextState:"turningOff" attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn" attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
} }
tileAttribute ("device.level", key: "SLIDER_CONTROL") { tileAttribute ("device.level", key: "SLIDER_CONTROL") {
@@ -38,12 +36,12 @@ metadata {
} }
} }
controlTile("colorTempSliderControl", "device.colorTemperature", "slider", width: 4, height: 2, inactiveLabel: false, range:"(2200..6500)") { controlTile("colorTempSliderControl", "device.colorTemperature", "slider", width: 4, height: 2, inactiveLabel: false, range:"(2000..6500)") {
state "colorTemperature", action:"color temperature.setColorTemperature" state "colorTemperature", action:"color temperature.setColorTemperature"
} }
valueTile("colorTemp", "device.colorTemperature", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { valueTile("colorTemp", "device.colorTemperature", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "colorTemperature", label: 'WHITES' state "colorTemperature", label: '${currentValue} K'
} }
standardTile("refresh", "device.refresh", height: 2, width: 2, inactiveLabel: false, decoration: "flat") { standardTile("refresh", "device.refresh", height: 2, width: 2, inactiveLabel: false, decoration: "flat") {
@@ -55,10 +53,6 @@ metadata {
} }
} }
void installed() {
sendEvent(name: "DeviceWatch-Enroll", value: "{\"protocol\": \"LAN\", \"scheme\":\"untracked\", \"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"
@@ -79,16 +73,20 @@ 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 setLevel(percent) { void setLevel(percent) {
log.debug "Executing 'setLevel'" log.debug "Executing 'setLevel'"
if (percent != null && percent >= 0 && percent <= 100) { if (percent != null && percent >= 0 && percent <= 100) {
log.trace parent.setLevel(this, percent) parent.setLevel(this, percent)
sendEvent(name: "level", value: percent)
sendEvent(name: "switch", value: "on")
} else { } else {
log.warn "$percent is not 0-100" log.warn "$percent is not 0-100"
} }
@@ -97,7 +95,9 @@ void setLevel(percent) {
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)
sendEvent(name: "colorTemperature", value: value)
sendEvent(name: "switch", value: "on")
} else { } else {
log.warn "Invalid color temperature" log.warn "Invalid color temperature"
} }

View File

@@ -29,7 +29,7 @@ metadata {
tiles { tiles {
standardTile("presence", "device.presence", width: 2, height: 2, canChangeBackground: true) { standardTile("presence", "device.presence", width: 2, height: 2, canChangeBackground: true) {
state("present", labelIcon:"st.presence.tile.mobile-present", backgroundColor:"#00A0DC") state("present", labelIcon:"st.presence.tile.mobile-present", backgroundColor:"#53a7c0")
state("not present", labelIcon:"st.presence.tile.mobile-not-present", backgroundColor:"#ffffff") state("not present", labelIcon:"st.presence.tile.mobile-not-present", backgroundColor:"#ffffff")
} }
@@ -39,7 +39,7 @@ metadata {
} }
def generatePresenceEvent(boolean present) { def generatePresenceEvent(boolean present) {
log.info "Life360 generatePresenceEvent($present)" log.debug "Here in generatePresenceEvent!"
def value = formatValue(present) def value = formatValue(present)
def linkText = getLinkText(device) def linkText = getLinkText(device)
def descriptionText = formatDescriptionText(linkText, present) def descriptionText = formatDescriptionText(linkText, present)

View File

@@ -11,10 +11,9 @@ metadata {
capability "Color Temperature" capability "Color Temperature"
capability "Switch" capability "Switch"
capability "Switch Level" // brightness capability "Switch Level" // brightness
capability "Polling"
capability "Refresh" capability "Refresh"
capability "Sensor" capability "Sensor"
capability "Health Check"
capability "Light"
} }
simulator { simulator {
@@ -24,9 +23,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:'${name}', action:"switch.off", icon:"http://hosted.lifx.co/smartthings/v1/196xOn.png", backgroundColor:"#00A0DC", nextState:"turningOff" attributeState "unreachable", label: "?", action:"refresh.refresh", icon:"http://hosted.lifx.co/smartthings/v1/196xUnreachable.png", backgroundColor:"#666666"
attributeState "on", label:'${name}', action:"switch.off", icon:"http://hosted.lifx.co/smartthings/v1/196xOn.png", backgroundColor:"#79b821", nextState:"turningOff"
attributeState "off", label:'${name}', action:"switch.on", icon:"http://hosted.lifx.co/smartthings/v1/196xOff.png", backgroundColor:"#ffffff", nextState:"turningOn" attributeState "off", label:'${name}', action:"switch.on", icon:"http://hosted.lifx.co/smartthings/v1/196xOff.png", backgroundColor:"#ffffff", nextState:"turningOn"
attributeState "turningOn", label:'Turning on', action:"switch.off", icon:"http://hosted.lifx.co/smartthings/v1/196xOn.png", backgroundColor:"#00A0DC", nextState:"turningOff" attributeState "turningOn", label:'Turning on', action:"switch.off", icon:"http://hosted.lifx.co/smartthings/v1/196xOn.png", backgroundColor:"#79b821", nextState:"turningOff"
attributeState "turningOff", label:'Turning off', action:"switch.on", icon:"http://hosted.lifx.co/smartthings/v1/196xOff.png", backgroundColor:"#ffffff", nextState:"turningOn" attributeState "turningOff", label:'Turning off', action:"switch.on", icon:"http://hosted.lifx.co/smartthings/v1/196xOff.png", backgroundColor:"#ffffff", nextState:"turningOn"
} }
@@ -64,8 +64,12 @@ metadata {
} }
} }
void installed() { // parse events into attributes
sendEvent(name: "DeviceWatch-Enroll", value: "{\"protocol\": \"cloud\", \"scheme\":\"untracked\", \"hubHardwareId\": \"${device?.hub?.hardwareID}\"}") def parse(String description) {
if (description == 'updated') {
return // don't poll when config settings is being updated as it may time out
}
poll()
} }
// handle commands // handle commands
@@ -137,6 +141,7 @@ def setLevel(percentage) {
percentage = 1 // clamp to 1% percentage = 1 // clamp to 1%
} }
if (percentage == 0) { if (percentage == 0) {
sendEvent(name: "level", value: 0) // Otherwise the level value tile does not update
return off() // if the brightness is set to 0, just turn it off return off() // if the brightness is set to 0, just turn it off
} }
parent.logErrors(logObject:log) { parent.logErrors(logObject:log) {
@@ -188,17 +193,14 @@ def off() {
return [] return []
} }
def refresh() { def poll() {
log.debug "Executing 'refresh'" log.debug "Executing 'poll' for ${device} ${this} ${device.deviceNetworkId}"
def resp = parent.apiGET("/lights/${selector()}") def resp = parent.apiGET("/lights/${selector()}")
if (resp.status == 404) { if (resp.status == 404) {
state.online = false sendEvent(name: "switch", value: "unreachable")
sendEvent(name: "DeviceWatch-DeviceStatusUpdate", value: "offline", displayed: false)
log.warn "$device is Offline"
return [] return []
} else if (resp.status != 200) { } else if (resp.status != 200) {
log.error("Unexpected result in refresh(): [${resp.status}] ${resp.data}") log.error("Unexpected result in poll(): [${resp.status}] ${resp.data}")
return [] return []
} }
def data = resp.data[0] def data = resp.data[0]
@@ -207,20 +209,19 @@ def refresh() {
sendEvent(name: "label", value: data.label) sendEvent(name: "label", value: data.label)
sendEvent(name: "level", value: Math.round((data.brightness ?: 1) * 100)) sendEvent(name: "level", value: Math.round((data.brightness ?: 1) * 100))
sendEvent(name: "switch.setLevel", value: Math.round((data.brightness ?: 1) * 100)) sendEvent(name: "switch.setLevel", value: Math.round((data.brightness ?: 1) * 100))
sendEvent(name: "switch", value: data.power) sendEvent(name: "switch", value: data.connected ? data.power : "unreachable")
sendEvent(name: "color", value: colorUtil.hslToHex((data.color.hue / 3.6) as int, (data.color.saturation * 100) as int)) sendEvent(name: "color", value: colorUtil.hslToHex((data.color.hue / 3.6) as int, (data.color.saturation * 100) as int))
sendEvent(name: "hue", value: data.color.hue / 3.6) sendEvent(name: "hue", value: data.color.hue / 3.6)
sendEvent(name: "saturation", value: data.color.saturation * 100) sendEvent(name: "saturation", value: data.color.saturation * 100)
sendEvent(name: "colorTemperature", value: data.color.kelvin) sendEvent(name: "colorTemperature", value: data.color.kelvin)
sendEvent(name: "model", value: data.product.name) sendEvent(name: "model", value: "${data.product.company} ${data.product.name}")
if (data.connected) { return []
sendEvent(name: "DeviceWatch-DeviceStatus", value: "online", displayed: false)
log.debug "$device is Online"
} else {
sendEvent(name: "DeviceWatch-DeviceStatus", value: "offline", displayed: false)
log.warn "$device is Offline"
} }
def refresh() {
log.debug "Executing 'refresh'"
poll()
} }
def selector() { def selector() {

View File

@@ -10,10 +10,9 @@ metadata {
capability "Color Temperature" capability "Color Temperature"
capability "Switch" capability "Switch"
capability "Switch Level" // brightness capability "Switch Level" // brightness
capability "Polling"
capability "Refresh" capability "Refresh"
capability "Sensor" capability "Sensor"
capability "Health Check"
capability "Light"
} }
simulator { simulator {
@@ -23,12 +22,13 @@ 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:'${name}', action:"switch.off", icon:"http://hosted.lifx.co/smartthings/v1/196xOn.png", backgroundColor:"#00A0DC", nextState:"turningOff" attributeState "unreachable", label: "?", action:"refresh.refresh", icon:"http://hosted.lifx.co/smartthings/v1/196xUnreachable.png", backgroundColor:"#666666"
attributeState "on", label:'${name}', action:"switch.off", icon:"http://hosted.lifx.co/smartthings/v1/196xOn.png", backgroundColor:"#79b821", nextState:"turningOff"
attributeState "off", label:'${name}', action:"switch.on", icon:"http://hosted.lifx.co/smartthings/v1/196xOff.png", backgroundColor:"#ffffff", nextState:"turningOn" attributeState "off", label:'${name}', action:"switch.on", icon:"http://hosted.lifx.co/smartthings/v1/196xOff.png", backgroundColor:"#ffffff", nextState:"turningOn"
attributeState "turningOn", label:'Turning on', action:"switch.off", icon:"http://hosted.lifx.co/smartthings/v1/196xOn.png", backgroundColor:"#00A0DC", nextState:"turningOff" attributeState "turningOn", label:'Turning on', action:"switch.off", icon:"http://hosted.lifx.co/smartthings/v1/196xOn.png", backgroundColor:"#79b821", nextState:"turningOff"
attributeState "turningOff", label:'Turning off', action:"switch.on", icon:"http://hosted.lifx.co/smartthings/v1/196xOff.png", backgroundColor:"#ffffff", nextState:"turningOn" attributeState "turningOff", label:'Turning off', action:"switch.on", icon:"http://hosted.lifx.co/smartthings/v1/196xOff.png", backgroundColor:"#ffffff", nextState:"turningOn"
}
}
tileAttribute ("device.level", key: "SLIDER_CONTROL") { tileAttribute ("device.level", key: "SLIDER_CONTROL") {
attributeState "level", action:"switch level.setLevel" attributeState "level", action:"switch level.setLevel"
} }
@@ -53,10 +53,15 @@ metadata {
main "switch" main "switch"
details(["switch", "colorTempSliderControl", "colorTemp", "refresh"]) details(["switch", "colorTempSliderControl", "colorTemp", "refresh"])
} }
} }
void installed() { // parse events into attributes
sendEvent(name: "DeviceWatch-Enroll", value: "{\"protocol\": \"cloud\", \"scheme\":\"untracked\", \"hubHardwareId\": \"${device?.hub?.hardwareID}\"}") def parse(String description) {
if (description == 'updated') {
return // don't poll when config settings is being updated as it may time out
}
poll()
} }
// handle commands // handle commands
@@ -66,6 +71,7 @@ def setLevel(percentage) {
percentage = 1 // clamp to 1% percentage = 1 // clamp to 1%
} }
if (percentage == 0) { if (percentage == 0) {
sendEvent(name: "level", value: 0) // Otherwise the level value tile does not update
return off() // if the brightness is set to 0, just turn it off return off() // if the brightness is set to 0, just turn it off
} }
parent.logErrors(logObject:log) { parent.logErrors(logObject:log) {
@@ -117,17 +123,14 @@ def off() {
return [] return []
} }
def refresh() { def poll() {
log.debug "Executing 'refresh'" log.debug "Executing 'poll' for ${device} ${this} ${device.deviceNetworkId}"
def resp = parent.apiGET("/lights/${selector()}") def resp = parent.apiGET("/lights/${selector()}")
if (resp.status == 404) { if (resp.status == 404) {
state.online = false sendEvent(name: "switch", value: "unreachable")
sendEvent(name: "DeviceWatch-DeviceStatusUpdate", value: "offline", displayed: false)
log.warn "$device is Offline"
return [] return []
} else if (resp.status != 200) { } else if (resp.status != 200) {
log.error("Unexpected result in refresh(): [${resp.status}] ${resp.data}") log.error("Unexpected result in poll(): [${resp.status}] ${resp.data}")
return [] return []
} }
def data = resp.data[0] def data = resp.data[0]
@@ -135,17 +138,16 @@ def refresh() {
sendEvent(name: "label", value: data.label) sendEvent(name: "label", value: data.label)
sendEvent(name: "level", value: Math.round((data.brightness ?: 1) * 100)) sendEvent(name: "level", value: Math.round((data.brightness ?: 1) * 100))
sendEvent(name: "switch.setLevel", value: Math.round((data.brightness ?: 1) * 100)) sendEvent(name: "switch.setLevel", value: Math.round((data.brightness ?: 1) * 100))
sendEvent(name: "switch", value: data.power) sendEvent(name: "switch", value: data.connected ? data.power : "unreachable")
sendEvent(name: "colorTemperature", value: data.color.kelvin) sendEvent(name: "colorTemperature", value: data.color.kelvin)
sendEvent(name: "model", value: data.product.name) sendEvent(name: "model", value: data.product.name)
if (data.connected) { return []
sendEvent(name: "DeviceWatch-DeviceStatus", value: "online", displayed: false) }
log.debug "$device is Online"
} else { def refresh() {
sendEvent(name: "DeviceWatch-DeviceStatus", value: "offline", displayed: false) log.debug "Executing 'refresh'"
log.warn "$device is Offline" poll()
}
} }
def selector() { def selector() {

View File

@@ -36,7 +36,6 @@ metadata {
capability "Switch" capability "Switch"
capability "Refresh" capability "Refresh"
capability "Contact Sensor" capability "Contact Sensor"
capability "Light"
attribute "powered", "string" attribute "powered", "string"
@@ -54,11 +53,11 @@ metadata {
// UI tile definitions // UI tile definitions
tiles { tiles {
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) { standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
state "doorClosed", label: "Closed", action: "on", icon: "st.doors.garage.garage-closed", backgroundColor: "#00A0DC" state "doorClosed", label: "Closed", action: "on", icon: "st.doors.garage.garage-closed", backgroundColor: "#79b821"
state "doorOpen", label: "Open", action: "on", icon: "st.doors.garage.garage-open", backgroundColor: "#e86d13" state "doorOpen", label: "Open", action: "on", icon: "st.doors.garage.garage-open", backgroundColor: "#ffa81e"
state "doorOpening", label: "Opening", action: "on", icon: "st.doors.garage.garage-opening", backgroundColor: "#e86d13" state "doorOpening", label: "Opening", action: "on", icon: "st.doors.garage.garage-opening", backgroundColor: "#ffa81e"
state "doorClosing", label: "Closing", action: "on", icon: "st.doors.garage.garage-closing", backgroundColor: "#00A0DC" state "doorClosing", label: "Closing", action: "on", icon: "st.doors.garage.garage-closing", backgroundColor: "#ffa81e"
state "on", label: "Actuate", action: "off", icon: "st.doors.garage.garage-closed", backgroundColor: "#00A0DC" state "on", label: "Actuate", action: "off", icon: "st.doors.garage.garage-closed", backgroundColor: "#53a7c0"
state "off", label: '${name}', action: "on", icon: "st.switches.switch.off", backgroundColor: "#ffffff" state "off", label: '${name}', action: "on", icon: "st.switches.switch.off", backgroundColor: "#ffffff"
} }
standardTile("contact", "device.contact", inactiveLabel: false) { standardTile("contact", "device.contact", inactiveLabel: false) {

View File

@@ -27,8 +27,8 @@ metadata {
tiles { tiles {
standardTile("presence", "device.presence", width: 2, height: 2, canChangeBackground: true) { standardTile("presence", "device.presence", width: 2, height: 2, canChangeBackground: true) {
state("present", labelIcon:"st.presence.tile.mobile-present", backgroundColor:"#00A0DC") state("present", labelIcon:"st.presence.tile.mobile-present", backgroundColor:"#53a7c0")
state("not present", labelIcon:"st.presence.tile.mobile-not-present", backgroundColor:"#ffffff") state("not present", labelIcon:"st.presence.tile.mobile-not-present", backgroundColor:"#ebeef2")
} }
main "presence" main "presence"
details "presence" details "presence"

View File

@@ -32,7 +32,7 @@ metadata {
tiles { tiles {
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) { standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
state "off", label: 'Push', action: "momentary.push", backgroundColor: "#ffffff", nextState: "on" state "off", label: 'Push', action: "momentary.push", backgroundColor: "#ffffff", nextState: "on"
state "on", label: 'Push', action: "momentary.push", backgroundColor: "#00A0DC" state "on", label: 'Push', action: "momentary.push", backgroundColor: "#53a7c0"
} }
main "switch" main "switch"
details "switch" details "switch"
@@ -43,8 +43,8 @@ def parse(String description) {
} }
def push() { def push() {
sendEvent(name: "switch", value: "on", isStateChange: true, displayed: false) sendEvent(name: "switch", value: "on", isStateChange: true, display: false)
sendEvent(name: "switch", value: "off", isStateChange: true, displayed: false) sendEvent(name: "switch", value: "off", isStateChange: true, display: false)
sendEvent(name: "momentary", value: "pushed", isStateChange: true) sendEvent(name: "momentary", value: "pushed", isStateChange: true)
} }

View File

@@ -28,8 +28,8 @@ metadata {
// UI tile definitions // UI tile definitions
tiles { tiles {
standardTile("motion", "device.motion", width: 2, height: 2) { standardTile("motion", "device.motion", width: 2, height: 2) {
state("active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#00A0DC") state("active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#53a7c0")
state("inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#cccccc") state("inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#ffffff")
} }
main "motion" main "motion"

View File

@@ -147,8 +147,8 @@ private Map parseIasMessage(String description) {
ZoneStatus zs = zigbee.parseZoneStatus(description) ZoneStatus zs = zigbee.parseZoneStatus(description)
Map resultMap = [:] Map resultMap = [:]
resultMap.name = 'motion' result.name = 'motion'
resultMap.value = zs.isAlarm2Set() ? 'active' : 'inactive' result.value = zs.isAlarm2Set() ? 'active' : 'inactive'
log.debug(zs.isAlarm2Set() ? 'motion' : 'no motion') log.debug(zs.isAlarm2Set() ? 'motion' : 'no motion')
return resultMap return resultMap

View File

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

View File

@@ -1,41 +0,0 @@
# Nyce Door/Window Sensor (Open/Close Sensor)
Cloud Execution
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
Nyce Door/Window sensor with reporting interval of 5 min.
SmartThings platform will ping the device after `checkInterval` seconds of inactivity in last attempt to reach the device before marking it `OFFLINE`
* __12min__ checkInterval
## 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

@@ -19,20 +19,19 @@ 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,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 {
@@ -274,28 +273,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 +334,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

@@ -31,7 +31,7 @@ metadata {
tiles { tiles {
standardTile("button", "device.switch", width: 2, height: 2, canChangeIcon: true) { standardTile("button", "device.switch", width: 2, height: 2, canChangeIcon: true) {
state "off", label: 'Off', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff", nextState: "on" state "off", label: 'Off', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff", nextState: "on"
state "on", label: 'On', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#00A0DC", nextState: "off" state "on", label: 'On', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821", nextState: "off"
} }
main "button" main "button"
details "button" details "button"

View File

@@ -31,7 +31,7 @@ metadata {
// UI tile definitions // UI tile definitions
tiles { tiles {
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true, canChangeBackground: true) { standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true, canChangeBackground: true) {
state "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#00A0DC" state "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821"
state "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff" state "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff"
} }

View File

@@ -29,8 +29,8 @@ metadata {
// UI tile definitions // UI tile definitions
tiles { tiles {
standardTile("contact", "device.contact", width: 2, height: 2) { standardTile("contact", "device.contact", width: 2, height: 2) {
state "open", label: '${name}', icon: "st.contact.contact.open", backgroundColor: "#e86d13" state "open", label: '${name}', icon: "st.contact.contact.open", backgroundColor: "#ffa81e"
state "closed", label: '${name}', icon: "st.contact.contact.closed", backgroundColor: "#00A0DC" state "closed", label: '${name}', icon: "st.contact.contact.closed", backgroundColor: "#79b821"
} }
main "contact" main "contact"
@@ -40,11 +40,14 @@ metadata {
// Parse incoming device messages to generate events // Parse incoming device messages to generate events
def parse(String description) { def parse(String description) {
def resMap def name = null
if (description.startsWith("zone")) { def value = description
resMap = createEvent(name: "contact", value: zigbee.parseZoneStatus(description).isAlarm1Set() ? "open" : "closed") if (zigbee.isZoneType19(description)) {
name = "contact"
value = zigbee.translateStatusZoneType19(description) ? "open" : "closed"
} }
log.debug "Parse returned $resMap" def result = createEvent(name: name, value: value)
return resMap log.debug "Parse returned ${result?.descriptionText}"
return result
} }

View File

@@ -0,0 +1,384 @@
/*
Osram Lightify Gardenspot Mini RGB
Osram bulbs have a firmware issue causing it to forget its dimming level when turned off (via commands). Handling
that issue by using state variables
*/
metadata {
definition (name: "OSRAM LIGHTIFY Gardenspot mini RGB", namespace: "smartthings", author: "SmartThings") {
capability "Color Temperature"
capability "Actuator"
capability "Switch"
capability "Switch Level"
capability "Configuration"
capability "Polling"
capability "Refresh"
capability "Sensor"
capability "Color Control"
attribute "colorName", "string"
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 {
// status messages
status "on": "on/off: 1"
status "off": "on/off: 0"
// reply messages
reply "zcl on-off on": "on/off: 1"
reply "zcl on-off off": "on/off: 0"
}
// UI tile definitions
tiles {
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
state "on", label: '${name}', action: "switch.off", icon: "st.switches.light.on", backgroundColor: "#79b821"
state "off", label: '${name}', action: "switch.on", icon: "st.switches.light.off", backgroundColor: "#ffffff"
}
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") {
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
}
controlTile("rgbSelector", "device.color", "color", height: 3, width: 3, inactiveLabel: false) {
state "color", action:"setAdjustedColor"
}
valueTile("colorName", "device.colorName", inactiveLabel: false, decoration: "flat") {
state "colorName", label: '${currentValue}'
}
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}%'
}
main(["switch"])
details(["switch", "refresh", "colorName", "levelSliderControl", "level", "rgbSelector"])
}
}
// Parse incoming device messages to generate events
def parse(String description) {
//log.info "description is $description"
if (description?.startsWith("catchall:")) {
if(description?.endsWith("0100") ||description?.endsWith("1001") || description?.matches("on/off\\s*:\\s*1"))
{
def result = createEvent(name: "switch", value: "on")
log.debug "Parse returned ${result?.descriptionText}"
return result
}
else if(description?.endsWith("0000") || description?.endsWith("1000") || description?.matches("on/off\\s*:\\s*0"))
{
if(!(description?.startsWith("catchall: 0104 0300"))){
def result = createEvent(name: "switch", value: "off")
log.debug "Parse returned ${result?.descriptionText}"
return result
}
}
}
else if (description?.startsWith("read attr -")) {
def descMap = parseDescriptionAsMap(description)
log.trace "descMap : $descMap"
if (descMap.cluster == "0300") {
if(descMap.attrId == "0000"){ //Hue Attribute
def hueValue = Math.round(convertHexToInt(descMap.value) / 255 * 360)
log.debug "Hue value returned is $hueValue"
sendEvent(name: "hue", value: hueValue, displayed:false)
}
else if(descMap.attrId == "0001"){ //Saturation Attribute
def saturationValue = Math.round(convertHexToInt(descMap.value) / 255 * 100)
log.debug "Saturation from refresh is $saturationValue"
sendEvent(name: "saturation", value: saturationValue, displayed:false)
}
}
else if(descMap.cluster == "0008"){
def dimmerValue = Math.round(convertHexToInt(descMap.value) * 100 / 255)
log.debug "dimmer value is $dimmerValue"
sendEvent(name: "level", value: dimmerValue)
}
}
else {
def name = description?.startsWith("on/off: ") ? "switch" : null
def value = name == "switch" ? (description?.endsWith(" 1") ? "on" : "off") : null
def result = createEvent(name: name, value: value)
log.debug "Parse returned ${result?.descriptionText}"
return result
}
}
def on() {
log.debug "on()"
sendEvent(name: "switch", value: "on")
setLevel(state?.levelValue)
}
def zigbeeOff() {
"st cmd 0x${device.deviceNetworkId} ${endpointId} 6 0 {}"
}
def off() {
log.debug "off()"
sendEvent(name: "switch", value: "off")
zigbeeOff()
}
def refresh() {
[
"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} 0x0300 0", "delay 500",
"st rattr 0x${device.deviceNetworkId} ${endpointId} 0x0300 1"
]
}
def configure() {
state.levelValue = 100
log.debug "Configuring Reporting and Bindings."
def configCmds = [
//Switch Reporting
"zcl global send-me-a-report 6 0 0x10 0 3600 {01}", "delay 500",
"send 0x${device.deviceNetworkId} ${endpointId} 1", "delay 1000",
//Level Control Reporting
"zcl global send-me-a-report 8 0 0x20 5 3600 {0010}", "delay 200",
"send 0x${device.deviceNetworkId} ${endpointId} 1", "delay 1500",
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 6 {${device.zigbeeId}} {}", "delay 1000",
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 8 {${device.zigbeeId}} {}", "delay 500",
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 0x0300 {${device.zigbeeId}} {}", "delay 500"
]
return configCmds + refresh() // send refresh cmds as part of config
}
def parseDescriptionAsMap(description) {
(description - "read attr - ").split(",").inject([:]) { map, param ->
def nameAndValue = param.split(":")
map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
}
}
def poll(){
log.debug "Poll is calling refresh"
refresh()
}
def zigbeeSetLevel(level) {
"st cmd 0x${device.deviceNetworkId} ${endpointId} 8 4 {${level} 0000}"
}
def setLevel(value) {
state.levelValue = (value==null) ? 100 : value
log.trace "setLevel($value)"
def cmds = []
if (value == 0) {
sendEvent(name: "switch", value: "off")
cmds << zigbeeOff()
}
else if (device.latestValue("switch") == "off") {
sendEvent(name: "switch", value: "on")
}
sendEvent(name: "level", value: state.levelValue)
def level = hex(state.levelValue * 255 / 100)
cmds << zigbeeSetLevel(level)
//log.debug cmds
cmds
}
//input Hue Integer values; returns color name for saturation 100%
private getColorName(hueValue){
if(hueValue>360 || hueValue<0)
return
hueValue = Math.round(hueValue / 100 * 360)
log.debug "hue value is $hueValue"
def colorName = "Color Mode"
if(hueValue>=0 && hueValue <= 4){
colorName = "Red"
}
else if (hueValue>=5 && hueValue <=21 ){
colorName = "Brick Red"
}
else if (hueValue>=22 && hueValue <=30 ){
colorName = "Safety Orange"
}
else if (hueValue>=31 && hueValue <=40 ){
colorName = "Dark Orange"
}
else if (hueValue>=41 && hueValue <=49 ){
colorName = "Amber"
}
else if (hueValue>=50 && hueValue <=56 ){
colorName = "Gold"
}
else if (hueValue>=57 && hueValue <=65 ){
colorName = "Yellow"
}
else if (hueValue>=66 && hueValue <=83 ){
colorName = "Electric Lime"
}
else if (hueValue>=84 && hueValue <=93 ){
colorName = "Lawn Green"
}
else if (hueValue>=94 && hueValue <=112 ){
colorName = "Bright Green"
}
else if (hueValue>=113 && hueValue <=135 ){
colorName = "Lime"
}
else if (hueValue>=136 && hueValue <=166 ){
colorName = "Spring Green"
}
else if (hueValue>=167 && hueValue <=171 ){
colorName = "Turquoise"
}
else if (hueValue>=172 && hueValue <=187 ){
colorName = "Aqua"
}
else if (hueValue>=188 && hueValue <=203 ){
colorName = "Sky Blue"
}
else if (hueValue>=204 && hueValue <=217 ){
colorName = "Dodger Blue"
}
else if (hueValue>=218 && hueValue <=223 ){
colorName = "Navy Blue"
}
else if (hueValue>=224 && hueValue <=251 ){
colorName = "Blue"
}
else if (hueValue>=252 && hueValue <=256 ){
colorName = "Han Purple"
}
else if (hueValue>=257 && hueValue <=274 ){
colorName = "Electric Indigo"
}
else if (hueValue>=275 && hueValue <=289 ){
colorName = "Electric Purple"
}
else if (hueValue>=290 && hueValue <=300 ){
colorName = "Orchid Purple"
}
else if (hueValue>=301 && hueValue <=315 ){
colorName = "Magenta"
}
else if (hueValue>=316 && hueValue <=326 ){
colorName = "Hot Pink"
}
else if (hueValue>=327 && hueValue <=335 ){
colorName = "Deep Pink"
}
else if (hueValue>=336 && hueValue <=339 ){
colorName = "Raspberry"
}
else if (hueValue>=340 && hueValue <=352 ){
colorName = "Crimson"
}
else if (hueValue>=353 && hueValue <=360 ){
colorName = "Red"
}
colorName
}
private getEndpointId() {
new BigInteger(device.endpointId, 16).toString()
}
private hex(value, width=2) {
def s = new BigInteger(Math.round(value).toString()).toString(16)
while (s.size() < width) {
s = "0" + s
}
s
}
private evenHex(value){
def s = new BigInteger(Math.round(value).toString()).toString(16)
while (s.size() % 2 != 0) {
s = "0" + s
}
s
}
private String swapEndianHex(String hex) {
reverseArray(hex.decodeHex()).encodeHex()
}
private Integer convertHexToInt(hex) {
Integer.parseInt(hex,16)
}
//Need to reverse array of size 2
private byte[] reverseArray(byte[] array) {
byte tmp;
tmp = array[1];
array[1] = array[0];
array[0] = tmp;
return array
}
def setAdjustedColor(value) {
log.debug "setAdjustedColor: ${value}"
def adjusted = value + [:]
adjusted.level = null // needed because color picker always sends 100
setColor(adjusted)
}
def setColor(value){
log.trace "setColor($value)"
def max = 0xfe
if (value.hex) { sendEvent(name: "color", value: value.hex, displayed:false)}
def colorName = getColorName(value.hue)
sendEvent(name: "colorName", value: colorName)
log.debug "color name is : $colorName"
sendEvent(name: "hue", value: value.hue, displayed:false)
sendEvent(name: "saturation", value: value.saturation, displayed:false)
def scaledHueValue = evenHex(Math.round(value.hue * max / 100.0))
def scaledSatValue = evenHex(Math.round(value.saturation * max / 100.0))
def cmd = []
if (value.switch != "off" && device.latestValue("switch") == "off") {
cmd << "st cmd 0x${device.deviceNetworkId} ${endpointId} 6 1 {}"
cmd << "delay 150"
}
cmd << "st cmd 0x${device.deviceNetworkId} ${endpointId} 0x300 0x00 {${scaledHueValue} 00 0000}"
cmd << "delay 150"
cmd << "st cmd 0x${device.deviceNetworkId} ${endpointId} 0x300 0x03 {${scaledSatValue} 0000}"
if (value.level) {
state.levelValue = value.level
sendEvent(name: "level", value: value.level)
def level = hex(value.level * 255 / 100)
cmd << zigbeeSetLevel(level)
}
if (value.switch == "off") {
cmd << "delay 150"
cmd << off()
}
cmd
}

View File

@@ -0,0 +1,459 @@
/*
Osram Flex RGBW Light Strip
Osram bulbs have a firmware issue causing it to forget its dimming level when turned off (via commands). Handling
that issue by using state variables
*/
//DEPRECATED - Using the generic DTH for this device. Users need to be moved before deleting this DTH
metadata {
definition (name: "OSRAM LIGHTIFY LED Flexible Strip RGBW", namespace: "smartthings", author: "SmartThings") {
capability "Color Temperature"
capability "Actuator"
capability "Switch"
capability "Switch Level"
capability "Configuration"
capability "Polling"
capability "Refresh"
capability "Sensor"
capability "Color Control"
attribute "colorName", "string"
command "setAdjustedColor"
//fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0300,0B04,FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY Flex RGBW"
//fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0300,0B04,FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "Flex RGBW"
}
// simulator metadata
simulator {
// status messages
status "on": "on/off: 1"
status "off": "on/off: 0"
// reply messages
reply "zcl on-off on": "on/off: 1"
reply "zcl on-off off": "on/off: 0"
}
// UI tile definitions
tiles {
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
state "on", label: '${name}', action: "switch.off", icon: "st.switches.light.on", backgroundColor: "#79b821"
state "off", label: '${name}', action: "switch.on", icon: "st.switches.light.off", backgroundColor: "#ffffff"
}
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") {
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
}
controlTile("colorTempSliderControl", "device.colorTemperature", "slider", height: 1, width: 2, inactiveLabel: false, range:"(2700..6500)") {
state "colorTemperature", action:"color temperature.setColorTemperature"
}
valueTile("colorTemp", "device.colorTemperature", inactiveLabel: false, decoration: "flat") {
state "colorTemperature", label: '${currentValue} K'
}
valueTile("colorName", "device.colorName", inactiveLabel: false, decoration: "flat") {
state "colorName", label: '${currentValue}'
}
controlTile("rgbSelector", "device.color", "color", height: 3, width: 3, inactiveLabel: false) {
state "color", action:"setAdjustedColor"
}
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}%'
}
main(["switch"])
details(["switch", "refresh", "colorName", "levelSliderControl", "level", "colorTempSliderControl", "colorTemp", "rgbSelector"])
}
}
// Parse incoming device messages to generate events
def parse(String description) {
//log.info "description is $description"
if (description?.startsWith("catchall:")) {
if(description?.endsWith("0100") ||description?.endsWith("1001") || description?.matches("on/off\\s*:\\s*1"))
{
def result = createEvent(name: "switch", value: "on")
log.debug "Parse returned ${result?.descriptionText}"
return result
}
else if(description?.endsWith("0000") || description?.endsWith("1000") || description?.matches("on/off\\s*:\\s*0"))
{
if(!(description?.startsWith("catchall: 0104 0300"))){
def result = createEvent(name: "switch", value: "off")
log.debug "Parse returned ${result?.descriptionText}"
return result
}
}
}
else if (description?.startsWith("read attr -")) { //for values returned after hitting refresh
def descMap = parseDescriptionAsMap(description)
log.trace "descMap : $descMap"
if (descMap.cluster == "0300") {
if(descMap.attrId == "0007"){
log.debug "in read attr"
log.debug descMap.value
def tempInMired = convertHexToInt(descMap.value)
def tempInKelvin = Math.round(1000000/tempInMired)
log.trace "temp in kelvin: $tempInKelvin"
sendEvent(name: "colorTemperature", value: tempInKelvin, displayed:false)
}
else if(descMap.attrId == "0008"){ //Color mode attribute
if(descMap.value == "00"){
state.colorType = "rgb"
}else if(descMap.value == "02"){
state.colorType = "white"
}
}
else if(descMap.attrId == "0000"){ //Hue Attribute
def hueValue = Math.round(convertHexToInt(descMap.value) / 255 * 360)
log.debug "Hue value returned is $hueValue"
sendEvent(name: "hue", value: hueValue, displayed:false)
}
else if(descMap.attrId == "0001"){ //Saturation Attribute
def saturationValue = Math.round(convertHexToInt(descMap.value) / 255 * 100)
log.debug "Saturation from refresh is $saturationValue"
sendEvent(name: "saturation", value: saturationValue, displayed:false)
}
}
else if(descMap.cluster == "0008"){
def dimmerValue = Math.round(convertHexToInt(descMap.value) * 100 / 255)
log.debug "dimmer value is $dimmerValue"
sendEvent(name: "level", value: dimmerValue)
}
}
else {
def name = description?.startsWith("on/off: ") ? "switch" : null
def value = name == "switch" ? (description?.endsWith(" 1") ? "on" : "off") : null
def result = createEvent(name: name, value: value)
log.debug "description is $description"
return result
}
}
def on() {
log.debug "on()"
sendEvent(name: "switch", value: "on")
setLevel(state?.levelValue)
}
def zigbeeOff() {
"st cmd 0x${device.deviceNetworkId} ${endpointId} 6 0 {}"
}
def off() {
log.debug "off()"
sendEvent(name: "switch", value: "off")
zigbeeOff()
}
def refresh() {
[
"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} 0x0300 0", "delay 500",
"st rattr 0x${device.deviceNetworkId} ${endpointId} 0x0300 1", "delay 500",
"st rattr 0x${device.deviceNetworkId} ${endpointId} 0x0300 7", "delay 500",
"st rattr 0x${device.deviceNetworkId} ${endpointId} 0x0300 8"
]
}
def configure() {
state.levelValue = 100
state.colorType = "white"
log.debug "Configuring Reporting and Bindings."
def configCmds = [
//Switch Reporting
"zcl global send-me-a-report 6 0 0x10 0 3600 {01}", "delay 500",
"send 0x${device.deviceNetworkId} ${endpointId} 1", "delay 1000",
//Level Control Reporting
"zcl global send-me-a-report 8 0 0x20 5 3600 {0010}", "delay 200",
"send 0x${device.deviceNetworkId} ${endpointId} 1", "delay 1500",
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 6 {${device.zigbeeId}} {}", "delay 1000",
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 8 {${device.zigbeeId}} {}", "delay 500",
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 0x0300 {${device.zigbeeId}} {}", "delay 500"
]
return configCmds + refresh() // send refresh cmds as part of config
}
def setColorTemperature(value) {
state?.colorType = "white"
if(value<101){
value = (value*38) + 2700 //Calculation of mapping 0-100 to 2700-6500
}
def tempInMired = Math.round(1000000/value)
def finalHex = swapEndianHex(hex(tempInMired, 4))
def genericName = getGenericName(value)
log.debug "generic name is : $genericName"
def cmds = []
sendEvent(name: "colorTemperature", value: value, displayed:false)
sendEvent(name: "colorName", value: genericName)
cmds << "st cmd 0x${device.deviceNetworkId} ${endpointId} 0x0300 0x0a {${finalHex} 2000}"
cmds
}
def parseDescriptionAsMap(description) {
(description - "read attr - ").split(",").inject([:]) { map, param ->
def nameAndValue = param.split(":")
map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
}
}
def poll(){
log.debug "Poll is calling refresh"
refresh()
}
def zigbeeSetLevel(level) {
"st cmd 0x${device.deviceNetworkId} ${endpointId} 8 4 {${level} 0000}"
}
def setLevel(value) {
state.levelValue = (value==null) ? 100 : value
log.trace "setLevel($value)"
def cmds = []
if (value == 0) {
sendEvent(name: "switch", value: "off")
cmds << zigbeeOff()
}
else if (device.latestValue("switch") == "off") {
sendEvent(name: "switch", value: "on")
}
sendEvent(name: "level", value: state.levelValue)
def level = hex(state.levelValue * 255 / 100)
cmds << zigbeeSetLevel(level)
//log.debug cmds
cmds
}
//Naming based on the wiki article here: http://en.wikipedia.org/wiki/Color_temperature
private getGenericName(value){
def genericName = "White"
if(state?.colorType == "rgb"){
genericName = "Color Mode"
}
else{
if(value < 3300){
genericName = "Soft White"
} else if(value < 4150){
genericName = "Moonlight"
} else if(value < 5000){
genericName = "Cool White"
} else if(value <= 6500){
genericName = "Daylight"
}
}
genericName
}
//input Hue Integer values; returns color name for saturation 100%
private getColorName(hueValue){
if(hueValue>360 || hueValue<0)
return
hueValue = Math.round(hueValue / 100 * 360)
log.debug "hue value is $hueValue"
def colorName = "Color Mode"
if(hueValue>=0 && hueValue <= 4){
colorName = "Red"
}
else if (hueValue>=5 && hueValue <=21 ){
colorName = "Brick Red"
}
else if (hueValue>=22 && hueValue <=30 ){
colorName = "Safety Orange"
}
else if (hueValue>=31 && hueValue <=40 ){
colorName = "Dark Orange"
}
else if (hueValue>=41 && hueValue <=49 ){
colorName = "Amber"
}
else if (hueValue>=50 && hueValue <=56 ){
colorName = "Gold"
}
else if (hueValue>=57 && hueValue <=65 ){
colorName = "Yellow"
}
else if (hueValue>=66 && hueValue <=83 ){
colorName = "Electric Lime"
}
else if (hueValue>=84 && hueValue <=93 ){
colorName = "Lawn Green"
}
else if (hueValue>=94 && hueValue <=112 ){
colorName = "Bright Green"
}
else if (hueValue>=113 && hueValue <=135 ){
colorName = "Lime"
}
else if (hueValue>=136 && hueValue <=166 ){
colorName = "Spring Green"
}
else if (hueValue>=167 && hueValue <=171 ){
colorName = "Turquoise"
}
else if (hueValue>=172 && hueValue <=187 ){
colorName = "Aqua"
}
else if (hueValue>=188 && hueValue <=203 ){
colorName = "Sky Blue"
}
else if (hueValue>=204 && hueValue <=217 ){
colorName = "Dodger Blue"
}
else if (hueValue>=218 && hueValue <=223 ){
colorName = "Navy Blue"
}
else if (hueValue>=224 && hueValue <=251 ){
colorName = "Blue"
}
else if (hueValue>=252 && hueValue <=256 ){
colorName = "Han Purple"
}
else if (hueValue>=257 && hueValue <=274 ){
colorName = "Electric Indigo"
}
else if (hueValue>=275 && hueValue <=289 ){
colorName = "Electric Purple"
}
else if (hueValue>=290 && hueValue <=300 ){
colorName = "Orchid Purple"
}
else if (hueValue>=301 && hueValue <=315 ){
colorName = "Magenta"
}
else if (hueValue>=316 && hueValue <=326 ){
colorName = "Hot Pink"
}
else if (hueValue>=327 && hueValue <=335 ){
colorName = "Deep Pink"
}
else if (hueValue>=336 && hueValue <=339 ){
colorName = "Raspberry"
}
else if (hueValue>=340 && hueValue <=352 ){
colorName = "Crimson"
}
else if (hueValue>=353 && hueValue <=360 ){
colorName = "Red"
}
colorName
}
private getEndpointId() {
new BigInteger(device.endpointId, 16).toString()
}
private hex(value, width=2) {
def s = new BigInteger(Math.round(value).toString()).toString(16)
while (s.size() < width) {
s = "0" + s
}
s
}
private evenHex(value){
def s = new BigInteger(Math.round(value).toString()).toString(16)
while (s.size() % 2 != 0) {
s = "0" + s
}
s
}
private String swapEndianHex(String hex) {
reverseArray(hex.decodeHex()).encodeHex()
}
private Integer convertHexToInt(hex) {
Integer.parseInt(hex,16)
}
//Need to reverse array of size 2
private byte[] reverseArray(byte[] array) {
byte tmp;
tmp = array[1];
array[1] = array[0];
array[0] = tmp;
return array
}
def setAdjustedColor(value) {
log.debug "setAdjustedColor: ${value}"
def adjusted = value + [:]
adjusted.level = null // needed because color picker always sends 100
setColor(adjusted)
}
def setColor(value){
state?.colorType = "rgb"
log.trace "setColor($value)"
def max = 0xfe
if (value.hex) { sendEvent(name: "color", value: value.hex, displayed:false)}
def colorName = getColorName(value.hue)
log.debug "color name is : $colorName"
sendEvent(name: "colorName", value: colorName)
sendEvent(name: "colorTemperature", value: "--", displayed:false)
sendEvent(name: "hue", value: value.hue, displayed:false)
sendEvent(name: "saturation", value: value.saturation, displayed:false)
def scaledHueValue = evenHex(Math.round(value.hue * max / 100.0))
def scaledSatValue = evenHex(Math.round(value.saturation * max / 100.0))
def cmd = []
if (value.switch != "off" && device.latestValue("switch") == "off") {
cmd << "st cmd 0x${device.deviceNetworkId} ${endpointId} 6 1 {}"
cmd << "delay 150"
}
cmd << "st cmd 0x${device.deviceNetworkId} ${endpointId} 0x300 0x00 {${scaledHueValue} 00 0000}"
cmd << "delay 150"
cmd << "st cmd 0x${device.deviceNetworkId} ${endpointId} 0x300 0x03 {${scaledSatValue} 0000}"
if (value.level) {
state.levelValue = value.level
sendEvent(name: "level", value: value.level)
def level = hex(value.level * 255 / 100)
cmd << zigbeeSetLevel(level)
}
if (value.switch == "off") {
cmd << "delay 150"
cmd << off()
}
cmd
}

View File

@@ -0,0 +1,252 @@
/*
Osram Tunable White 60 A19 bulb
Osram bulbs have a firmware issue causing it to forget its dimming level when turned off (via commands). Handling
that issue by using state variables
*/
//DEPRECATED - Using the generic DTH for this device. Users need to be moved before deleting this DTH
metadata {
definition (name: "OSRAM LIGHTIFY LED Tunable White 60W", namespace: "smartthings", author: "SmartThings") {
capability "Color Temperature"
capability "Actuator"
capability "Switch"
capability "Switch Level"
capability "Configuration"
capability "Refresh"
capability "Sensor"
attribute "colorName", "string"
}
// simulator metadata
simulator {
// status messages
status "on": "on/off: 1"
status "off": "on/off: 0"
// reply messages
reply "zcl on-off on": "on/off: 1"
reply "zcl on-off off": "on/off: 0"
}
// UI tile definitions
tiles {
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
state "on", label: '${name}', action: "switch.off", icon: "st.switches.light.on", backgroundColor: "#79b821"
state "off", label: '${name}', action: "switch.on", icon: "st.switches.light.off", backgroundColor: "#ffffff"
}
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") {
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
}
controlTile("colorTempSliderControl", "device.colorTemperature", "slider", height: 1, width: 2, inactiveLabel: false, range:"(2700..6500)") {
state "colorTemperature", action:"color temperature.setColorTemperature"
}
valueTile("colorTemp", "device.colorTemperature", inactiveLabel: false, decoration: "flat") {
state "colorTemperature", label: '${currentValue} K'
}
valueTile("colorName", "device.colorName", inactiveLabel: false, decoration: "flat") {
state "colorName", label: '${currentValue}'
}
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}%'
}
main(["switch"])
details(["switch", "refresh", "colorName", "levelSliderControl", "level", "colorTempSliderControl", "colorTemp"])
}
}
// Parse incoming device messages to generate events
def parse(String description) {
//log.trace description
if (description?.startsWith("catchall:")) {
if(description?.endsWith("0100") ||description?.endsWith("1001") || description?.matches("on/off\\s*:\\s*1"))
{
def result = createEvent(name: "switch", value: "on")
log.debug "Parse returned ${result?.descriptionText}"
return result
}
else if(description?.endsWith("0000") || description?.endsWith("1000") || description?.matches("on/off\\s*:\\s*0"))
{
def result = createEvent(name: "switch", value: "off")
log.debug "Parse returned ${result?.descriptionText}"
return result
}
}
else if (description?.startsWith("read attr -")) {
def descMap = parseDescriptionAsMap(description)
log.trace "descMap : $descMap"
if (descMap.cluster == "0300") {
log.debug descMap.value
def tempInMired = convertHexToInt(descMap.value)
def tempInKelvin = Math.round(1000000/tempInMired)
log.trace "temp in kelvin: $tempInKelvin"
sendEvent(name: "colorTemperature", value: tempInKelvin, displayed:false)
}
else if(descMap.cluster == "0008"){
def dimmerValue = Math.round(convertHexToInt(descMap.value) * 100 / 255)
log.debug "dimmer value is $dimmerValue"
sendEvent(name: "level", value: dimmerValue)
}
}
else {
def name = description?.startsWith("on/off: ") ? "switch" : null
def value = name == "switch" ? (description?.endsWith(" 1") ? "on" : "off") : null
def result = createEvent(name: name, value: value)
log.debug "Parse returned ${result?.descriptionText}"
return result
}
}
def on() {
log.debug "on()"
sendEvent(name: "switch", value: "on")
setLevel(state?.levelValue)
}
def off() {
log.debug "off()"
sendEvent(name: "switch", value: "off")
"st cmd 0x${device.deviceNetworkId} ${endpointId} 6 0 {}"
}
def refresh() {
[
"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} 0x0300 7"
]
}
def configure() {
state.levelValue = 100
log.debug "Configuring Reporting and Bindings."
def configCmds = [
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 0x0300 {${device.zigbeeId}} {}", "delay 500"
]
return onOffConfig() + levelConfig() + configCmds + refresh() // send refresh cmds as part of config
}
def onOffConfig() {
[
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 6 {${device.zigbeeId}} {}", "delay 200",
"zcl global send-me-a-report 6 0 0x10 0 300 {01}",
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 1500"
]
}
//level config for devices with min reporting interval as 5 seconds and reporting interval if no activity as 1hour (3600s)
//min level change is 01
def levelConfig() {
[
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 8 {${device.zigbeeId}} {}", "delay 200",
"zcl global send-me-a-report 8 0 0x20 5 3600 {01}",
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 1500"
]
}
def setColorTemperature(value) {
if(value<101){
value = (value*38) + 2700 //Calculation of mapping 0-100 to 2700-6500
}
def tempInMired = Math.round(1000000/value)
def finalHex = swapEndianHex(hex(tempInMired, 4))
def genericName = getGenericName(value)
log.debug "generic name is : $genericName"
def cmds = []
sendEvent(name: "colorTemperature", value: value, displayed:false)
sendEvent(name: "colorName", value: genericName)
cmds << "st cmd 0x${device.deviceNetworkId} ${endpointId} 0x0300 0x0a {${finalHex} 2000}"
cmds
}
def parseDescriptionAsMap(description) {
(description - "read attr - ").split(",").inject([:]) { map, param ->
def nameAndValue = param.split(":")
map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
}
}
def setLevel(value) {
state.levelValue = (value==null) ? 100 : value
log.trace "setLevel($value)"
def cmds = []
if (value == 0) {
sendEvent(name: "switch", value: "off")
cmds << "st cmd 0x${device.deviceNetworkId} ${endpointId} 6 0 {}"
}
else if (device.latestValue("switch") == "off") {
sendEvent(name: "switch", value: "on")
}
sendEvent(name: "level", value: state.levelValue)
def level = hex(state.levelValue * 254 / 100)
cmds << "st cmd 0x${device.deviceNetworkId} ${endpointId} 8 4 {${level} 0000}"
//log.debug cmds
cmds
}
//Naming based on the wiki article here: http://en.wikipedia.org/wiki/Color_temperature
private getGenericName(value){
def genericName = "White"
if(value < 3300){
genericName = "Soft White"
} else if(value < 4150){
genericName = "Moonlight"
} else if(value < 5000){
genericName = "Cool White"
} else if(value <= 6500){
genericName = "Daylight"
}
genericName
}
private getEndpointId() {
new BigInteger(device.endpointId, 16).toString()
}
private hex(value, width=2) {
def s = new BigInteger(Math.round(value).toString()).toString(16)
while (s.size() < width) {
s = "0" + s
}
s
}
private String swapEndianHex(String hex) {
reverseArray(hex.decodeHex()).encodeHex()
}
private Integer convertHexToInt(hex) {
Integer.parseInt(hex,16)
}
//Need to reverse array of size 2
private byte[] reverseArray(byte[] array) {
byte tmp;
tmp = array[1];
array[1] = array[0];
array[0] = tmp;
return array
}

View File

@@ -37,9 +37,9 @@ 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:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#00a0dc", nextState:"turningOff" 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 "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:"#00a0dc", nextState:"turningOff" 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" state "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
} }
standardTile("reset", "device.reset", inactiveLabel: false, decoration: "flat") { standardTile("reset", "device.reset", inactiveLabel: false, decoration: "flat") {

View File

@@ -42,9 +42,9 @@ metadata {
tiles { tiles {
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) { standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
state "on", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#00A0DC", nextState:"turningOff" state "on", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#79b821", nextState:"turningOff"
state "off", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn" state "off", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn"
state "turningOn", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#00A0DC", nextState:"turningOff" state "turningOn", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#79b821", nextState:"turningOff"
state "turningOff", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn" state "turningOff", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn"
} }
controlTile("levelSliderControl", "device.level", "slider", height: 1, width: 3, inactiveLabel: false) { controlTile("levelSliderControl", "device.level", "slider", height: 1, width: 3, inactiveLabel: false) {

View File

@@ -24,7 +24,6 @@ metadata {
capability "Refresh" capability "Refresh"
capability "Actuator" capability "Actuator"
capability "Sensor" capability "Sensor"
capability "Outlet"
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0B04,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "4257050-ZHAC" fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0B04,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "4257050-ZHAC"
@@ -70,36 +69,283 @@ 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 finalResult = isKnownDescription(description)
if (!event) { if (finalResult != "false") {
log.warn "DID NOT PARSE MESSAGE for description : $description" log.info finalResult
log.debug zigbee.parseDescriptionAsMap(description) if (finalResult.type == "update") {
} else if (event.name == "power") { log.info "$device updates: ${finalResult.value}"
/* }
Dividing by 10 as the Divisor is 10000 and unit is kW for the device. Simplifying to 10 power level is an integer. else if (finalResult.type == "power") {
*/ def powerValue = (finalResult.value as Integer)/10
event.value = event.value / 10 sendEvent(name: "power", value: powerValue)
}
return event ? createEvent(event) : event /*
Dividing by 10 as the Divisor is 10000 and unit is kW for the device. AttrId: 0302 and 0300. Simplifying to 10
power level is an integer. The exact power level with correct units needs to be handled in the device type
to account for the different Divisor value (AttrId: 0302) and POWER Unit (AttrId: 0300). CLUSTER for simple metering is 0702
*/
}
else {
sendEvent(name: finalResult.type, value: finalResult.value)
}
}
else {
log.warn "DID NOT PARSE MESSAGE for description : $description"
log.debug parseDescriptionAsMap(description)
}
} }
def setLevel(value) { // Commands to device
zigbee.setLevel(value) def zigbeeCommand(cluster, attribute){
"st cmd 0x${device.deviceNetworkId} ${endpointId} ${cluster} ${attribute} {}"
} }
def off() { def off() {
zigbee.off() zigbeeCommand("6", "0")
} }
def on() { def on() {
zigbee.on() zigbeeCommand("6", "1")
}
def setLevel(value) {
value = value as Integer
if (value == 0) {
off()
}
else {
if (device.latestValue("switch") == "off") {
sendEvent(name: "switch", value: "on")
}
sendEvent(name: "level", value: value)
setLevelWithRate(value, "0000") //value is between 0 to 100
}
} }
def refresh() { def refresh() {
zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.electricMeasurementPowerRefresh() [
"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} 0x0B04 0x050B", "delay 500"
]
} }
def configure() { def configure() {
refresh() + zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.electricMeasurementPowerConfig() onOffConfig() + levelConfig() + powerConfig() + refresh()
}
private getEndpointId() {
new BigInteger(device.endpointId, 16).toString()
}
private hex(value, width=2) {
def s = new BigInteger(Math.round(value).toString()).toString(16)
while (s.size() < width) {
s = "0" + s
}
s
}
private String swapEndianHex(String hex) {
reverseArray(hex.decodeHex()).encodeHex()
}
private Integer convertHexToInt(hex) {
Integer.parseInt(hex,16)
}
//Need to reverse array of size 2
private byte[] reverseArray(byte[] array) {
byte tmp;
tmp = array[1];
array[1] = array[0];
array[0] = tmp;
return array
}
def parseDescriptionAsMap(description) {
if (description?.startsWith("read attr -")) {
(description - "read attr - ").split(",").inject([:]) { map, param ->
def nameAndValue = param.split(":")
map += [(nameAndValue[0].trim()): nameAndValue[1].trim()]
}
}
else if (description?.startsWith("catchall: ")) {
def seg = (description - "catchall: ").split(" ")
def zigbeeMap = [:]
zigbeeMap += [raw: (description - "catchall: ")]
zigbeeMap += [profileId: seg[0]]
zigbeeMap += [clusterId: seg[1]]
zigbeeMap += [sourceEndpoint: seg[2]]
zigbeeMap += [destinationEndpoint: seg[3]]
zigbeeMap += [options: seg[4]]
zigbeeMap += [messageType: seg[5]]
zigbeeMap += [dni: seg[6]]
zigbeeMap += [isClusterSpecific: Short.valueOf(seg[7], 16) != 0]
zigbeeMap += [isManufacturerSpecific: Short.valueOf(seg[8], 16) != 0]
zigbeeMap += [manufacturerId: seg[9]]
zigbeeMap += [command: seg[10]]
zigbeeMap += [direction: seg[11]]
zigbeeMap += [data: seg.size() > 12 ? seg[12].split("").findAll { it }.collate(2).collect {
it.join('')
} : []]
zigbeeMap
}
}
def isKnownDescription(description) {
if ((description?.startsWith("catchall:")) || (description?.startsWith("read attr -"))) {
def descMap = parseDescriptionAsMap(description)
if (descMap.cluster == "0006" || descMap.clusterId == "0006") {
isDescriptionOnOff(descMap)
}
else if (descMap.cluster == "0008" || descMap.clusterId == "0008"){
isDescriptionLevel(descMap)
}
else if (descMap.cluster == "0B04" || descMap.clusterId == "0B04"){
isDescriptionPower(descMap)
}
}
else if(description?.startsWith("on/off:")) {
def switchValue = description?.endsWith("1") ? "on" : "off"
return [type: "switch", value : switchValue]
}
else {
return "false"
}
}
def isDescriptionOnOff(descMap) {
def switchValue = "undefined"
if (descMap.cluster == "0006") { //cluster info from read attr
value = descMap.value
if (value == "01"){
switchValue = "on"
}
else if (value == "00"){
switchValue = "off"
}
}
else if (descMap.clusterId == "0006") {
//cluster info from catch all
//command 0B is Default response and the last two bytes are [on/off][success]. on/off=00, success=00
//command 01 is Read attr response. the last two bytes are [datatype][value]. boolean datatype=10; on/off value = 01/00
if ((descMap.command=="0B" && descMap.raw.endsWith("0100")) || (descMap.command=="01" && descMap.raw.endsWith("1001"))){
switchValue = "on"
}
else if ((descMap.command=="0B" && descMap.raw.endsWith("0000")) || (descMap.command=="01" && descMap.raw.endsWith("1000"))){
switchValue = "off"
}
else if(descMap.command=="07"){
return [type: "update", value : "switch (0006) capability configured successfully"]
}
}
if (switchValue != "undefined"){
return [type: "switch", value : switchValue]
}
else {
return "false"
}
}
//@return - false or "success" or level [0-100]
def isDescriptionLevel(descMap) {
def dimmerValue = -1
if (descMap.cluster == "0008"){
//TODO: the message returned with catchall is command 0B with clusterId 0008. That is just a confirmation message
def value = convertHexToInt(descMap.value)
dimmerValue = Math.round(value * 100 / 255)
if(dimmerValue==0 && value > 0) {
dimmerValue = 1 //handling for non-zero hex value less than 3
}
}
else if(descMap.clusterId == "0008") {
if(descMap.command=="0B"){
return [type: "update", value : "level updated successfully"] //device updating the level change was successful. no value sent.
}
else if(descMap.command=="07"){
return [type: "update", value : "level (0008) capability configured successfully"]
}
}
if (dimmerValue != -1){
return [type: "level", value : dimmerValue]
}
else {
return "false"
}
}
def isDescriptionPower(descMap) {
def powerValue = "undefined"
if (descMap.cluster == "0B04") {
if (descMap.attrId == "050b") {
if(descMap.value!="ffff")
powerValue = convertHexToInt(descMap.value)
}
}
else if (descMap.clusterId == "0B04") {
if(descMap.command=="07"){
return [type: "update", value : "power (0B04) capability configured successfully"]
}
}
if (powerValue != "undefined"){
return [type: "power", value : powerValue]
}
else {
return "false"
}
}
def onOffConfig() {
[
"zdo bind 0x${device.deviceNetworkId} 1 ${endpointId} 6 {${device.zigbeeId}} {}", "delay 200",
"zcl global send-me-a-report 6 0 0x10 0 600 {01}",
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 1500"
]
}
//level config for devices with min reporting interval as 5 seconds and reporting interval if no activity as 1hour (3600s)
//min level change is 01
def levelConfig() {
[
"zdo bind 0x${device.deviceNetworkId} 1 ${endpointId} 8 {${device.zigbeeId}} {}", "delay 200",
"zcl global send-me-a-report 8 0 0x20 5 3600 {01}",
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 1500"
]
}
//power config for devices with min reporting interval as 1 seconds and reporting interval if no activity as 10min (600s)
//min change in value is 05
def powerConfig() {
[
"zdo bind 0x${device.deviceNetworkId} 1 ${endpointId} 0x0B04 {${device.zigbeeId}} {}", "delay 200",
"zcl global send-me-a-report 0x0B04 0x050B 0x29 1 600 {05 00}", //The send-me-a-report is custom to the attribute type for CentraLite
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500"
]
}
def setLevelWithRate(level, rate) {
if(rate == null){
rate = "0000"
}
level = convertToHexString(level * 255 / 100) //Converting the 0-100 range to 0-FF range in hex
"st cmd 0x${device.deviceNetworkId} ${endpointId} 8 4 {$level $rate}"
}
String convertToHexString(value, width=2) {
def s = new BigInteger(Math.round(value).toString()).toString(16)
while (s.size() < width) {
s = "0" + s
}
s
} }

View File

@@ -4,7 +4,6 @@ metadata {
capability "Actuator" capability "Actuator"
capability "Switch" capability "Switch"
capability "Sensor" capability "Sensor"
capability "Outlet"
fingerprint profileId: "0104", inClusters: "0006, 0004, 0003, 0000, 0005", outClusters: "0019", manufacturer: "Compacta International, Ltd", model: "ZBMPlug15", deviceJoinName: "SmartPower Outlet V1" fingerprint profileId: "0104", inClusters: "0006, 0004, 0003, 0000, 0005", outClusters: "0019", manufacturer: "Compacta International, Ltd", model: "ZBMPlug15", deviceJoinName: "SmartPower Outlet V1"
} }
@@ -24,7 +23,7 @@ metadata {
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 "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff" attributeState "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff"
attributeState "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#00A0DC" attributeState "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821"
} }
} }
main "switch" main "switch"
@@ -48,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 2000'
]
} }
def off() { def off() {
[ 'zcl on-off off'
'zcl on-off off',
'delay 200',
"send 0x${zigbee.deviceNetworkId} 0x01 0x${zigbee.endpointId}",
'delay 2000'
]
} }

View File

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

View File

@@ -1,39 +0,0 @@
# SmartPower Outlet
Local Execution on V2 Hubs
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
SmartPower outlet with reporting interval of 5 mins
SmartThings platform will ping the device after `checkInterval` seconds of inactivity in last attempt to reach the device before marking it `OFFLINE`
* V1, TV, HubV2 AppEngine < 1.5.1 - __21min__ checkInterval
* HubV2 AppEngine 1.5.1 - __12min__ checkInterval
## 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

@@ -13,9 +13,10 @@
* License for the specific language governing permissions and limitations * License for the specific language governing permissions and limitations
* under the License. * under the License.
*/ */
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", category: "C1") {
capability "Actuator" capability "Actuator"
capability "Switch" capability "Switch"
capability "Power Meter" capability "Power Meter"
@@ -23,12 +24,10 @@ metadata {
capability "Refresh" capability "Refresh"
capability "Sensor" capability "Sensor"
capability "Health Check" capability "Health Check"
capability "Light"
capability "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", 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"
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0B04,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "4257050-RZHAC", deviceJoinName: "Outlet" fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0B04,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "4257050-RZHAC", deviceJoinName: "Outlet"
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0B04,0B05", outClusters: "0019" fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0B04,0B05", outClusters: "0019"
} }
@@ -46,32 +45,32 @@ metadata {
preferences { preferences {
section { section {
image(name: 'educationalcontent', multiple: true, images: [ image(name: 'educationalcontent', multiple: true, images: [
"http://cdn.device-gse.smartthings.com/Outlet/US/OutletUS1.jpg", "http://cdn.device-gse.smartthings.com/Outlet/US/OutletUS1.jpg",
"http://cdn.device-gse.smartthings.com/Outlet/US/OutletUS2.jpg" "http://cdn.device-gse.smartthings.com/Outlet/US/OutletUS2.jpg"
]) ])
} }
} }
// UI tile definitions // UI tile definitions
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: "#00A0DC", nextState: "turningOff" attributeState "on", label: 'On', 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: 'Off', 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: "#00A0DC", nextState: "turningOff" attributeState "turningOn", label: 'Turning On', 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: 'Turning Off', 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'
} }
} }
standardTile("refresh", "device.power", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { standardTile("refresh", "device.power", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "default", label: '', action: "refresh.refresh", icon: "st.secondary.refresh" state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
} }
main "switch" main "switch"
details(["switch", "refresh"]) details(["switch","refresh"])
} }
} }
@@ -79,33 +78,41 @@ 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 finalResult = zigbee.getKnownDescription(description)
if (event) { //TODO: Remove this after getKnownDescription can parse it automatically
if (event.name == "power") { if (!finalResult && description!="updated")
def value = (event.value as Integer) / 10 finalResult = getPowerDescription(zigbee.parseDescriptionAsMap(description))
event = createEvent(name: event.name, value: value, descriptionText: '{{ device.displayName }} power is {{ value }} Watts', translatable: true)
} else if (event.name == "switch") { if (finalResult) {
def descriptionText = event.value == "on" ? '{{ device.displayName }} is On' : '{{ device.displayName }} is Off' log.info "final result = $finalResult"
event = createEvent(name: event.name, value: event.value, descriptionText: descriptionText, translatable: true) if (finalResult.type == "update") {
log.info "$device updates: ${finalResult.value}"
} }
} else { else if (finalResult.type == "power") {
def cluster = zigbee.parse(description) def powerValue = (finalResult.value as Integer)/10
sendEvent(name: "power", value: powerValue, descriptionText: '{{ device.displayName }} power is {{ value }} Watts', translatable: true )
if (cluster && cluster.clusterId == 0x0006 && cluster.command == 0x07) { /*
if (cluster.data[0] == 0x00) { Dividing by 10 as the Divisor is 10000 and unit is kW for the device. AttrId: 0302 and 0300. Simplifying to 10
log.debug "ON/OFF REPORTING CONFIG RESPONSE: " + cluster power level is an integer. The exact power level with correct units needs to be handled in the device type
event = createEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) to account for the different Divisor value (AttrId: 0302) and POWER Unit (AttrId: 0300). CLUSTER for simple metering is 0702
} else { */
log.warn "ON/OFF REPORTING CONFIG FAILED- error code:${cluster.data[0]}" }
event = null else {
def descriptionText = finalResult.value == "on" ? '{{ device.displayName }} is On' : '{{ device.displayName }} is Off'
sendEvent(name: finalResult.type, value: finalResult.value, descriptionText: descriptionText, translatable: true)
// Temporary fix for the case when Device is OFFLINE and is connected again
if (state.lastOnOff == null){
state.lastOnOff = now()
sendEvent(name: "deviceWatch-lastActivity", value: state.lastOnOff, description: "Last Activity is on ${new Date(state.lastOnOff)}", displayed: false, isStateChange: true)
} }
} else { state.lastOnOff = now()
log.warn "DID NOT PARSE MESSAGE for description : $description"
log.debug "${cluster}"
} }
} }
return event ? createEvent(event) : event else {
log.warn "DID NOT PARSE MESSAGE for description : $description"
log.debug zigbee.parseDescriptionAsMap(description)
}
} }
def off() { def off() {
@@ -116,10 +123,19 @@ def on() {
zigbee.on() zigbee.on()
} }
/** /**
* PING is used by Device-Watch in attempt to reach the Device * PING is used by Device-Watch in attempt to reach the Outlet
* */ * */
def ping() { def ping() {
return zigbee.onOffRefresh()
// send read attribute onOFF if the last time we heard from the outlet is outside of the checkInterval
if (state.lastOnOff < (now() - (1000 * device.currentValue("checkInterval"))) ){
log.info "ping, alive=no, lastOnOff=${new Date(state.lastOnOff)}"
state.lastOnOff = null
return zigbee.onOffRefresh()
} else { // if the last onOff activity is within the checkInterval we artificially create a Device-Watch event
log.info "ping, alive=yes, lastOnOff=${new Date(state.lastOnOff)}"
sendEvent(name: "deviceWatch-lastActivity", value: state.lastOnOff, description: "Last Activity is on ${new Date(state.lastOnOff)}", displayed: false, isStateChange: true)
}
} }
def refresh() { def refresh() {
@@ -127,11 +143,43 @@ def refresh() {
} }
def configure() { def configure() {
// Device-Watch allows 2 check-in misses from device + ping (plus 1 min lag time) sendEvent(name: "checkInterval", value: 1200, displayed: false)
// enrolls with default periodic reporting until newer 5 min interval is confirmed zigbee.onOffConfig() + powerConfig() + refresh()
sendEvent(name: "checkInterval", value: 2 * 10 * 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) + zigbee.electricMeasurementPowerConfig()
} }
//power config for devices with min reporting interval as 1 seconds and reporting interval if no activity as 10min (600s)
//min change in value is 01
def powerConfig() {
[
"zdo bind 0x${device.deviceNetworkId} 1 ${endpointId} 0x0B04 {${device.zigbeeId}} {}", "delay 200",
"zcl global send-me-a-report 0x0B04 0x050B 0x29 1 600 {05 00}", //The send-me-a-report is custom to the attribute type for CentraLite
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500"
]
}
private getEndpointId() {
new BigInteger(device.endpointId, 16).toString()
}
//TODO: Remove this after getKnownDescription can parse it automatically
def getPowerDescription(descMap) {
def powerValue = "undefined"
if (descMap.cluster == "0B04") {
if (descMap.attrId == "050b") {
if(descMap.value!="ffff")
powerValue = zigbee.convertHexToInt(descMap.value)
}
}
else if (descMap.clusterId == "0B04") {
if(descMap.command=="07"){
return [type: "update", value : "power (0B04) capability configured successfully"]
}
}
if (powerValue != "undefined"){
return [type: "power", value : powerValue]
}
else {
return [:]
}
}

View File

@@ -50,18 +50,18 @@ metadata {
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 "closed", label:'${name}', icon:"st.doors.garage.garage-closed", backgroundColor:"#00A0DC", nextState:"opening" attributeState "closed", label:'${name}', icon:"st.doors.garage.garage-closed", backgroundColor:"#79b821", nextState:"opening"
attributeState "open", label:'${name}', icon:"st.doors.garage.garage-open", backgroundColor:"#e86d13", nextState:"closing" attributeState "open", label:'${name}', icon:"st.doors.garage.garage-open", backgroundColor:"#ffa81e", nextState:"closing"
attributeState "opening", label:'${name}', icon:"st.doors.garage.garage-opening", backgroundColor:"#e86d13" attributeState "opening", label:'${name}', icon:"st.doors.garage.garage-opening", backgroundColor:"#ffe71e"
attributeState "closing", label:'${name}', icon:"st.doors.garage.garage-closing", backgroundColor:"#00A0DC" attributeState "closing", label:'${name}', icon:"st.doors.garage.garage-closing", backgroundColor:"#ffe71e"
} }
} }
standardTile("contact", "device.contact", width: 2, height: 2) { standardTile("contact", "device.contact", width: 2, height: 2) {
state("open", label:'${name}', icon:"st.contact.contact.open", backgroundColor:"#e86d13") state("open", label:'${name}', icon:"st.contact.contact.open", backgroundColor:"#ffa81e")
state("closed", label:'${name}', icon:"st.contact.contact.closed", backgroundColor:"#00A0DC") state("closed", label:'${name}', icon:"st.contact.contact.closed", backgroundColor:"#79b821")
} }
standardTile("acceleration", "device.acceleration", decoration: "flat", width: 2, height: 2) { standardTile("acceleration", "device.acceleration", decoration: "flat", width: 2, height: 2) {
state("active", label:'${name}', icon:"st.motion.acceleration.active", backgroundColor:"#00A0DC") state("active", label:'${name}', icon:"st.motion.acceleration.active", backgroundColor:"#53a7c0")
state("inactive", label:'${name}', icon:"st.motion.acceleration.inactive", backgroundColor:"#ffffff") state("inactive", label:'${name}', icon:"st.motion.acceleration.inactive", backgroundColor:"#ffffff")
} }
valueTile("temperature", "device.temperature", decoration: "flat", width: 2, height: 2) { valueTile("temperature", "device.temperature", decoration: "flat", width: 2, height: 2) {
@@ -86,9 +86,9 @@ metadata {
def parse(String description) { def parse(String description) {
log.debug "parse($description)" log.debug "parse($description)"
def results = [:] def results = null
if (!isSupportedDescription(description) || description.startsWith("zone")) { if (!isSupportedDescription(description) || zigbee.isZoneType19(description)) {
// Ignore this in favor of orientation-based state // Ignore this in favor of orientation-based state
// results = parseSingleMessage(description) // results = parseSingleMessage(description)
} }

View File

@@ -50,17 +50,17 @@ metadata {
tiles { tiles {
standardTile("status", "device.status", width: 2, height: 2) { standardTile("status", "device.status", width: 2, height: 2) {
state("closed", label:'${name}', icon:"st.doors.garage.garage-closed", action: "actuate", backgroundColor:"#00A0DC", nextState:"opening") state("closed", label:'${name}', icon:"st.doors.garage.garage-closed", action: "actuate", backgroundColor:"#79b821", nextState:"opening")
state("open", label:'${name}', icon:"st.doors.garage.garage-open", action: "actuate", backgroundColor:"#e86d13", nextState:"closing") state("open", label:'${name}', icon:"st.doors.garage.garage-open", action: "actuate", backgroundColor:"#ffa81e", nextState:"closing")
state("opening", label:'${name}', icon:"st.doors.garage.garage-opening", backgroundColor:"#e86d13") state("opening", label:'${name}', icon:"st.doors.garage.garage-opening", backgroundColor:"#ffe71e")
state("closing", label:'${name}', icon:"st.doors.garage.garage-closing", backgroundColor:"#00A0DC") state("closing", label:'${name}', icon:"st.doors.garage.garage-closing", backgroundColor:"#ffe71e")
} }
standardTile("contact", "device.contact") { standardTile("contact", "device.contact") {
state("open", label:'${name}', icon:"st.contact.contact.open", backgroundColor:"#ffa81e") state("open", label:'${name}', icon:"st.contact.contact.open", backgroundColor:"#ffa81e")
state("closed", label:'${name}', icon:"st.contact.contact.closed", backgroundColor:"#79b821") state("closed", label:'${name}', icon:"st.contact.contact.closed", backgroundColor:"#79b821")
} }
standardTile("acceleration", "device.acceleration", decoration: "flat") { standardTile("acceleration", "device.acceleration", decoration: "flat") {
state("active", label:'${name}', icon:"st.motion.acceleration.active", backgroundColor:"#00A0DC") state("active", label:'${name}', icon:"st.motion.acceleration.active", backgroundColor:"#53a7c0")
state("inactive", label:'${name}', icon:"st.motion.acceleration.inactive", backgroundColor:"#ffffff") state("inactive", label:'${name}', icon:"st.motion.acceleration.inactive", backgroundColor:"#ffffff")
} }
valueTile("temperature", "device.temperature", decoration: "flat") { valueTile("temperature", "device.temperature", decoration: "flat") {
@@ -117,7 +117,7 @@ def parse(String description) {
log.debug "parse($description)" log.debug "parse($description)"
def results = null def results = null
if (!isSupportedDescription(description) || description.startsWith("zone")) { if (!isSupportedDescription(description) || zigbee.isZoneType19(description)) {
// Ignore this in favor of orientation-based state // Ignore this in favor of orientation-based state
// results = parseSingleMessage(description) // results = parseSingleMessage(description)
} }

View File

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

View File

@@ -1,45 +0,0 @@
# Smartsense Moisture Sensor
Local Execution on V2 Hubs
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
SmartSense Moisture sensor with reporting interval of 5 mins.
SmartThings platform will ping the device after `checkInterval` seconds of inactivity in last attempt to reach the device before marking it `OFFLINE`
* V1, TV, HubV2 AppEngine < 1.5.1 - __121min__ checkInterval
* HubV2 AppEngine 1.5.1 - __12min__ checkInterval
## 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

@@ -17,7 +17,7 @@ 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", category: "C2") {
capability "Configuration" capability "Configuration"
capability "Battery" capability "Battery"
capability "Refresh" capability "Refresh"
@@ -29,10 +29,9 @@ metadata {
command "enrollResponse" command "enrollResponse"
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3315-S", deviceJoinName: "Water Leak Sensor" fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3315-S", deviceJoinName: "Water Leak Sensor"
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3315" fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3315"
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3315-Seu", deviceJoinName: "Water Leak Sensor" fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3315-Seu", deviceJoinName: "Water Leak Sensor"
fingerprint inClusters: "0000,0001,0003,0020,0402,0500,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3315-L", deviceJoinName: "Iris Smart Water Sensor"
fingerprint inClusters: "0000,0001,0003,000F,0020,0402,0500", outClusters: "0019", manufacturer: "SmartThings", model: "moisturev4", deviceJoinName: "Water Leak Sensor" fingerprint inClusters: "0000,0001,0003,000F,0020,0402,0500", outClusters: "0019", manufacturer: "SmartThings", model: "moisturev4", deviceJoinName: "Water Leak Sensor"
} }
@@ -43,10 +42,10 @@ metadata {
preferences { preferences {
section { section {
image(name: 'educationalcontent', multiple: true, images: [ image(name: 'educationalcontent', multiple: true, images: [
"http://cdn.device-gse.smartthings.com/Moisture/Moisture1.png", "http://cdn.device-gse.smartthings.com/Moisture/Moisture1.png",
"http://cdn.device-gse.smartthings.com/Moisture/Moisture2.png", "http://cdn.device-gse.smartthings.com/Moisture/Moisture2.png",
"http://cdn.device-gse.smartthings.com/Moisture/Moisture3.png" "http://cdn.device-gse.smartthings.com/Moisture/Moisture3.png"
]) ])
} }
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"
@@ -55,32 +54,32 @@ metadata {
} }
tiles(scale: 2) { tiles(scale: 2) {
multiAttributeTile(name: "water", type: "generic", width: 6, height: 4) { multiAttributeTile(name:"water", type: "generic", width: 6, height: 4){
tileAttribute("device.water", key: "PRIMARY_CONTROL") { tileAttribute ("device.water", key: "PRIMARY_CONTROL") {
attributeState "dry", label: "Dry", icon: "st.alarm.water.dry", backgroundColor: "#ffffff" attributeState "dry", label: "Dry", icon:"st.alarm.water.dry", backgroundColor:"#ffffff"
attributeState "wet", label: "Wet", icon: "st.alarm.water.wet", backgroundColor: "#53a7c0" attributeState "wet", label: "Wet", icon:"st.alarm.water.wet", backgroundColor:"#53a7c0"
} }
} }
valueTile("temperature", "device.temperature", inactiveLabel: false, width: 2, height: 2) { valueTile("temperature", "device.temperature", inactiveLabel: false, width: 2, height: 2) {
state "temperature", label: '${currentValue}°', state "temperature", label:'${currentValue}°',
backgroundColors: [ backgroundColors:[
[value: 31, color: "#153591"], [value: 31, color: "#153591"],
[value: 44, color: "#1e9cbb"], [value: 44, color: "#1e9cbb"],
[value: 59, color: "#90d2a7"], [value: 59, color: "#90d2a7"],
[value: 74, color: "#44b621"], [value: 74, color: "#44b621"],
[value: 84, color: "#f1d801"], [value: 84, color: "#f1d801"],
[value: 95, color: "#d04e00"], [value: 95, color: "#d04e00"],
[value: 96, color: "#bc2323"] [value: 96, color: "#bc2323"]
] ]
} }
valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false, width: 2, height: 2) { valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false, width: 2, height: 2) {
state "battery", label: '${currentValue}% battery', unit: "" state "battery", label:'${currentValue}% battery', unit:""
} }
standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "default", action: "refresh.refresh", icon: "st.secondary.refresh" state "default", action:"refresh.refresh", icon:"st.secondary.refresh"
} }
main(["water", "temperature"]) main (["water", "temperature"])
details(["water", "temperature", "battery", "refresh"]) details(["water", "temperature", "battery", "refresh"])
} }
} }
@@ -88,119 +87,255 @@ metadata {
def parse(String description) { def parse(String description) {
log.debug "description: $description" log.debug "description: $description"
// getEvent will handle temperature and humidity Map map = [:]
Map map = zigbee.getEvent(description) if (description?.startsWith('catchall:')) {
if (!map) { map = parseCatchAllMessage(description)
if (description?.startsWith('zone status')) { }
map = parseIasMessage(description) else if (description?.startsWith('read attr -')) {
} else { map = parseReportAttributeMessage(description)
Map descMap = zigbee.parseDescriptionAsMap(description) }
if (descMap.clusterInt == 0x0001 && descMap.commandInt != 0x07 && descMap?.value) { else if (description?.startsWith('temperature: ')) {
map = getBatteryResult(Integer.parseInt(descMap.value, 16)) map = parseCustomMessage(description)
} else if (descMap?.clusterInt == zigbee.TEMPERATURE_MEASUREMENT_CLUSTER && descMap.commandInt == 0x07) { }
if (descMap.data[0] == "00") { else if (description?.startsWith('zone status')) {
log.debug "TEMP REPORTING CONFIG RESPONSE: $descMap" map = parseIasMessage(description)
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
} else {
log.warn "TEMP REPORTING CONFIG FAILED- error code: ${descMap.data[0]}"
}
}
}
} }
log.debug "Parse returned $map" log.debug "Parse returned $map"
def result = map ? createEvent(map) : [:] def result = map ? createEvent(map) : null
if (description?.startsWith('enroll request')) { if (description?.startsWith('enroll request')) {
List cmds = zigbee.enrollResponse() List cmds = enrollResponse()
log.debug "enroll response: ${cmds}" log.debug "enroll response: ${cmds}"
result = cmds?.collect { new physicalgraph.device.HubAction(it) } result = cmds?.collect { new physicalgraph.device.HubAction(it) }
} }
return result return result
} }
private Map parseCatchAllMessage(String description) {
Map resultMap = [:]
def cluster = zigbee.parse(description)
if (shouldProcessMessage(cluster)) {
switch(cluster.clusterId) {
case 0x0001:
resultMap = getBatteryResult(cluster.data.last())
break
case 0x0402:
// temp is last 2 data values. reverse to swap endian
String temp = cluster.data[-2..-1].reverse().collect { cluster.hex1(it) }.join()
def value = getTemperature(temp)
resultMap = getTemperatureResult(value)
break
}
}
return resultMap
}
private boolean shouldProcessMessage(cluster) {
// 0x0B is default response indicating message got through
// 0x07 is bind message
boolean ignoredMessage = cluster.profileId != 0x0104 ||
cluster.command == 0x0B ||
cluster.command == 0x07 ||
(cluster.data.size() > 0 && cluster.data.first() == 0x3e)
return !ignoredMessage
}
private Map parseReportAttributeMessage(String description) {
Map descMap = (description - "read attr - ").split(",").inject([:]) { map, param ->
def nameAndValue = param.split(":")
map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
}
log.debug "Desc Map: $descMap"
Map resultMap = [:]
if (descMap.cluster == "0402" && descMap.attrId == "0000") {
def value = getTemperature(descMap.value)
resultMap = getTemperatureResult(value)
}
else if (descMap.cluster == "0001" && descMap.attrId == "0020") {
resultMap = getBatteryResult(Integer.parseInt(descMap.value, 16))
}
return resultMap
}
private Map parseCustomMessage(String description) {
Map resultMap = [:]
if (description?.startsWith('temperature: ')) {
def value = zigbee.parseHATemperatureValue(description, "temperature: ", getTemperatureScale())
resultMap = getTemperatureResult(value)
}
return resultMap
}
private Map parseIasMessage(String description) { private Map parseIasMessage(String description) {
ZoneStatus zs = zigbee.parseZoneStatus(description) ZoneStatus zs = zigbee.parseZoneStatus(description)
return zs.isAlarm1Set() ? getMoistureResult('wet') : getMoistureResult('dry') return zs.isAlarm1Set() ? getMoistureResult('wet') : getMoistureResult('dry')
} }
def getTemperature(value) {
def celsius = Integer.parseInt(value, 16).shortValue() / 100
if(getTemperatureScale() == "C"){
return celsius
} else {
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 linkText = getLinkText(device)
def result = [:] def result = [
name: 'battery',
value: '--',
translatable: true
]
def volts = rawValue / 10 def volts = rawValue / 10
if (!(rawValue == 0 || rawValue == 255)) { if (rawValue == 0 || rawValue == 255) {}
result.name = 'battery' else {
result.translatable = true if (volts > 3.5) {
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%" result.descriptionText = "{{ device.displayName }} battery has too much power: (> 3.5) volts."
if (device.getDataValue("manufacturer") == "SmartThings") {
volts = rawValue // For the batteryMap to work the key needs to be an int
def batteryMap = [28: 100, 27: 100, 26: 100, 25: 90, 24: 90, 23: 70,
22: 70, 21: 50, 20: 50, 19: 30, 18: 30, 17: 15, 16: 1, 15: 0]
def minVolts = 15
def maxVolts = 28
if (volts < minVolts)
volts = minVolts
else if (volts > maxVolts)
volts = maxVolts
def pct = batteryMap[volts]
result.value = pct
} else {
def minVolts = 2.1
def maxVolts = 3.0
def pct = (volts - minVolts) / (maxVolts - minVolts)
def roundedPct = Math.round(pct * 100)
if (roundedPct <= 0)
roundedPct = 1
result.value = Math.min(100, roundedPct)
} }
else {
if (device.getDataValue("manufacturer") == "SmartThings") {
volts = rawValue // For the batteryMap to work the key needs to be an int
def batteryMap = [28:100, 27:100, 26:100, 25:90, 24:90, 23:70,
22:70, 21:50, 20:50, 19:30, 18:30, 17:15, 16:1, 15:0]
def minVolts = 15
def maxVolts = 28
if (volts < minVolts)
volts = minVolts
else if (volts > maxVolts)
volts = maxVolts
def pct = batteryMap[volts]
if (pct != null) {
result.value = pct
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%"
}
}
else {
def minVolts = 2.1
def maxVolts = 3.0
def pct = (volts - minVolts) / (maxVolts - minVolts)
def roundedPct = Math.round(pct * 100)
result.value = Math.min(100, roundedPct)
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%"
}
}
} }
return result return result
} }
private Map getMoistureResult(value) { private Map getTemperatureResult(value) {
log.debug "water" log.debug 'TEMP'
def descriptionText if (tempOffset) {
if (value == "wet") def offset = tempOffset as int
descriptionText = '{{ device.displayName }} is wet' def v = value as int
else value = v + offset
descriptionText = '{{ device.displayName }} is dry' }
def descriptionText
if ( temperatureScale == 'C' )
descriptionText = '{{ device.displayName }} was {{ value }}°C'
else
descriptionText = '{{ device.displayName }} was {{ value }}°F'
return [ return [
name : 'water', name: 'temperature',
value : value, value: value,
descriptionText: descriptionText, descriptionText: descriptionText,
translatable : true translatable: true
] ]
} }
/** private Map getMoistureResult(value) {
* PING is used by Device-Watch in attempt to reach the Device log.debug "water"
* */ def descriptionText
def ping() { if ( value == "wet" )
return zigbee.readAttribute(0x001, 0x0020) // Read the Battery Level descriptionText = '{{ device.displayName }} is wet'
else
descriptionText = '{{ device.displayName }} is dry'
return [
name: 'water',
value: value,
descriptionText: descriptionText,
translatable: true
]
} }
def refresh() { def refresh() {
log.debug "Refreshing Temperature and Battery" log.debug "Refreshing Temperature and Battery"
def refreshCmds = zigbee.readAttribute(zigbee.TEMPERATURE_MEASUREMENT_CLUSTER, 0x0000) + def refreshCmds = [
zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020) "st rattr 0x${device.deviceNetworkId} 1 0x402 0", "delay 200",
"st rattr 0x${device.deviceNetworkId} 1 1 0x20", "delay 200"
]
return refreshCmds + zigbee.enrollResponse() return refreshCmds + enrollResponse()
} }
def configure() { def configure() {
// Device-Watch allows 2 check-in misses from device + ping (plus 1 min lag time) sendEvent(name: "checkInterval", value: 7200, displayed: false)
// enrolls with default periodic reporting until newer 5 min interval is confirmed
sendEvent(name: "checkInterval", value: 2 * 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 String zigbeeEui = swapEndianHex(device.hub.zigbeeEui)
// battery minReport 30 seconds, maxReportTime 6 hrs by default log.debug "Configuring Reporting, IAS CIE, and Bindings."
return refresh() + zigbee.batteryConfig() + zigbee.temperatureConfig(30, 300) // send refresh cmds as part of config def configCmds = [
"zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200",
"send 0x${device.deviceNetworkId} 1 1", "delay 500",
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 1 {${device.zigbeeId}} {}", "delay 500",
"zcl global send-me-a-report 1 0x20 0x20 30 21600 {01}", //checkin time 6 hrs
"send 0x${device.deviceNetworkId} 1 1", "delay 500",
"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() {
log.debug "Sending enroll response"
String zigbeeEui = swapEndianHex(device.hub.zigbeeEui)
[
//Resending the CIE in case the enroll request is sent before CIE is written
"zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200",
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500",
//Enroll Response
"raw 0x500 {01 23 00 00 00}",
"send 0x${device.deviceNetworkId} 1 1", "delay 200"
]
}
private getEndpointId() {
new BigInteger(device.endpointId, 16).toString()
}
private hex(value) {
new BigInteger(Math.round(value).toString()).toString(16)
}
private String swapEndianHex(String hex) {
reverseArray(hex.decodeHex()).encodeHex()
}
private byte[] reverseArray(byte[] array) {
int i = 0;
int j = array.length - 1;
byte tmp;
while (j > i) {
tmp = array[j];
array[j] = array[i];
array[i] = tmp;
j--;
i++;
}
return array
} }

View File

@@ -37,13 +37,13 @@ metadata {
multiAttributeTile(name:"water", type: "generic", width: 6, height: 4){ multiAttributeTile(name:"water", type: "generic", width: 6, height: 4){
tileAttribute ("device.water", key: "PRIMARY_CONTROL") { tileAttribute ("device.water", key: "PRIMARY_CONTROL") {
attributeState "dry", label: "Dry", icon:"st.alarm.water.dry", backgroundColor:"#ffffff" attributeState "dry", label: "Dry", icon:"st.alarm.water.dry", backgroundColor:"#ffffff"
attributeState "wet", label: "Wet", icon:"st.alarm.water.wet", backgroundColor:"#00A0DC" attributeState "wet", label: "Wet", icon:"st.alarm.water.wet", backgroundColor:"#53a7c0"
} }
} }
standardTile("temperatureState", "device.temperature", width: 2, height: 2) { standardTile("temperatureState", "device.temperature", width: 2, height: 2) {
state "normal", icon:"st.alarm.temperature.normal", backgroundColor:"#ffffff" state "normal", icon:"st.alarm.temperature.normal", backgroundColor:"#ffffff"
state "freezing", icon:"st.alarm.temperature.freeze", backgroundColor:"#00A0DC" state "freezing", icon:"st.alarm.temperature.freeze", backgroundColor:"#53a7c0"
state "overheated", icon:"st.alarm.temperature.overheat", backgroundColor:"#e86d13" state "overheated", icon:"st.alarm.temperature.overheat", backgroundColor:"#F80000"
} }
valueTile("temperature", "device.temperature", width: 2, height: 2) { valueTile("temperature", "device.temperature", width: 2, height: 2) {
state("temperature", label:'${currentValue}°', state("temperature", label:'${currentValue}°',

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