Compare commits

..

12 Commits

Author SHA1 Message Date
Vinay Rao
a17971d68c Merge pull request #1583 from SmartThingsCommunity/staging
Rolling up staging to production for deploy
2017-01-10 15:01:44 -08:00
Vinay Rao
076ffecd19 Merge pull request #1582 from SmartThingsCommunity/production
Rolling down production hotfix to staging
2017-01-10 15:00:27 -08:00
Vinay Rao
8db0556696 Merge pull request #1562 from varzac/increase-delays
DPROT-223 Increase zigbee message delays
2017-01-10 09:35:46 -08:00
Vinay Rao
490ec329cb Merge pull request #1570 from workingmonk/feature/iris_button
DEVC-520 DEVC-525 Adding Iris Button after clearing certification
2017-01-06 09:34:49 -08:00
Vinay Rao
3109049122 Merge pull request #1569 from workingmonk/feature/certification_work
DEVC-518 Iris Smart Water Sensor
2017-01-06 09:34:25 -08:00
Zach Varberg
930c4ed914 Increase zigbee message delays
This changes our smartsense DTHs that don't use the ZigBee library for
everything to have larger delays between ZigBee messages.  This is to
reduce the network load to try to work around some of the poorer
behaving ZigBee routers we support.

This resolves: https://smartthings.atlassian.net/browse/DPROT-223
2017-01-06 10:45:13 -06:00
Vinay Rao
259516f21f DEVC-520 DEVC-525 Adding Iris Button after clearing certification 2017-01-05 16:54:46 -08:00
Vinay Rao
b7a08a88e0 DEVC-518 Iris Smart Water Sensor 2017-01-05 16:41:50 -08:00
bflorian
76e139153a Merge pull request #1532 from tslagle13/fix-color-coordinator-NPE
[DVCSMP-2273] Fix for NPE for slave/master settings
2016-12-15 17:11:01 -05:00
tslagle13
a4bc248006 One further fix for NPEs 2016-12-14 11:40:02 -08:00
tslagle13
e7eb461b4e [DVCSMP-2273] Fix for NPE for slave/master settings 2016-12-13 14:37:49 -08:00
Vinay Rao
083ed7cc9a Merge pull request #1518 from SmartThingsCommunity/staging
Rolling up staging to prod for deploy
2016-12-07 12:41:33 -08:00
12 changed files with 92 additions and 565 deletions

View File

@@ -1,486 +0,0 @@
/**
* Qubino Weatherstation
* Device Handler
* Version 1.0
* Date: 16.12.2016
* Author: Kristjan Jamšek (Kjamsek), Goap d.o.o.
* Copyright 2016 Kristjan Jamšek
*
* 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.
*
* - DEVICE HANDLER FOR QUBINO WEATHERSTATION Z-WAVE DEVICE -
* The handler supports all unsecure functions of the Qubino Weatherstation device. Configuration parameters and
* association groups can be set in the device's preferences screen, but they are applied on the device only after
* pressing the 'Set configuration' and 'Set associations' buttons on the bottom of the details view.
*
* This device handler supports data values that are currently not implemented as capabilities, so custom attribute
* states are used. Please use a SmartApp that supports custom attribute monitoring with this device in your rules.
* By default the 'Temperature Measurement' and 'Relative Humidity Measurement' capability states are mirrored from
* the custom attribute state values 'temperatureCh1' and 'humidityCh1'.
*
* This device handler uses hubAction in configure() method in order to set a MultiChannel Lifeline association to the
* device, so reports from all endpoints can be received.
*
*
* CHANGELOG:
* 0.99: Final release code cleanup and commenting
* 1.00: Added comments to code for readability
*/
metadata {
definition (name: "Qubino Weatherstation", namespace: "Goap", author: "Kristjan Jamšek") {
capability "Configuration" //Needed for configure() function to set MultiChannel Lifeline Association Set
capability "Temperature Measurement" //Used on main tile, needed in order to have the device appear in capabilities lists, mirrors temperatureCh1 attribute states
capability "Relative Humidity Measurement" //Needed in order to have the device appear in capabilities lists, mirrors humidityCh1 attribute states
capability "Sensor" // - Tagging capability
attribute "temperatureCh1", "number" // temperature attribute for Ch1 Temperature reported by device's endpoint 1
attribute "windDirection", "number" // wind direction attribute for Wind Gauge - Direction reported by device's endpoint 2
attribute "windVelocity", "number" // wind velocity attribute for Wind Gauge - Velocity reported by device's endpoint 3
attribute "windGust", "number" // wind gust velocity attribute for Wind Gauge - Wind gust reported by device's endpoint 4
attribute "windTemperature", "number" // wind temperature attribute for Wind Gauge - Temperature reported by device's endpoint 5
attribute "windChillTemperature", "number" // wind chill temperature attribute for Wind Gauge - Wind Chill reported by device's endpoint 6
attribute "rainRate", "number" // rain rate attribute for Rain Sensor data reported by device's endpoint 7
attribute "humidityCh1", "number" // humidity attribute for Ch1 Humidity reported by device's endpoint 8
attribute "temperatureCh2", "number" // temperature attribute for Ch2 Temperature reported by device's endpoint 9
attribute "humidityCh2", "number" // humidity attribute for Ch2 Humidity reported by device's endpoint 10
attribute "setConfigParams", "string" // attribute for tile element for setConfigurationParams command
attribute "setAssocGroups", "string" // attribute for tile element for setAssociations command
command "setConfigurationParams" // command to issue Configuration Set command sequence according to user's preferences
command "setAssociations" // command to issue Association Set command sequence according to user's preferences
fingerprint mfr:"0159", prod:"0007", model:"0053" //Manufacturer Information values for Qubino Weatherstation
}
simulator {
// TESTED WITH PHYSICAL DEVICE - UNNEEDED
}
tiles(scale: 2) {
valueTile("temperature", "device.temperature") {
state("temperature", label:'${currentValue} °', unit:"°", icon: "st.Weather.weather2", backgroundColors: [
// Celsius Color Range
[value: 0, color: "#153591"],
[value: 7, color: "#1e9cbb"],
[value: 15, color: "#90d2a7"],
[value: 23, color: "#44b621"],
[value: 29, color: "#f1d801"],
[value: 33, color: "#d04e00"],
[value: 36, color: "#bc2323"],
// Fahrenheit Color Range
[value: 40, color: "#153591"],
[value: 44, color: "#1e9cbb"],
[value: 59, color: "#90d2a7"],
[value: 74, color: "#44b621"],
[value: 84, color: "#f1d801"],
[value: 92, color: "#d04e00"],
[value: 96, color: "#bc2323"]
])
}
valueTile("humidity", "device.humidity") {
state("humidity", label:'${currentValue} %', unit:"%", display:false)
}
standardTile("temperatureCh1", "device.temperatureCh1", width: 3, height: 3) {
state("temperatureCh1", label:'Temperature Ch1: ${currentValue} °', unit:'°', icon: 'st.Weather.weather2')
}
standardTile("windDirection", "device.windDirection", width: 3, height: 3) {
state("windDirection", label:'Wind Direction: ${currentValue} °', unit: "°", icon: 'st.Outdoor.outdoor20')
}
standardTile("windVelocity", "device.windVelocity", width: 3, height: 3) {
state("windVelocity", label:'Wind Velocity: ${currentValue} m/s', unit:"m/s", icon: 'st.Weather.weather1')
}
standardTile("windGust", "device.windGust", width: 3, height: 3) {
state("windGust", label:'Wind Gust: ${currentValue} m/s', unit:"m/s", icon: 'st.Weather.weather1')
}
standardTile("windTemperature", "device.windTemperature", width: 3, height: 3) {
state("windTemperature", label:'Wind Temperature: ${currentValue} °', unit:'°', icon: 'st.Weather.weather2')
}
standardTile("windChillTemperature", "device.windChillTemperature", width: 3, height: 3) {
state("windChillTemperature", label:'Wind Chill: ${currentValue} °', unit:'°', icon: 'st.Weather.weather2')
}
standardTile("rainRate", "device.rainRate", width: 3, height: 3) {
state("rainRate", label:'Rain Sensor: ${currentValue} mm/h', unit:"mm/h", icon: 'st.Weather.weather10')
}
standardTile("humidityCh1", "device.humidityCh1", width: 3, height: 3) {
state("humidityCh1", label:'Humidity Ch1: ${currentValue} %', unit:"%", icon: 'st.Weather.weather12')
}
standardTile("temperatureCh2", "device.temperatureCh2", width: 3, height: 3) {
state("temperatureCh2", label:'Temperature Ch2: ${currentValue} °', unit:'°', icon: 'st.Weather.weather2')
}
standardTile("humidityCh2", "device.humidityCh2", width: 3, height: 3) {
state("humidityCh2", label:'Humidity Ch2: ${currentValue} %', unit:"%", icon: 'st.Weather.weather12')
}
standardTile("setConfigParams", "device.setConfigParams", decoration: "flat", width: 3, height: 3) {
state("setConfigParams", label:'Set configuration', action:'setConfigurationParams', icon: "st.secondary.tools")
}
standardTile("setAssocGroups", "device.setAssocGroups", decoration: "flat", width: 3, height: 3) {
state("setAssocGroups", label:'Set associations', action:'setAssociations', icon: "st.secondary.tools")
}
main("temperature")
details(["temperatureCh1", "humidityCh1", "windDirection", "windVelocity", "windGust", "windTemperature", "windChillTemperature", "rainRate", "temperatureCh2", "humidityCh2", "setConfigParams", "setAssocGroups"])
}
preferences {
/**
* -------- CONFIGURATION PARAMETER SECTION --------
*/
input name: "param1", type: "number", range: "0..8800", required: false,
title: "1. Wind Gauge, Wind Gust Top Value. " +
"If the Wind Gust is higher than the parameter value, the device triggers an association.\n" +
"Available settings:\n" +
"0 ... 8800 = value from 0,00 m/s to 88,00 m/s,\n" +
"Default value: 1000 (10,00 m/s)."
input name: "param2", type: "number", range: "0..30000", required: false,
title: "2. Rain Gauge, Rain Rate Top Value. " +
"If the Rain Sensor Rain Rate is higher than the parameter value, the device triggers an association.\n" +
"Available settings:\n" +
"0 ... 30000 = value from 0,00 mm/h to 300,00 mm/h,\n" +
"Default value: 200 (2,00 mm/h)."
input name: "param3", type: "enum", required: false,
options: ["0" : "0",
"1" : "1"],
title: "3. Wind Gauge, Wind Gust. " +
"Available settings:\n" +
"0 = If the Wind Gust is higher than the parameter number 1 value, then the device sends a Basic Set 0x00,\n" +
"1 = If the Wind Gust is higher than the parameter number 1 value, then the device sends a Basic Set 0xFF,\n" +
"Default value: 1."
input name: "param4", type: "enum", required: false,
options: ["0" : "0",
"1" : "1"],
title: "4. Rain Gauge, Rain Rate. " +
"Available settings:\n" +
"0 = If the Rain Rate is higher than the parameter number 2 value, then the device sends a Basic Set 0x00,\n" +
"1 = If the Rain Rate is higher than the parameter number 2 value, then the device sends a Basic Set 0xFF,\n" +
"Default value: 1."
input name: "param5", type: "enum", required: false,
options: ["0" : "0",
"1" : "1"],
title: "5. Endpoint 1 - Unsolicited Report enable/disable. " +
"Available settings:\n" +
"0 = Unsolicited Report disabled,\n" +
"1 = Unsolicited Report enabled,\n" +
"Default value: 1."
input name: "param6", type: "enum", required: false,
options: ["0" : "0",
"1" : "1"],
title: "6. Endpoint 2 - Unsolicited Report enable/disable. " +
"Available settings:\n" +
"0 = Unsolicited Report disabled,\n" +
"1 = Unsolicited Report enabled,\n" +
"Default value: 1."
input name: "param7", type: "enum", required: false,
options: ["0" : "0",
"1" : "1"],
title: "7. Endpoint 3 - Unsolicited Report enable/disable. " +
"Available settings:\n" +
"0 = Unsolicited Report disabled,\n" +
"1 = Unsolicited Report enabled,\n" +
"Default value: 1."
input name: "param8", type: "enum", required: false,
options: ["0" : "0",
"1" : "1"],
title: "8. Endpoint 4 - Unsolicited Report enable/disable. " +
"Available settings:\n" +
"0 = Unsolicited Report disabled,\n" +
"1 = Unsolicited Report enabled,\n" +
"Default value: 1."
input name: "param9", type: "enum", required: false,
options: ["0" : "0",
"1" : "1"],
title: "9. Endpoint 5 - Unsolicited Report enable/disable. " +
"Available settings:\n" +
"0 = Unsolicited Report disabled,\n" +
"1 = Unsolicited Report enabled,\n" +
"Default value: 1."
input name: "param10", type: "enum", required: false,
options: ["0" : "0",
"1" : "1"],
title: "10. Endpoint 6 - Unsolicited Report enable/disable. " +
"Available settings:\n" +
"0 = Unsolicited Report disabled,\n" +
"1 = Unsolicited Report enabled,\n" +
"Default value: 1."
input name: "param11", type: "enum", required: false,
options: ["0" : "0",
"1" : "1"],
title: "11. Endpoint 7 - Unsolicited Report enable/disable. " +
"Available settings:\n" +
"0 = Unsolicited Report disabled,\n" +
"1 = Unsolicited Report enabled,\n" +
"Default value: 1."
input name: "param12", type: "enum", required: false,
options: ["0" : "0",
"1" : "1"],
title: "12. Endpoint 8 - Unsolicited Report enable/disable. " +
"Available settings:\n" +
"0 = Unsolicited Report disabled,\n" +
"1 = Unsolicited Report enabled,\n" +
"Default value: 1."
input name: "param13", type: "enum", required: false,
options: ["0" : "0",
"1" : "1"],
title: "13. Endpoint 9 - Unsolicited Report enable/disable. " +
"Available settings:\n" +
"0 = Unsolicited Report disabled,\n" +
"1 = Unsolicited Report enabled,\n" +
"Default value: 1."
input name: "param14", type: "enum", required: false,
options: ["0" : "0",
"1" : "1"],
title: "14. Endpoint 10 - Unsolicited Report enable/disable. " +
"Available settings:\n" +
"0 = Unsolicited Report disabled,\n" +
"1 = Unsolicited Report enabled,\n" +
"Default value: 1."
input name: "param15", type: "enum", required: false,
options: ["0" : "0",
"1" : "1"],
title: "15. Random ID enable/disable " +
"Available settings:\n" +
"0 = Random ID disabled,\n" +
"1 = Random ID enabled,\n" +
"Default value: 0."
/**
* -------- ASSOCIATION GROUP SECTION --------
*/
input name: "assocGroup2", type: "text", required: false,
title: "Association group 2: \n" +
"Basic On/Off command will be sent to associated nodes when the Wind Gust of the Wind Gauge exceeds the Configuration parameter 1 value. \n" +
"NOTE: Insert the node Id value of the devices you wish to associate this group with. Multiple nodeIds can also be set at once by separating individual values by a comma (2,3,...)."
input name: "assocGroup3", type: "text", required: false,
title: "Association group 3: \n" +
"Basic On/Off command will be sent to associated nodes when the Rain Rate exceeds the Configuration parameter 2 value. \n" +
"NOTE: Insert the node Id value of the devices you wish to associate this group with. Multiple nodeIds can also be set at once by separating individual values by a comma (2,3,...)."
}
}
/**
* -------- HELPER METHODS SECTION --------
*/
/**
* Converts a list of String type node id values to Integer type.
*
* @param stringList - a list of String type node id values.
* @return stringList - a list of Integer type node id values.
*/
def convertStringListToIntegerList(stringList){
for(int i=0;i<stringList.size();i++){
stringList[i] = stringList[i].toInteger()
}
return stringList
}
/**
* Converts temperature values to fahrenheit or celsius scales accordign to user's setting.
*
* @param scaleParam user set scale parameter.
* @param encapCmd received temperature parsed value.
* @return String type value of the converted temperature value.
*/
def convertDegrees(scaleParam, encapCmd){
switch (scaleParam) {
default:
break;
case "F":
if(encapCmd.scale == 1){
return encapCmd.scaledSensorValue.toString()
}else{
return (encapCmd.scaledSensorValue * 9 / 5 + 32).toString()
}
break;
case "C":
if(encapCmd.scale == 0){
return encapCmd.scaledSensorValue.toString()
}else{
return (encapCmd.scaledSensorValue * 9 / 5 + 32).toString()
}
break;
}
}
/*
* -------- HANDLE COMMANDS SECTION --------
*/
/**
* Configuration capability command handler that executes after module inclusion to remove existing singlechannel association Lifeline and replace it
* with a multichannel Lifeline association setting for node id 1 and endpoint 1, which enables modules to report multichannel encapsulated frames.
*
* @param void
* @return List of commands that will be executed in sequence with 500 ms delay inbetween.
*/
def configure() {
log.debug "Qubino Weatherstation: configure()"
/** In this method we first clear the association group 1 that is set by SmartThings automatically.
* Afterwards we use physicalgraph.device.HubAction("8E0101000101") as a workaround for SmartThings' MultiChannel Association Command Class implementation,
* where we cannot set a node id and endpoint id. The hardcoded parameter "8E0101000101" should be interpreted in sequence as hexadecimal byte values:
* 8E010100 - Command class and command values, along with the field for singlechannel node id omitted
* 0101 - These two byte values represent the multichannel node id 01 followed by the endpoint id 01 (in general this value can be left as is unless
* multiple subordinated controllers are included in your network. In that case please adjust these two value to the correct id.
*/
def assocCmds = []
assocCmds << zwave.associationV1.associationRemove(groupingIdentifier:1, nodeId:zwaveHubNodeId).format()
assocCmds << new physicalgraph.device.HubAction("8E0101000101")
return delayBetween(assocCmds, 500)
}
/**
* setAssociations command handler that sets user selected association groups. In case no node id is insetred the group is instead cleared.
* Lifeline association hidden from user influence by design.
*
* @param void
* @return List of Association commands that will be executed in sequence with 500 ms delay inbetween.
*/
def setAssociations() {
log.debug "Qubino Weatherstation: setAssociations()"
def assocSet = []
if(settings.assocGroup2 != null){
def group2parsed = settings.assocGroup2.tokenize(",")
group2parsed = convertStringListToIntegerList(group2parsed)
assocSet << zwave.associationV1.associationSet(groupingIdentifier:2, nodeId:group2parsed).format()
}else{
assocSet << zwave.associationV1.associationRemove(groupingIdentifier:2, nodeId: 0).format()
}
if(settings.assocGroup3 != null){
def group3parsed = settings.assocGroup3.tokenize(",")
group3parsed = convertStringListToIntegerList(group3parsed)
assocSet << zwave.associationV1.associationSet(groupingIdentifier:3, nodeId:group3parsed).format()
}else{
assocSet << zwave.associationV1.associationRemove(groupingIdentifier:3, nodeId: 0).format()
}
return delayBetween(assocSet, 500)
}
/**
* setConfigurationParams command handler that sets user selected configuration parameters on the device.
* In case no value is set for a specific parameter the method skips setting that parameter.
* Secure mdoe setting hidden from user influence by design.
*
* @param void
* @return List of Configuration Set commands that will be executed in sequence with 500 ms delay inbetween.
*/
def setConfigurationParams() {
log.debug "Qubino Weatherstation: setConfigurationParams()"
def configSequence = []
if(settings.param1 != null){
configSequence << zwave.configurationV1.configurationSet(parameterNumber: 1, size: 2, scaledConfigurationValue: settings.param1.toInteger()).format()
}
if(settings.param2 != null){
configSequence << zwave.configurationV1.configurationSet(parameterNumber: 2, size: 2, scaledConfigurationValue: settings.param2.toInteger()).format()
}
if(settings.param3 != null){
configSequence << zwave.configurationV1.configurationSet(parameterNumber: 3, size: 1, scaledConfigurationValue: settings.param3.toInteger()).format()
}
if(settings.param4 != null){
configSequence << zwave.configurationV1.configurationSet(parameterNumber: 4, size: 1, scaledConfigurationValue: settings.param4.toInteger()).format()
}
if(settings.param5 != null){
configSequence << zwave.configurationV1.configurationSet(parameterNumber: 5, size: 1, scaledConfigurationValue: settings.param5.toInteger()).format()
}
if(settings.param6 != null){
configSequence << zwave.configurationV1.configurationSet(parameterNumber: 6, size: 1, scaledConfigurationValue: settings.param6.toInteger()).format()
}
if(settings.param7 != null){
configSequence << zwave.configurationV1.configurationSet(parameterNumber: 7, size: 1, scaledConfigurationValue: settings.param7.toInteger()).format()
}
if(settings.param8 != null){
configSequence << zwave.configurationV1.configurationSet(parameterNumber: 8, size: 1, scaledConfigurationValue: settings.param8.toInteger()).format()
}
if(settings.param9 != null){
configSequence << zwave.configurationV1.configurationSet(parameterNumber: 9, size: 1, scaledConfigurationValue: settings.param9.toInteger()).format()
}
if(settings.param10 != null){
configSequence << zwave.configurationV1.configurationSet(parameterNumber: 10, size: 1, scaledConfigurationValue: settings.param10.toInteger()).format()
}
if(settings.param11 != null){
configSequence << zwave.configurationV1.configurationSet(parameterNumber: 11, size: 1, scaledConfigurationValue: settings.param11.toInteger()).format()
}
if(settings.param12 != null){
configSequence << zwave.configurationV1.configurationSet(parameterNumber: 12, size: 1, scaledConfigurationValue: settings.param12.toInteger()).format()
}
if(settings.param13 != null){
configSequence << zwave.configurationV1.configurationSet(parameterNumber: 13, size: 1, scaledConfigurationValue: settings.param13.toInteger()).format()
}
if(settings.param14 != null){
configSequence << zwave.configurationV1.configurationSet(parameterNumber: 14, size: 1, scaledConfigurationValue: settings.param14.toInteger()).format()
}
if(settings.param15 != null){
configSequence << zwave.configurationV1.configurationSet(parameterNumber: 15, size: 1, scaledConfigurationValue: settings.param15.toInteger()).format()
}
return delayBetween(configSequence, 500)
}
/*
* -------- EVENT PARSER SECTION --------
*/
/**
* parse function takes care of parsing received bytes and passing them on to event methods.
*
* @param description String type value of the received bytes.
* @return Parsed result of the received bytes.
*/
def parse(String description) {
log.debug "Qubino Weatherstation: Parsing '${description}'"
def result = null
def cmd = zwave.parse(description)
if (cmd) {
result = zwaveEvent(cmd)
log.debug "Parsed ${cmd} to ${result.inspect()}"
} else {
log.debug "Non-parsed event: ${description}"
}
return result
}
/**
* Event method for MultiChannelCmdEncap encapsulation frames.
*
* @param cmd Type physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap received command.
* @return List of events that will update UI elements for data display.
*/
def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd) {
def encapsulatedCommand = cmd.encapsulatedCommand([0x30: 1, 0x31: 1])
def tempScale = location.temperatureScale
def resultEvents = []
switch (cmd.sourceEndPoint) {
default:
break;
case 1:
resultEvents << createEvent(name:"temperatureCh1", value: convertDegrees(tempScale,encapsulatedCommand), unit:"°"+location.temperatureScale, descriptionText: "Temperature Ch1: "+convertDegrees(tempScale,encapsulatedCommand)+"°"+location.temperatureScale)
resultEvents << createEvent(name:"temperature", value: convertDegrees(tempScale,encapsulatedCommand), unit:"°"+location.temperatureScale, descriptionText: "Temperature Ch1: "+convertDegrees(tempScale,encapsulatedCommand)+"°"+location.temperatureScale, displayed: false)
break;
case 2:
resultEvents << createEvent(name:"windDirection", value: encapsulatedCommand.scaledSensorValue.toString(), unit:"°", descriptionText: "Wind Direction: "+encapsulatedCommand.scaledSensorValue.toString()+" °")
break;
case 3:
resultEvents << createEvent(name:"windVelocity", value: encapsulatedCommand.scaledSensorValue.toString(), unit:"m/s", descriptionText: "Wind Velocity: "+encapsulatedCommand.scaledSensorValue.toString()+" m/s")
break;
case 4:
resultEvents << createEvent(name:"windGust", value: encapsulatedCommand.scaledSensorValue.toString(), unit:"m/s", descriptionText: "Wind Gust: "+encapsulatedCommand.scaledSensorValue.toString()+" m/s")
break;
case 5:
resultEvents << createEvent(name:"windTemperature", value: convertDegrees(tempScale,encapsulatedCommand), unit:"°"+location.temperatureScale, descriptionText: "Wind Temperature: "+convertDegrees(tempScale,encapsulatedCommand)+" °"+location.temperatureScale)
break;
case 6:
resultEvents << createEvent(name:"windChillTemperature", value: convertDegrees(tempScale,encapsulatedCommand), unit:"°"+location.temperatureScale, descriptionText: "Wind Chill: "+convertDegrees(tempScale,encapsulatedCommand)+" °"+location.temperatureScale)
break;
case 7:
resultEvents << createEvent(name:"rainRate", value: encapsulatedCommand.scaledSensorValue.toString(), unit:"mm/h", descriptionText: "Rain Sensor: "+encapsulatedCommand.scaledSensorValue.toString()+" mm/h")
break;
case 8:
resultEvents << createEvent(name:"humidity", value: encapsulatedCommand.scaledSensorValue.toString(), unit:"%", descriptionText: "Humidity Ch1: "+encapsulatedCommand.scaledSensorValue.toString()+" %", displayed: false)
resultEvents << createEvent(name:"humidityCh1", value: encapsulatedCommand.scaledSensorValue.toString(), unit:"%", descriptionText: "Humidity Ch1: "+encapsulatedCommand.scaledSensorValue.toString()+" %")
break;
case 9:
resultEvents << createEvent(name:"temperatureCh2", value: convertDegrees(tempScale,encapsulatedCommand), unit:"°"+location.temperatureScale, descriptionText: "Temperature Ch2: "+convertDegrees(tempScale,encapsulatedCommand)+" °"+location.temperatureScale)
break;
case 10:
resultEvents << createEvent(name:"humidityCh2", value: encapsulatedCommand.scaledSensorValue.toString(), unit:"%", descriptionText: "Humidity Ch2: "+encapsulatedCommand.scaledSensorValue.toString()+" %")
break;
}
return resultEvents
}

View File

@@ -99,7 +99,7 @@ def parse(String description) {
def poll() {
def refreshCmds = [
"st wattr 0x${device.deviceNetworkId} 1 8 0x10 0x21 {${state?.dOnOff ?: '0000'}}", "delay 500"
"st wattr 0x${device.deviceNetworkId} 1 8 0x10 0x21 {${state?.dOnOff ?: '0000'}}", "delay 2000"
]
return refreshCmds + zigbee.onOffRefresh() + zigbee.levelRefresh()
@@ -197,7 +197,7 @@ def off() {
def refresh() {
def refreshCmds = [
"st wattr 0x${device.deviceNetworkId} 1 8 0x10 0x21 {${state?.dOnOff ?: '0000'}}", "delay 500"
"st wattr 0x${device.deviceNetworkId} 1 8 0x10 0x21 {${state?.dOnOff ?: '0000'}}", "delay 2000"
]
return refreshCmds + zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.onOffConfig()

View File

@@ -128,9 +128,9 @@ def setLevel(value) {
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} 0x0B04 0x050B", "delay 500"
"st rattr 0x${device.deviceNetworkId} ${endpointId} 6 0", "delay 2000",
"st rattr 0x${device.deviceNetworkId} ${endpointId} 8 0", "delay 2000",
"st rattr 0x${device.deviceNetworkId} ${endpointId} 0x0B04 0x050B", "delay 2000"
]
}
@@ -313,9 +313,9 @@ def isDescriptionPower(descMap) {
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"
"zdo bind 0x${device.deviceNetworkId} 1 ${endpointId} 6 {${device.zigbeeId}} {}", "delay 2000",
"zcl global send-me-a-report 6 0 0x10 0 600 {01}", "delay 200",
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 2000"
]
}
@@ -323,9 +323,9 @@ def onOffConfig() {
//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"
"zdo bind 0x${device.deviceNetworkId} 1 ${endpointId} 8 {${device.zigbeeId}} {}", "delay 2000",
"zcl global send-me-a-report 8 0 0x20 5 3600 {01}", "delay 200",
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 2000"
]
}
@@ -333,9 +333,10 @@ def levelConfig() {
//min change in value is 05
def powerConfig() {
[
"zdo bind 0x${device.deviceNetworkId} 1 ${endpointId} 0x0B04 {${device.zigbeeId}} {}", "delay 200",
"zdo bind 0x${device.deviceNetworkId} 1 ${endpointId} 0x0B04 {${device.zigbeeId}} {}", "delay 2000",
"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"
"delay 200",
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 2000"
]
}
@@ -344,7 +345,10 @@ def setLevelWithRate(level, rate) {
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}"
[
"st cmd 0x${device.deviceNetworkId} ${endpointId} 8 4 {$level $rate}",
"delay 2000"
]
}
String convertToHexString(value, width=2) {

View File

@@ -51,7 +51,7 @@ def on() {
'zcl on-off on',
'delay 200',
"send 0x${zigbee.deviceNetworkId} 0x01 0x${zigbee.endpointId}",
'delay 500'
'delay 2000'
]
@@ -62,6 +62,6 @@ def off() {
'zcl on-off off',
'delay 200',
"send 0x${zigbee.deviceNetworkId} 0x01 0x${zigbee.endpointId}",
'delay 500'
'delay 2000'
]
}

View File

@@ -157,9 +157,10 @@ def configure() {
//min change in value is 01
def powerConfig() {
[
"zdo bind 0x${device.deviceNetworkId} 1 ${endpointId} 0x0B04 {${device.zigbeeId}} {}", "delay 200",
"zdo bind 0x${device.deviceNetworkId} 1 ${endpointId} 0x0B04 {${device.zigbeeId}} {}", "delay 2000",
"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"
"delay 200",
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 2000"
]
}

View File

@@ -29,9 +29,10 @@ metadata {
command "enrollResponse"
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3315-S", deviceJoinName: "Water Leak Sensor"
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3315"
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3315-Seu", deviceJoinName: "Water Leak Sensor"
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3315-S", deviceJoinName: "Water Leak Sensor"
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3315"
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3315-Seu", deviceJoinName: "Water Leak Sensor"
fingerprint inClusters: "0000,0001,0003,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"
}
@@ -285,8 +286,8 @@ def ping() {
def refresh() {
log.debug "Refreshing Temperature and Battery"
def refreshCmds = [
"st rattr 0x${device.deviceNetworkId} 1 0x402 0", "delay 200",
"st rattr 0x${device.deviceNetworkId} 1 1 0x20", "delay 200"
"st rattr 0x${device.deviceNetworkId} 1 0x402 0", "delay 2000",
"st rattr 0x${device.deviceNetworkId} 1 1 0x20", "delay 2000"
]
return refreshCmds + enrollResponse()
@@ -308,10 +309,10 @@ def enrollResponse() {
[
//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",
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 2000",
//Enroll Response
"raw 0x500 {01 23 00 00 00}",
"send 0x${device.deviceNetworkId} 1 1", "delay 200"
"raw 0x500 {01 23 00 00 00}", "delay 200",
"send 0x${device.deviceNetworkId} 1 1", "delay 2000"
]
}

View File

@@ -298,8 +298,8 @@ def ping() {
def refresh() {
log.debug "refresh called"
def refreshCmds = [
"st rattr 0x${device.deviceNetworkId} 1 0x402 0", "delay 200",
"st rattr 0x${device.deviceNetworkId} 1 1 0x20", "delay 200"
"st rattr 0x${device.deviceNetworkId} 1 0x402 0", "delay 2000",
"st rattr 0x${device.deviceNetworkId} 1 1 0x20", "delay 2000"
]
return refreshCmds + enrollResponse()
@@ -321,10 +321,10 @@ def enrollResponse() {
[
//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",
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 2000",
//Enroll Response
"raw 0x500 {01 23 00 00 00}",
"send 0x${device.deviceNetworkId} 1 1", "delay 200"
"raw 0x500 {01 23 00 00 00}", "delay 200",
"send 0x${device.deviceNetworkId} 1 1", "delay 2000"
]
}

View File

@@ -428,10 +428,10 @@ def enrollResponse() {
[
//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",
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 2000",
//Enroll Response
"raw 0x500 {01 23 00 00 00}", "delay 200",
"send 0x${device.deviceNetworkId} 1 1", "delay 200"
"send 0x${device.deviceNetworkId} 1 1", "delay 2000"
]
}

View File

@@ -252,8 +252,8 @@ def ping() {
def refresh() {
log.debug "Refreshing Temperature and Battery"
def refreshCmds = [
"st rattr 0x${device.deviceNetworkId} 1 0x402 0", "delay 200",
"st rattr 0x${device.deviceNetworkId} 1 1 0x20", "delay 200"
"st rattr 0x${device.deviceNetworkId} 1 0x402 0", "delay 2000",
"st rattr 0x${device.deviceNetworkId} 1 1 0x20", "delay 2000"
]
return refreshCmds + enrollResponse()
@@ -277,10 +277,10 @@ def enrollResponse() {
[
//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",
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 2000",
//Enroll Response
"raw 0x500 {01 23 00 00 00}",
"send 0x${device.deviceNetworkId} 1 1", "delay 200"
"raw 0x500 {01 23 00 00 00}", "delay 200",
"send 0x${device.deviceNetworkId} 1 1", "delay 2000"
]
}

View File

@@ -270,9 +270,9 @@ def configure() {
log.debug "Configuring Reporting and Bindings."
def humidityConfigCmds = [
"zdo bind 0x${device.deviceNetworkId} 1 1 0xFC45 {${device.zigbeeId}} {}", "delay 500",
"zcl global send-me-a-report 0xFC45 0 0x29 30 3600 {6400}",
"send 0x${device.deviceNetworkId} 1 1", "delay 500"
"zdo bind 0x${device.deviceNetworkId} 1 1 0xFC45 {${device.zigbeeId}} {}", "delay 2000",
"zcl global send-me-a-report 0xFC45 0 0x29 30 3600 {6400}", "delay 200",
"send 0x${device.deviceNetworkId} 1 1", "delay 2000"
]
// temperature minReportTime 30 seconds, maxReportTime 5 min. Reporting interval if no activity

View File

@@ -28,8 +28,8 @@ metadata {
fingerprint inClusters: "0000, 0001, 0003, 0020, 0402, 0B05", outClusters: "0003, 0006, 0008, 0019", manufacturer: "OSRAM", model: "LIGHTIFY Dimming Switch", deviceJoinName: "OSRAM LIGHTIFY Dimming Switch"
//fingerprint inClusters: "0000, 0001, 0003, 0020, 0500", outClusters: "0003,0019", manufacturer: "CentraLite", model: "3455-L", deviceJoinName: "Iris Care Pendant"
//fingerprint inClusters: "0000, 0001, 0003, 0007, 0020, 0402, 0B05", outClusters: "0003, 0006, 0019", manufacturer: "CentraLite", model: "3460-L", deviceJoinName: "Iris Smart Button"
//fingerprint inClusters: "0000, 0001, 0003, 0007, 0020, 0B05", outClusters: "0003, 0006, 0019", manufacturer: "CentraLite", model:"3450-L", deviceJoinName: "Iris KeyFob"
fingerprint inClusters: "0000, 0001, 0003, 0007, 0020, 0402, 0B05", outClusters: "0003, 0006, 0019", manufacturer: "CentraLite", model: "3460-L", deviceJoinName: "Iris Smart Button"
fingerprint inClusters: "0000, 0001, 0003, 0007, 0020, 0B05", outClusters: "0003, 0006, 0019", manufacturer: "CentraLite", model:"3450-L", deviceJoinName: "Iris KeyFob"
}
simulator {}

View File

@@ -1,10 +1,11 @@
/**
* Color Coordinator
* Version 1.1.0 - 11/9/16
* Version 1.1.1 - 11/9/16
* By Michael Struck
*
* 1.0.0 - Initial release
* 1.1.0 - Fixed issue where master can be part of slaves. This causes a loop that impacts SmartThings.
* 1.1.1 - Fix NPE being thrown for slave/master inputs being empty.
*
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
@@ -33,17 +34,17 @@ preferences {
def mainPage() {
dynamicPage(name: "mainPage", title: "", install: true, uninstall: false) {
def masterInList = slaves.id.find{it==master.id}
def masterInList = slaves?.id?.find{it==master?.id}
if (masterInList) {
section ("**WARNING**"){
paragraph "You have included the Master Light in the Slave Group. This will cause a loop in execution. Please remove this device from the Slave Group.", image: "https://raw.githubusercontent.com/MichaelStruck/SmartThingsPublic/master/img/caution.png"
}
}
section("Master Light") {
input "master", "capability.colorControl", title: "Colored Light"
input "master", "capability.colorControl", title: "Colored Light", required: true
}
section("Lights that follow the master settings") {
input "slaves", "capability.colorControl", title: "Colored Lights", multiple: true, required: false, submitOnChange: true
input "slaves", "capability.colorControl", title: "Colored Lights", multiple: true, required: true, submitOnChange: true
}
section([mobileOnly:true], "Options") {
input "randomYes", "bool",title: "When Master Turned On, Randomize Color", defaultValue: false
@@ -81,40 +82,44 @@ def init() {
}
//-----------------------------------
def onOffHandler(evt){
if (!slaves.id.find{it==master.id}){
if (master.currentValue("switch") == "on"){
if (randomYes) getRandomColorMaster()
else slaves?.on()
}
else {
slaves?.off()
}
if (slaves && master) {
if (!slaves?.id.find{it==master?.id}){
if (master?.currentValue("switch") == "on"){
if (randomYes) getRandomColorMaster()
else slaves?.on()
}
else {
slaves?.off()
}
}
}
}
def colorHandler(evt) {
if (!slaves.id.find{it==master.id} && master.currentValue("switch") == "on"){
log.debug "Changing Slave units H,S,L"
def dimLevel = master.currentValue("level")
def hueLevel = master.currentValue("hue")
def saturationLevel = master.currentValue("saturation")
def newValue = [hue: hueLevel, saturation: saturationLevel, level: dimLevel as Integer]
slaves?.setColor(newValue)
try {
log.debug "Changing Slave color temp"
def tempLevel = master.currentValue("colorTemperature")
slaves?.setColorTemperature(tempLevel)
}
catch (e){
log.debug "Color temp for master --"
}
if (slaves && master) {
if (!slaves?.id?.find{it==master?.id} && master?.currentValue("switch") == "on"){
log.debug "Changing Slave units H,S,L"
def dimLevel = master?.currentValue("level")
def hueLevel = master?.currentValue("hue")
def saturationLevel = master.currentValue("saturation")
def newValue = [hue: hueLevel, saturation: saturationLevel, level: dimLevel as Integer]
slaves?.setColor(newValue)
try {
log.debug "Changing Slave color temp"
def tempLevel = master?.currentValue("colorTemperature")
slaves?.setColorTemperature(tempLevel)
}
catch (e){
log.debug "Color temp for master --"
}
}
}
}
def getRandomColorMaster(){
def hueLevel = Math.floor(Math.random() *1000)
def saturationLevel = Math.floor(Math.random() * 100)
def dimLevel = master.currentValue("level")
def dimLevel = master?.currentValue("level")
def newValue = [hue: hueLevel, saturation: saturationLevel, level: dimLevel as Integer]
log.debug hueLevel
log.debug saturationLevel
@@ -123,12 +128,14 @@ def getRandomColorMaster(){
}
def tempHandler(evt){
if (!slaves.id.find{it==master.id} && master.currentValue("switch") == "on"){
if (evt.value != "--") {
log.debug "Changing Slave color temp based on Master change"
def tempLevel = master.currentValue("colorTemperature")
slaves?.setColorTemperature(tempLevel)
}
if (slaves && master) {
if (!slaves?.id?.find{it==master?.id} && master?.currentValue("switch") == "on"){
if (evt.value != "--") {
log.debug "Changing Slave color temp based on Master change"
def tempLevel = master.currentValue("colorTemperature")
slaves?.setColorTemperature(tempLevel)
}
}
}
}
@@ -139,7 +146,7 @@ private def textAppName() {
}
private def textVersion() {
def text = "Version 1.1.0 (11/09/2016)"
def text = "Version 1.1.1 (12/13/2016)"
}
private def textCopyright() {
@@ -166,4 +173,4 @@ private def textHelp() {
"This application will allow you to control the settings of multiple colored lights with one control. " +
"Simply choose a master control light, and then choose the lights that will follow the settings of the master, "+
"including on/off conditions, hue, saturation, level and color temperature. Also includes a random color feature."
}
}