mirror of
https://github.com/mtan93/SmartThingsPublic.git
synced 2026-03-09 05:11:52 +00:00
Compare commits
1 Commits
MSA-2003-1
...
MSA-1899-2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ae3a01a7f7 |
@@ -1,2 +0,0 @@
|
||||
.st-ignore
|
||||
README.md
|
||||
@@ -1,44 +0,0 @@
|
||||
# Express Controls EZMultiPli
|
||||
|
||||
Works with:
|
||||
|
||||
* [Express Controls EZMultiPli](https://www.smartthings.com/works-with-smartthings/)
|
||||
|
||||
## Table of contents
|
||||
|
||||
* [Release Notes](#release-notes)
|
||||
* [Capabilities](#capabilities)
|
||||
* [Troubleshooting](#troubleshooting)
|
||||
|
||||
## Release Notes
|
||||
|
||||
* **2017-04-19** - _dkirker_ - Update default config values in config value range check functions, use lux if lum option is null, fix NullPointerException on initial pairing when color data has not been set (and set the default color data!)
|
||||
* **2017-04-10** - _DrZwave_ (with help from Donald Kirker) - changed fingerprint to the new format, lowered the OnTime and other parameters to be "more in line with ST user expectations", get the luminance in LUX so it reports in lux all the time.
|
||||
* **2016-10-06** - _erocm1231_ - Added "updated" method to run when configuration options are changed. Depending on model of unit, luminance is being reported as a relative percentace or as a lux value. Added the option to configure this in the handler.
|
||||
* **2016-01-28** - _erocm1231_ - Changed the configuration method to use scaledConfiguration so that it properly formatted negative numbers. Also, added configurationGet and a configurationReport method so that config values can be verified.
|
||||
* **2015-12-04** - _erocm1231_ - added range value to preferences as suggested by @Dela-Rick.
|
||||
* **2015-11-26** - _erocm1231_ - Fixed null condition error when adding as a new device.
|
||||
* **2015-11-24** - _erocm1231_ - Added refresh command. Made a few changes to how the handler maps colors to the LEDs. Fixed the device not having its on/off status updated when colors are changed.
|
||||
* **2015-11-23** - _erocm1231_ - Changed the look to match SmartThings v2 devices.
|
||||
* **2015-11-21** - _erocm1231_ - Made code much more efficient. Also made it compatible when setColor is passed a hex value. Mapping of special colors: Soft White - Default - Yellow, White - Concentrate - White, Daylight - Energize - Teal, Warm White - Relax - Yellow
|
||||
* **2015-11-19** - _erocm1231_ - Fixed a couple incorrect colors, changed setColor to be more compatible with other apps
|
||||
* **2015-11-18** - _erocm1231_ - Added to setColor for compatibility with Smart Lighting
|
||||
* **v0.1.0** - _DrZWave_ - chose better icons, Got color LED to work - first fully functional version
|
||||
* **v0.0.9** - _jrs_ - got the temp and luminance to work. Motion works. Debugging the color wheel.
|
||||
* **v0.0.8** - _DrZWave_ 2/25/2015 - change the color control to be tiles since there are only 8 colors.
|
||||
* **v0.0.7** - _jrs_ - 02/23/2015 - Jim Sulin
|
||||
|
||||
## Capabilities
|
||||
|
||||
* **Actuator** - represents that a Device has commands
|
||||
* **Sensor** - detects sensor events
|
||||
* **Motion Sensor** - can detect motion
|
||||
* **Temperature Measurement** - defines device measures current temperature
|
||||
* **Illuminance Measurement** - gives the illuminance reading from devices that support it
|
||||
* **Switch** - can detect state (possible values: on/off)
|
||||
* **Color Control** - represents that the color attributes of a device can be controlled (hue, saturation, color value
|
||||
* **Configuration** - configure() command called when device is installed or device preferences updated
|
||||
* **Refresh** - refresh() command for status updates
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
@@ -1,405 +0,0 @@
|
||||
// Express Controls EZMultiPli Multi-sensor
|
||||
// Motion Sensor - Temperature - Light level - 8 Color Indicator LED - Z-Wave Range Extender - Wall Powered
|
||||
// driver for SmartThings
|
||||
// The EZMultiPli is also known as the HSM200 from HomeSeer.com
|
||||
|
||||
metadata {
|
||||
definition (name: "EZmultiPli", namespace: "DrZWave", author: "Eric Ryherd", oauth: true) {
|
||||
capability "Actuator"
|
||||
capability "Sensor"
|
||||
capability "Motion Sensor"
|
||||
capability "Temperature Measurement"
|
||||
capability "Illuminance Measurement"
|
||||
capability "Switch"
|
||||
capability "Color Control"
|
||||
capability "Configuration"
|
||||
capability "Refresh"
|
||||
|
||||
fingerprint mfr: "001E", prod: "0004", model: "0001" // new format for Fingerprint which is unique for every certified Z-Wave product
|
||||
} // end definition
|
||||
|
||||
simulator {
|
||||
// messages the device returns in response to commands it receives
|
||||
status "motion" : "command: 7105000000FF07, payload: 07"
|
||||
status "no motion" : "command: 7105000000FF07, payload: 00"
|
||||
|
||||
for (int i = 0; i <= 100; i += 20) {
|
||||
status "temperature ${i}F": new physicalgraph.zwave.Zwave().sensorMultilevelV5.sensorMultilevelReport(
|
||||
scaledSensorValue: i, precision: 1, sensorType: 1, scale: 1).incomingMessage()
|
||||
}
|
||||
for (int i = 0; i <= 100; i += 20) {
|
||||
status "luminance ${i} %": new physicalgraph.zwave.Zwave().sensorMultilevelV5.sensorMultilevelReport(
|
||||
scaledSensorValue: i, precision: 0, sensorType: 3).incomingMessage()
|
||||
}
|
||||
|
||||
} //end simulator
|
||||
|
||||
|
||||
tiles (scale: 2){
|
||||
multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){
|
||||
tileAttribute ("device.switch", key: "PRIMARY_CONTROL", icon: "st.Lighting.light18") {
|
||||
attributeState "on", label:'${name}', action:"switch.off", icon:"st.switches.light.on", backgroundColor:"#00A0DC", nextState:"turningOff"
|
||||
attributeState "off", label:'${name}', action:"switch.on", icon:"st.switches.light.off", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
attributeState "turningOn", label:'${name}', icon:"st.switches.light.on", backgroundColor:"#00A0DC"
|
||||
attributeState "turningOff", label:'${name}', icon:"st.switches.light.off", backgroundColor:"#ffffff"
|
||||
}
|
||||
tileAttribute ("device.color", key: "COLOR_CONTROL") {
|
||||
attributeState "color", action:"setColor"
|
||||
}
|
||||
tileAttribute ("statusText", key: "SECONDARY_CONTROL") {
|
||||
attributeState "statusText", label:'${currentValue}'
|
||||
}
|
||||
}
|
||||
|
||||
standardTile("motion", "device.motion", width: 2, height: 2, canChangeIcon: true, canChangeBackground: true) {
|
||||
state "active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#00A0DC"
|
||||
state "inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#cccccc"
|
||||
}
|
||||
valueTile("temperature", "device.temperature", width: 2, height: 2) {
|
||||
state "temperature", label:'${currentValue}°', unit:"F", icon:"", // would be better if the units would switch to the desired units of the system (imperial or metric)
|
||||
backgroundColors:[
|
||||
[value: 0, color: "#153591"], // blue=cold
|
||||
[value: 65, color: "#44b621"], // green
|
||||
[value: 70, color: "#44b621"], // green
|
||||
[value: 75, color: "#f1d801"], // yellow
|
||||
[value: 80, color: "#f1d801"], // yellow
|
||||
[value: 85, color: "#f1d801"], // yellow
|
||||
[value: 90, color: "#d04e00"], // red
|
||||
[value: 95, color: "#bc2323"] // red=hot
|
||||
]
|
||||
}
|
||||
|
||||
// icons to use would be st.Weather.weather2 or st.alarm.temperature.normal - see http://scripts.3dgo.net/smartthings/icons/ for a list of icons
|
||||
valueTile("illuminance", "device.illuminance", width: 2, height: 2, inactiveLabel: false) {
|
||||
// jrs 4/7/2015 - Null on display
|
||||
//state "luminosity", label:'${currentValue} ${unit}'
|
||||
state "luminosity", label:'${currentValue}', unit:'${currentValue}', icon:"",
|
||||
backgroundColors:[
|
||||
[value: 25, color: "#404040"],
|
||||
[value: 50, color: "#808080"],
|
||||
[value: 75, color: "#a0a0a0"],
|
||||
[value: 90, color: "#e0e0e0"],
|
||||
//lux measurement values
|
||||
[value: 150, color: "#404040"],
|
||||
[value: 300, color: "#808080"],
|
||||
[value: 600, color: "#a0a0a0"],
|
||||
[value: 900, color: "#e0e0e0"]
|
||||
]
|
||||
}
|
||||
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
|
||||
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
|
||||
}
|
||||
standardTile("configure", "device.configure", inactiveLabel: false, width: 2, height: 2, decoration: "flat") {
|
||||
state "configure", label:'', action:"configuration.configure", icon:"st.secondary.configure"
|
||||
}
|
||||
|
||||
main (["temperature","motion", "switch"])
|
||||
details(["switch", "motion", "temperature", "illuminance", "refresh", "configure"])
|
||||
} // end tiles
|
||||
|
||||
preferences {
|
||||
input "OnTime", "number", title: "No Motion Interval", description: "N minutes lights stay on after no motion detected [0, 1-127]", range: "0..127", defaultValue: 2, displayDuringSetup: true, required: false
|
||||
input "OnLevel", "number", title: "Dimmer Onlevel", description: "Dimmer OnLevel for associated node 2 lights [-1, 0, 1-99]", range: "-1..99", defaultValue: -1, displayDuringSetup: true, required: false
|
||||
input "LiteMin", "number", title: "Luminance Report Frequency", description: "Luminance report sent every N minutes [0-127]", range: "0..127", defaultValue: 6, displayDuringSetup: true, required: false
|
||||
input "TempMin", "number", title: "Temperature Report Frequency", description: "Temperature report sent every N minutes [0-127]", range: "0..127", defaultValue: 6, displayDuringSetup: true, required: false
|
||||
input "TempAdj", "number", title: "Temperature Calibration", description: "Adjust temperature up/down N tenths of a degree F [(-127)-(+128)]", range: "-127..128", defaultValue: 0, displayDuringSetup: true, required: false
|
||||
input("lum", "enum", title:"Illuminance Measurement", description: "Percent or Lux", defaultValue: 2 ,required: false, displayDuringSetup: true, options:
|
||||
[1:"Percent",
|
||||
2:"Lux"])
|
||||
}
|
||||
|
||||
} // end metadata
|
||||
|
||||
// Parse incoming device messages from device to generate events
|
||||
def parse(String description){
|
||||
//log.debug "==> New Zwave Event: ${description}"
|
||||
def result = []
|
||||
def cmd = zwave.parse(description, [0x31: 5]) // 0x31=SensorMultilevel which we force to be version 5
|
||||
if (cmd) {
|
||||
def cmdData = zwaveEvent(cmd)
|
||||
if (cmdData != [:])
|
||||
result << createEvent(cmdData)
|
||||
}
|
||||
|
||||
def statusTextmsg = ""
|
||||
if (device.currentState('temperature') != null && device.currentState('illuminance') != null) {
|
||||
statusTextmsg = "${device.currentState('temperature').value} ° - ${device.currentState('illuminance').value} ${(lum == 1) ? "%" : "LUX"}"
|
||||
sendEvent("name":"statusText", "value":statusTextmsg, displayed:false)
|
||||
}
|
||||
if (result != [null] && result != []) log.debug "Parse returned ${result}"
|
||||
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
// Event Generation
|
||||
def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelReport cmd){
|
||||
def map = [:]
|
||||
switch (cmd.sensorType) {
|
||||
case 0x01: // SENSOR_TYPE_TEMPERATURE_VERSION_1
|
||||
def cmdScale = cmd.scale == 1 ? "F" : "C"
|
||||
map.value = convertTemperatureIfNeeded(cmd.scaledSensorValue, cmdScale, cmd.precision)
|
||||
map.unit = getTemperatureScale()
|
||||
map.name = "temperature"
|
||||
log.debug "Temperature report"
|
||||
break;
|
||||
case 0x03 : // SENSOR_TYPE_LUMINANCE_VERSION_1
|
||||
map.value = cmd.scaledSensorValue.toInteger().toString()
|
||||
if(lum == 1) map.unit = "%"
|
||||
else map.unit = "lux"
|
||||
map.name = "illuminance"
|
||||
log.debug "Luminance report"
|
||||
break;
|
||||
}
|
||||
return map
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.configurationv2.ConfigurationReport cmd) {
|
||||
log.debug "${device.displayName} parameter '${cmd.parameterNumber}' with a byte size of '${cmd.size}' is set to '${cmd.configurationValue}'"
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cmd) {
|
||||
def map = [:]
|
||||
if (cmd.notificationType==0x07) { // NOTIFICATION_TYPE_BURGLAR
|
||||
if (cmd.event==0x07 || cmd.event==0x08) {
|
||||
map.name = "motion"
|
||||
map.value = "active"
|
||||
map.descriptionText = "$device.displayName motion detected"
|
||||
log.debug "motion recognized"
|
||||
} else if (cmd.event==0) {
|
||||
map.name = "motion"
|
||||
map.value = "inactive"
|
||||
map.descriptionText = "$device.displayName no motion detected"
|
||||
log.debug "No motion recognized"
|
||||
}
|
||||
}
|
||||
if (map.name != "motion") {
|
||||
log.debug "unmatched parameters for cmd: ${cmd.toString()}}"
|
||||
}
|
||||
return map
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) {
|
||||
// The EZMultiPli sets the color back to #ffffff on "off" or at init, so update the ST device to reflect this.
|
||||
if (device.latestState("color") == null || (cmd.value == 0 && device.latestState("color").value != "#ffffff")) {
|
||||
sendEvent(name: "color", value: "#ffffff", displayed: true)
|
||||
}
|
||||
[name: "switch", value: cmd.value ? "on" : "off", type: "digital"]
|
||||
}
|
||||
|
||||
def updated()
|
||||
{
|
||||
log.debug "updated() is being called"
|
||||
|
||||
def cmds = configure()
|
||||
|
||||
if (cmds != []) response(cmds)
|
||||
}
|
||||
|
||||
def on() {
|
||||
log.debug "Turning Light 'on'"
|
||||
delayBetween([
|
||||
zwave.basicV1.basicSet(value: 0xFF).format(),
|
||||
zwave.basicV1.basicGet().format()
|
||||
], 500)
|
||||
}
|
||||
|
||||
def off() {
|
||||
log.debug "Turning Light 'off'"
|
||||
delayBetween([
|
||||
zwave.basicV1.basicSet(value: 0x00).format(),
|
||||
zwave.basicV1.basicGet().format()
|
||||
], 500)
|
||||
}
|
||||
|
||||
|
||||
def setColor(value) {
|
||||
log.debug "setColor() : ${value}"
|
||||
def myred
|
||||
def mygreen
|
||||
def myblue
|
||||
def hexValue
|
||||
def cmds = []
|
||||
|
||||
if ( value.level == 1 && value.saturation > 20) {
|
||||
def rgb = huesatToRGB(value.hue as Integer, 100)
|
||||
myred = rgb[0] >=128 ? 255 : 0
|
||||
mygreen = rgb[1] >=128 ? 255 : 0
|
||||
myblue = rgb[2] >=128 ? 255 : 0
|
||||
}
|
||||
else if ( value.level > 1 ) {
|
||||
def rgb = huesatToRGB(value.hue as Integer, value.saturation as Integer)
|
||||
myred = rgb[0] >=128 ? 255 : 0
|
||||
mygreen = rgb[1] >=128 ? 255 : 0
|
||||
myblue = rgb[2] >=128 ? 255 : 0
|
||||
}
|
||||
else if (value.hex) {
|
||||
def rgb = value.hex.findAll(/[0-9a-fA-F]{2}/).collect { Integer.parseInt(it, 16) }
|
||||
myred = rgb[0] >=128 ? 255 : 0
|
||||
mygreen = rgb[1] >=128 ? 255 : 0
|
||||
myblue = rgb[2] >=128 ? 255 : 0
|
||||
}
|
||||
else {
|
||||
myred=value.red >=128 ? 255 : 0 // the EZMultiPli has just on/off for each of the 3 channels RGB so convert the 0-255 value into 0 or 255.
|
||||
mygreen=value.green >=128 ? 255 : 0
|
||||
myblue=value.blue>=128 ? 255 : 0
|
||||
}
|
||||
//log.debug "Red: ${myred} Green: ${mygreen} Blue: ${myblue}"
|
||||
//cmds << zwave.colorControlV1.stateSet(stateDataLength: 3, VariantGroup1: [0x02, myred], VariantGroup2:[ 0x03, mygreen], VariantGroup3:[0x04,myblue]).format() // ST support for this command as of 2015/02/23 does not support the color IDs so this command cannot be used.
|
||||
// So instead we'll use these commands to hack around the lack of support of the above command
|
||||
cmds << zwave.basicV1.basicSet(value: 0x00).format() // As of 2015/02/23 ST is not supporting stateSet properly but found this hack that works.
|
||||
if (myred!=0) {
|
||||
cmds << zwave.colorControlV1.startCapabilityLevelChange(capabilityId: 0x02, startState: myred, ignoreStartState: True, updown: True).format()
|
||||
cmds << zwave.colorControlV1.stopStateChange(capabilityId: 0x02).format()
|
||||
}
|
||||
if (mygreen!=0) {
|
||||
cmds << zwave.colorControlV1.startCapabilityLevelChange(capabilityId: 0x03, startState: mygreen, ignoreStartState: True, updown: True).format()
|
||||
cmds << zwave.colorControlV1.stopStateChange(capabilityId: 0x03).format()
|
||||
}
|
||||
if (myblue!=0) {
|
||||
cmds << zwave.colorControlV1.startCapabilityLevelChange(capabilityId: 0x04, startState: myblue, ignoreStartState: True, updown: True).format()
|
||||
cmds << zwave.colorControlV1.stopStateChange(capabilityId: 0x04).format()
|
||||
}
|
||||
cmds << zwave.basicV1.basicGet().format()
|
||||
hexValue = rgbToHex([r:myred, g:mygreen, b:myblue])
|
||||
if(hexValue) sendEvent(name: "color", value: hexValue, displayed: true)
|
||||
delayBetween(cmds, 100)
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.Command cmd) {
|
||||
// Handles all Z-Wave commands we aren't interested in
|
||||
[:]
|
||||
}
|
||||
|
||||
// ensure we are passing acceptable param values for LiteMin & TempMin configs
|
||||
def checkLiteTempInput(value) {
|
||||
if (value == null) {
|
||||
value=6
|
||||
}
|
||||
def liteTempVal = value.toInteger()
|
||||
switch (liteTempVal) {
|
||||
case { it < 0 }:
|
||||
return 6 // bad value, set to default
|
||||
break
|
||||
case { it > 127 }:
|
||||
return 127 // bad value, greater then MAX, set to MAX
|
||||
break
|
||||
default:
|
||||
return liteTempVal // acceptable value
|
||||
}
|
||||
}
|
||||
|
||||
// ensure we are passing acceptable param value for OnTime config
|
||||
def checkOnTimeInput(value) {
|
||||
if (value == null) {
|
||||
value=2
|
||||
}
|
||||
def onTimeVal = value.toInteger()
|
||||
switch (onTimeVal) {
|
||||
case { it < 0 }:
|
||||
return 2 // bad value set to default
|
||||
break
|
||||
case { it > 127 }:
|
||||
return 127 // bad value, greater then MAX, set to MAX
|
||||
break
|
||||
default:
|
||||
return onTimeVal // acceptable value
|
||||
}
|
||||
}
|
||||
|
||||
// ensure we are passing acceptable param value for OnLevel config
|
||||
def checkOnLevelInput(value) {
|
||||
if (value == null) {
|
||||
value=99
|
||||
}
|
||||
def onLevelVal = value.toInteger()
|
||||
switch (onLevelVal) {
|
||||
case { it < -1 }:
|
||||
return -1 // bad value set to default
|
||||
break
|
||||
case { it > 99 }:
|
||||
return 99 // bad value, greater then MAX, set to MAX
|
||||
break
|
||||
default:
|
||||
return onLevelVal // acceptable value
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ensure we are passing an acceptable param value for TempAdj configs
|
||||
def checkTempAdjInput(value) {
|
||||
if (value == null) {
|
||||
value=0
|
||||
}
|
||||
def tempAdjVal = value.toInteger()
|
||||
switch (tempAdjVal) {
|
||||
case { it < -127 }:
|
||||
return 0 // bad value, set to default
|
||||
break
|
||||
case { it > 128 }:
|
||||
return 128 // bad value, greater then MAX, set to MAX
|
||||
break
|
||||
default:
|
||||
return tempAdjVal // acceptable value
|
||||
}
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
def cmd = []
|
||||
cmd << zwave.switchColorV3.switchColorGet().format()
|
||||
cmd << zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType:1, scale:1).format()
|
||||
cmd << zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType:3, scale:1).format()
|
||||
cmd << zwave.basicV1.basicGet().format()
|
||||
delayBetween(cmd, 1000)
|
||||
}
|
||||
|
||||
def configure() {
|
||||
log.debug "OnTime=${settings.OnTime} OnLevel=${settings.OnLevel} TempAdj=${settings.TempAdj}"
|
||||
def cmd = delayBetween([
|
||||
zwave.configurationV1.configurationSet(parameterNumber: 1, size: 1, scaledConfigurationValue: checkOnTimeInput(settings.OnTime)).format(),
|
||||
zwave.configurationV1.configurationSet(parameterNumber: 2, size: 1, scaledConfigurationValue: checkOnLevelInput(settings.OnLevel)).format(),
|
||||
zwave.configurationV1.configurationSet(parameterNumber: 3, size: 1, scaledConfigurationValue: checkLiteTempInput(settings.LiteMin)).format(),
|
||||
zwave.configurationV1.configurationSet(parameterNumber: 4, size: 1, scaledConfigurationValue: checkLiteTempInput(settings.TempMin)).format(),
|
||||
zwave.configurationV1.configurationSet(parameterNumber: 5, size: 1, scaledConfigurationValue: checkTempAdjInput(settings.TempAdj)).format(),
|
||||
zwave.configurationV1.configurationGet(parameterNumber: 1).format(),
|
||||
zwave.configurationV1.configurationGet(parameterNumber: 2).format(),
|
||||
zwave.configurationV1.configurationGet(parameterNumber: 3).format(),
|
||||
zwave.configurationV1.configurationGet(parameterNumber: 4).format(),
|
||||
zwave.configurationV1.configurationGet(parameterNumber: 5).format()
|
||||
], 100)
|
||||
//log.debug cmd
|
||||
cmd + refresh()
|
||||
}
|
||||
|
||||
def huesatToRGB(float hue, float sat) {
|
||||
while(hue >= 100) hue -= 100
|
||||
int h = (int)(hue / 100 * 6)
|
||||
float f = hue / 100 * 6 - h
|
||||
int p = Math.round(255 * (1 - (sat / 100)))
|
||||
int q = Math.round(255 * (1 - (sat / 100) * f))
|
||||
int t = Math.round(255 * (1 - (sat / 100) * (1 - f)))
|
||||
switch (h) {
|
||||
case 0: return [255, t, p]
|
||||
case 1: return [q, 255, p]
|
||||
case 2: return [p, 255, t]
|
||||
case 3: return [p, q, 255]
|
||||
case 4: return [t, p, 255]
|
||||
case 5: return [255, p, q]
|
||||
}
|
||||
}
|
||||
def rgbToHex(rgb) {
|
||||
def r = hex(rgb.r)
|
||||
def g = hex(rgb.g)
|
||||
def b = hex(rgb.b)
|
||||
def hexColor = "#${r}${g}${b}"
|
||||
|
||||
hexColor
|
||||
}
|
||||
private hex(value, width=2) {
|
||||
def s = new BigInteger(Math.round(value).toString()).toString(16)
|
||||
while (s.size() < width) {
|
||||
s = "0" + s
|
||||
}
|
||||
s
|
||||
}
|
||||
@@ -39,8 +39,8 @@ metadata {
|
||||
}
|
||||
|
||||
tileAttribute("device.tamper", key:"SECONDARY_CONTROL") {
|
||||
attributeState("active", label:'tamper active', backgroundColor:"#00A0DC")
|
||||
attributeState("inactive", label:'tamper inactive', backgroundColor:"#CCCCCC")
|
||||
attributeState("active", label:'tamper active', backgroundColor:"#53a7c0")
|
||||
attributeState("inactive", label:'tamper inactive', backgroundColor:"#ffffff")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,8 +37,8 @@ metadata {
|
||||
}
|
||||
|
||||
tileAttribute("device.tamper", key:"SECONDARY_CONTROL") {
|
||||
attributeState("active", label:'tamper active', backgroundColor:"#00A0DC")
|
||||
attributeState("inactive", label:'tamper inactive', backgroundColor:"#CCCCCC")
|
||||
attributeState("active", label:'tamper active', backgroundColor:"#53a7c0")
|
||||
attributeState("inactive", label:'tamper inactive', backgroundColor:"#ffffff")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,8 +34,8 @@ metadata {
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name:"FGMS", type:"lighting", width:6, height:4) {//with generic type secondary control text is not displayed in Android app
|
||||
tileAttribute("device.motion", key:"PRIMARY_CONTROL") {
|
||||
attributeState("inactive", label:"no motion", icon:"st.motion.motion.inactive", backgroundColor:"#cccccc")
|
||||
attributeState("active", label:"motion", icon:"st.motion.motion.active", backgroundColor:"#00A0DC")
|
||||
attributeState("inactive", label:"no motion", icon:"st.motion.motion.inactive", backgroundColor:"#79b821")
|
||||
attributeState("active", label:"motion", icon:"st.motion.motion.active", backgroundColor:"#ffa81e")
|
||||
}
|
||||
|
||||
tileAttribute("device.tamper", key:"SECONDARY_CONTROL") {
|
||||
|
||||
@@ -95,12 +95,12 @@ metadata {
|
||||
multiAttributeTile(name:"doorCtrl", type:"generic", width:6, height:4) {tileAttribute("device.doorState", key: "PRIMARY_CONTROL")
|
||||
{
|
||||
attributeState "unknown", label: '${name}', action:"openDoor", icon: "st.Outdoor.outdoor20", nextState:"Sent"
|
||||
attributeState "open", label: '${name}', action:"closeDoor", icon: "st.Outdoor.outdoor20", backgroundColor: "#00A0DC" , nextState:"Sent"
|
||||
attributeState "opening", label: '${name}', action:"closeDoor", icon: "st.Outdoor.outdoor20", backgroundColor: "#00A0DC"
|
||||
attributeState "closed", label: '${name}', action:"openDoor", icon: "st.Outdoor.outdoor20", backgroundColor: "#ffffff", nextState:"Sent"
|
||||
attributeState "closing", label: '${name}', action:"openDoor", icon: "st.Outdoor.outdoor20", backgroundColor: "#ffffff"
|
||||
attributeState "jammed", label: '${name}', action:"closeDoorHiI", icon: "st.Outdoor.outdoor20", backgroundColor: "#ff0000", nextState:"Sent"
|
||||
attributeState "forced close", label: 'forced\rclose', action:"openDoor", icon: "st.Outdoor.outdoor20", backgroundColor: "#ff8000", nextState:"Sent"
|
||||
attributeState "open", label: '${name}', action:"closeDoor", icon: "st.Outdoor.outdoor20", backgroundColor: "#0000ff" , nextState:"Sent"
|
||||
attributeState "opening", label: '${name}', action:"closeDoor", icon: "st.Outdoor.outdoor20", backgroundColor: "#ffa81e"
|
||||
attributeState "closed", label: '${name}', action:"openDoor", icon: "st.Outdoor.outdoor20", backgroundColor: "#79b821", nextState:"Sent"
|
||||
attributeState "closing", label: '${name}', action:"openDoor", icon: "st.Outdoor.outdoor20", backgroundColor: "#ffa81e"
|
||||
attributeState "jammed", label: '${name}', action:"closeDoorHiI", icon: "st.Outdoor.outdoor20", backgroundColor: "#ff0000", nextState:"Sent"
|
||||
attributeState "forced close", label: 'forced\rclose', action:"openDoor", icon: "st.Outdoor.outdoor20", backgroundColor: "#ff8000", nextState:"Sent"
|
||||
attributeState "fault", label: 'FAULT', action:"openDoor", icon: "st.Outdoor.outdoor20", backgroundColor: "#ff0000", nextState:"Sent"
|
||||
attributeState "Sent", label: 'wait', icon: "st.motion.motion.active", backgroundColor: "#ffa81e"
|
||||
}
|
||||
@@ -135,13 +135,13 @@ metadata {
|
||||
}
|
||||
|
||||
standardTile("autoClose", "device.autoCloseEnable", width: 2, height: 2){
|
||||
state "on", label: 'Auto', action:"autoCloseOff", icon: "st.doors.garage.garage-closing", backgroundColor: "#00A0DC", nextState:"Sent"
|
||||
state "on", label: 'Auto', action:"autoCloseOff", icon: "st.doors.garage.garage-closing", backgroundColor: "#79b821", nextState:"Sent"
|
||||
state "off", label: 'Auto', action:"autoCloseOn", icon: "st.doors.garage.garage-closing", nextState:"Sent"
|
||||
state "Sent", label: 'wait', icon: "st.motion.motion.active", backgroundColor: "#ffa81e"
|
||||
}
|
||||
|
||||
standardTile("autoOpen", "device.autoOpenEnable", width: 2, height: 2){
|
||||
state "on", label: 'Auto', action:"autoOpenOff", icon: "st.doors.garage.garage-opening", backgroundColor: "#00A0DC", nextState:"Sent"
|
||||
state "on", label: 'Auto', action:"autoOpenOff", icon: "st.doors.garage.garage-opening", backgroundColor: "#79b821", nextState:"Sent"
|
||||
state "off", label: 'Auto', action:"autoOpenOn", icon: "st.doors.garage.garage-opening", nextState:"Sent"
|
||||
state "Sent", label: 'wait', icon: "st.motion.motion.active", backgroundColor: "#ffa81e"
|
||||
}
|
||||
@@ -189,13 +189,13 @@ metadata {
|
||||
|
||||
standardTile("aux1", "device.Aux1", width: 2, height: 2, canChangeIcon: true) {
|
||||
state "off", label:'Aux 1', action:"Aux1On", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"Sent"
|
||||
state "on", label:'Aux 1', action:"Aux1Off", icon:"st.switches.switch.on", backgroundColor:"#00A0DC", nextState:"Sent"
|
||||
state "on", label:'Aux 1', action:"Aux1Off", icon:"st.switches.switch.on", backgroundColor:"#79b821", nextState:"Sent"
|
||||
state "Sent", label: 'wait', icon: "st.motion.motion.active", backgroundColor: "#ffa81e"
|
||||
}
|
||||
|
||||
standardTile("aux2", "device.Aux2", width: 2, height: 2, canChangeIcon: true) {
|
||||
state "off", label:'Aux 2', action:"Aux2On", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"Sent"
|
||||
state "on", label:'Aux 2', action:"Aux2Off", icon:"st.switches.switch.on", backgroundColor:"#00A0DC", nextState:"Sent"
|
||||
state "on", label:'Aux 2', action:"Aux2Off", icon:"st.switches.switch.on", backgroundColor:"#79b821", nextState:"Sent"
|
||||
state "Sent", label: 'wait', icon: "st.motion.motion.active", backgroundColor: "#ffa81e"
|
||||
}
|
||||
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
.st-ignore
|
||||
README.md
|
||||
@@ -1,33 +0,0 @@
|
||||
# Osotech Plant Link
|
||||
|
||||
Cloud Execution
|
||||
|
||||
Works with:
|
||||
|
||||
* [OSO Technologies PlantLink Soil Moisture Sensor](https://www.smartthings.com/works-with-smartthings/oso-technologies/oso-technologies-plantlink-soil-moisture-sensor)
|
||||
|
||||
## Table of contents
|
||||
|
||||
* [Capabilities](#capabilities)
|
||||
* [Health](#device-health)
|
||||
* [Troubleshooting](#troubleshooting)
|
||||
|
||||
## Capabilities
|
||||
|
||||
* **Sensor** - detects sensor events
|
||||
* **Health Check** - indicates ability to get device health notifications
|
||||
|
||||
## Device Health
|
||||
|
||||
Plant Link sensor is a ZigBee sleepy device and checks in every 15 minutes.
|
||||
Device-Watch allows 2 check-in misses from device plus some lag time. So Check-in interval = (2*15 + 2)mins = 32 mins.
|
||||
|
||||
* __32min__ checkInterval
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the sensor is out of range.
|
||||
Pairing needs to be tried again by placing the sensor closer to the hub.
|
||||
Instructions related to pairing, resetting and removing the different motion sensors from SmartThings can be found in the following links
|
||||
for the different models:
|
||||
* [OSO Technologies PlantLink Soil Moisture Sensor Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/206868986-PlantLink-Soil-Moisture-Sensor)
|
||||
@@ -24,7 +24,6 @@ import groovy.json.JsonBuilder
|
||||
metadata {
|
||||
definition (name: "PlantLink", namespace: "OsoTech", author: "Oso Technologies") {
|
||||
capability "Sensor"
|
||||
capability "Health Check"
|
||||
|
||||
command "setStatusIcon"
|
||||
command "setPlantFuelLevel"
|
||||
@@ -71,16 +70,6 @@ metadata {
|
||||
}
|
||||
}
|
||||
|
||||
def updated() {
|
||||
// Device-Watch allows 2 check-in misses from device
|
||||
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
||||
}
|
||||
|
||||
def installed() {
|
||||
// Device-Watch allows 2 check-in misses from device
|
||||
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
||||
}
|
||||
|
||||
def setStatusIcon(value){
|
||||
def status = ''
|
||||
switch (value) {
|
||||
@@ -172,4 +161,4 @@ def parseDescriptionAsMap(description) {
|
||||
map += []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,6 @@ metadata {
|
||||
capability "Battery"
|
||||
|
||||
fingerprint deviceId: "0x0101", inClusters: "0x86,0x72,0x70,0x80,0x84,0x85"
|
||||
fingerprint mfr: "0086", prod: "0001", model: "0026", deviceJoinName: "Aeon Panic Button"
|
||||
}
|
||||
|
||||
simulator {
|
||||
@@ -131,12 +130,5 @@ def updated() {
|
||||
}
|
||||
|
||||
def initialize() {
|
||||
def zwMap = getZwaveInfo()
|
||||
def buttons = 4 // Default for Key Fob
|
||||
|
||||
// Only one button for Aeon Panic Button
|
||||
if (zwMap && zwMap.mfr == "0086" && zwMap.prod == "0001" && zwMap.model == "0026") {
|
||||
buttons = 1
|
||||
}
|
||||
sendEvent(name: "numberOfButtons", value: buttons)
|
||||
sendEvent(name: "numberOfButtons", value: 4)
|
||||
}
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
.st-ignore
|
||||
README.md
|
||||
@@ -1,43 +0,0 @@
|
||||
# Aeon Multisensor 6
|
||||
|
||||
Cloud Execution
|
||||
|
||||
Works with:
|
||||
|
||||
* [Aeon Labs MultiSensor 6](https://www.smartthings.com/products/aeon-labs-multisensor-6)
|
||||
|
||||
## Table of contents
|
||||
|
||||
* [Capabilities](#capabilities)
|
||||
* [Health](#device-health)
|
||||
* [Troubleshooting](#troubleshooting)
|
||||
|
||||
## Capabilities
|
||||
|
||||
* **Motion Sensor** - can detect motion
|
||||
* **Temperature Measurement** - defines device measures current temperature
|
||||
* **Relative Humidity Measurement** - allow reading the relative humidity from devices that support it
|
||||
* **Illuminance Measurement** - gives the illuminance reading from devices that support it
|
||||
* **Ultraviolet Index** - gives the ability to get the ultraviolet index from devices that report it
|
||||
* **Configuration** - _configure()_ command called when device is installed or device preferences updated
|
||||
* **Sensor** - detects sensor events
|
||||
* **Battery** - defines device uses a battery
|
||||
* **Health Check** - indicates ability to get device health notifications
|
||||
|
||||
## Device Health
|
||||
|
||||
Aeon Labs MultiSensor 6 is 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.
|
||||
Device-Watch allows 2 check-in misses from device plus some lag time. So Check-in interval = (2*15 + 2)mins = 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:
|
||||
* [Aeon Labs MultiSensor 6 Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/206157226)
|
||||
@@ -22,7 +22,6 @@ metadata {
|
||||
capability "Configuration"
|
||||
capability "Sensor"
|
||||
capability "Battery"
|
||||
capability "Health Check"
|
||||
|
||||
attribute "tamper", "enum", ["detected", "clear"]
|
||||
attribute "batteryStatus", "string"
|
||||
@@ -30,7 +29,6 @@ metadata {
|
||||
|
||||
fingerprint deviceId: "0x2101", inClusters: "0x5E,0x86,0x72,0x59,0x85,0x73,0x71,0x84,0x80,0x30,0x31,0x70,0x7A", outClusters: "0x5A"
|
||||
fingerprint deviceId: "0x2101", inClusters: "0x5E,0x86,0x72,0x59,0x85,0x73,0x71,0x84,0x80,0x30,0x31,0x70,0x7A,0x5A"
|
||||
fingerprint mfr:"0086", prod:"0102", model:"0064", deviceJoinName: "Aeon Labs MultiSensor 6"
|
||||
}
|
||||
|
||||
simulator {
|
||||
@@ -105,7 +103,7 @@ metadata {
|
||||
}
|
||||
|
||||
valueTile("illuminance", "device.illuminance", inactiveLabel: false, width: 2, height: 2) {
|
||||
state "illuminance", label:'${currentValue} lux', unit:""
|
||||
state "illuminance", label:'${currentValue} ${unit}', unit:"lux"
|
||||
}
|
||||
|
||||
valueTile("ultravioletIndex", "device.ultravioletIndex", inactiveLabel: false, width: 2, height: 2) {
|
||||
@@ -129,14 +127,7 @@ metadata {
|
||||
}
|
||||
}
|
||||
|
||||
def installed(){
|
||||
// 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])
|
||||
}
|
||||
|
||||
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])
|
||||
log.debug "Updated with settings: ${settings}"
|
||||
log.debug "${device.displayName} is now ${device.latestValue("powerSupply")}"
|
||||
|
||||
@@ -335,13 +326,6 @@ def zwaveEvent(physicalgraph.zwave.Command cmd) {
|
||||
createEvent(descriptionText: cmd.toString(), isStateChange: false)
|
||||
}
|
||||
|
||||
/**
|
||||
* PING is used by Device-Watch in attempt to reach the Device
|
||||
* */
|
||||
def ping() {
|
||||
secure(zwave.batteryV1.batteryGet())
|
||||
}
|
||||
|
||||
def configure() {
|
||||
// This sensor joins as a secure device if you double-click the button to include it
|
||||
log.debug "${device.displayName} is configuring its settings"
|
||||
@@ -426,4 +410,4 @@ private command(physicalgraph.zwave.Command cmd) {
|
||||
private commands(commands, delay=200) {
|
||||
log.info "sending commands: ${commands}"
|
||||
delayBetween(commands.collect{ command(it) }, delay)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ metadata {
|
||||
state "humidity", label:'${currentValue}% humidity', unit:""
|
||||
}
|
||||
valueTile("illuminance", "device.illuminance", inactiveLabel: false, width: 2, height: 2) {
|
||||
state "luminosity", label:'${currentValue} lux', unit:""
|
||||
state "luminosity", label:'${currentValue} ${unit}', unit:"lux"
|
||||
}
|
||||
valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
|
||||
state "battery", label:'${currentValue}% battery', unit:""
|
||||
@@ -282,4 +282,5 @@ private secure(physicalgraph.zwave.Command cmd) {
|
||||
|
||||
private secureSequence(commands, delay=200) {
|
||||
delayBetween(commands.collect{ secure(it) }, delay)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ metadata {
|
||||
state "humidity", label:'${currentValue}% humidity', unit:""
|
||||
}
|
||||
valueTile("illuminance", "device.illuminance", inactiveLabel: false, width: 2, height: 2) {
|
||||
state "luminosity", label:'${currentValue} lux', unit:""
|
||||
state "luminosity", label:'${currentValue} ${unit}', unit:"lux"
|
||||
}
|
||||
valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
|
||||
state "battery", label:'${currentValue}% battery', unit:""
|
||||
@@ -193,4 +193,4 @@ def configure() {
|
||||
// set data reporting period to 5 minutes
|
||||
zwave.configurationV1.configurationSet(parameterNumber: 111, size: 4, scaledConfigurationValue: 300).format()
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
* Date: 2014-07-15
|
||||
*/
|
||||
metadata {
|
||||
definition (name: "Aeon Siren", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "x.com.st.d.sensor.smoke") {
|
||||
definition (name: "Aeon Siren", namespace: "smartthings", author: "SmartThings") {
|
||||
capability "Actuator"
|
||||
capability "Alarm"
|
||||
capability "Switch"
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
.st-ignore
|
||||
README.md
|
||||
@@ -1,50 +0,0 @@
|
||||
# Arrival Sensor HA (2016+ Model)
|
||||
|
||||
Cloud Execution
|
||||
|
||||
Works with:
|
||||
|
||||
* [Samsung SmartThings Arrival Sensor](https://support.smartthings.com/hc/en-us/articles/212417083-Samsung-SmartThings-Arrival-Sensor)
|
||||
|
||||
## Table of contents
|
||||
|
||||
* [Capabilities](#capabilities)
|
||||
* [Health](#device-health)
|
||||
* [Battery](#battery)
|
||||
* [Troubleshooting](#troubleshooting)
|
||||
|
||||
|
||||
## Capabilities
|
||||
|
||||
* **Tone** - beep command to allow an audible tone
|
||||
* **Actuator** - device has commands
|
||||
* **Presence Sensor** - device tells presence with enum - {present, not present}
|
||||
* **Sensor** - device has attributes
|
||||
* **Battery** - defines device uses a battery
|
||||
* **Configuration** - _configure()_ command called when device is installed or device preferences updated
|
||||
* **Health Check** - indicates ability to get device health notifications
|
||||
|
||||
|
||||
## Device Health
|
||||
|
||||
Arrival Sensor ZigBee is an untracked device. Sends broadcast of battery level every 20 seconds.
|
||||
Disconnects when Hub goes OFFLINE.
|
||||
|
||||
|
||||
## Battery
|
||||
|
||||
Uses 1 CR2032 Battery
|
||||
|
||||
* [Changing the Battery](https://support.smartthings.com/hc/en-us/articles/200907400-How-to-change-the-battery-in-the-SmartSense-Presence-Sensor-and-Samsung-SmartThings-Arrival-Sensor)
|
||||
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the arrival sensor is out of range.
|
||||
Pairing needs to be tried again by placing the sensor closer to the hub.
|
||||
|
||||
* [Samsung SmartThings Arrival Sensor Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/205382134-Samsung-SmartThings-Arrival-Sensor-2015-model-)
|
||||
|
||||
If the arrival sensor doesn't update its status, here are a few things you can try to debug.
|
||||
|
||||
* [Troubleshooting: Samsung SmartThings Arrival Sensor won't update its status](https://support.smartthings.com/hc/en-us/articles/200846514-Troubleshooting-Samsung-SmartThings-Arrival-Sensor-won-t-update-its-status)
|
||||
@@ -1,7 +1,5 @@
|
||||
import groovy.json.JsonOutput
|
||||
|
||||
/**
|
||||
* Copyright 2017 SmartThings
|
||||
* Copyright 2016 SmartThings
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at:
|
||||
@@ -21,7 +19,6 @@ metadata {
|
||||
capability "Sensor"
|
||||
capability "Battery"
|
||||
capability "Configuration"
|
||||
capability "Health Check"
|
||||
|
||||
fingerprint inClusters: "0000,0001,0003,000F,0020", outClusters: "0003,0019",
|
||||
manufacturer: "SmartThings", model: "tagv4", deviceJoinName: "Arrival Sensor"
|
||||
@@ -61,13 +58,8 @@ def updated() {
|
||||
startTimer()
|
||||
}
|
||||
|
||||
def installed() {
|
||||
// Arrival sensors only goes OFFLINE when Hub is off
|
||||
sendEvent(name: "DeviceWatch-Enroll", value: JsonOutput.toJson([protocol: "zigbee", scheme:"untracked"]), displayed: false)
|
||||
}
|
||||
|
||||
def configure() {
|
||||
def cmds = zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020) + zigbee.batteryConfig(20, 20, 0x01)
|
||||
def cmds = zigbee.batteryConfig(20, 20, 0x01)
|
||||
log.debug "configure -- cmds: ${cmds}"
|
||||
return cmds
|
||||
}
|
||||
@@ -174,4 +166,4 @@ def checkPresenceCallback() {
|
||||
if (timeSinceLastCheckin >= theCheckInterval) {
|
||||
handlePresenceEvent(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
.st-ignore
|
||||
README.md
|
||||
@@ -1,49 +0,0 @@
|
||||
# Arrival Sensor (2015 Model)
|
||||
|
||||
Cloud Execution
|
||||
|
||||
Works with:
|
||||
|
||||
* [Arrival Sensor](https://www.smartthings.com/products/samsung-smartthings-arrival-sensor)
|
||||
|
||||
## Table of contents
|
||||
|
||||
* [Capabilities](#capabilities)
|
||||
* [Health](#device-health)
|
||||
* [Battery](#battery)
|
||||
* [Troubleshooting](#troubleshooting)
|
||||
|
||||
|
||||
## Capabilities
|
||||
|
||||
* **Tone** - beep command to allow an audible tone
|
||||
* **Actuator** - device has commands
|
||||
* **Signal Strength** - device can read the strength of signal- lqi: Link Quality Indication, rssi: Received Signal Strength Indication
|
||||
* **Presence Sensor** - device tells presence with enum - {present, not present}
|
||||
* **Sensor** - device has attributes
|
||||
* **Battery** - defines device uses a battery
|
||||
* **Health Check** - indicates ability to get device health notifications
|
||||
|
||||
|
||||
## Device Health
|
||||
|
||||
Arrival Sensor ZigBee is an untracked device. Disconnects when Hub goes OFFLINE.
|
||||
|
||||
|
||||
## Battery
|
||||
|
||||
Uses 1 CR2032 Battery
|
||||
|
||||
* [Changing the Battery](https://support.smartthings.com/hc/en-us/articles/200907400-How-to-change-the-battery-in-the-SmartSense-Presence-Sensor-and-Samsung-SmartThings-Arrival-Sensor)
|
||||
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the arrival sensor is out of range.
|
||||
Pairing needs to be tried again by placing the sensor closer to the hub.
|
||||
|
||||
* [Samsung SmartThings Arrival Sensor Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/205382134-Samsung-SmartThings-Arrival-Sensor-2015-model-)
|
||||
|
||||
If the arrival sensor doesn't update its status, here are a few things you can try to debug.
|
||||
|
||||
* [Troubleshooting: Samsung SmartThings Arrival Sensor won't update its status](https://support.smartthings.com/hc/en-us/articles/200846514-Troubleshooting-Samsung-SmartThings-Arrival-Sensor-won-t-update-its-status)
|
||||
@@ -1,5 +1,3 @@
|
||||
import groovy.json.JsonOutput
|
||||
|
||||
/**
|
||||
* Copyright 2015 SmartThings
|
||||
*
|
||||
@@ -21,7 +19,6 @@ metadata {
|
||||
capability "Presence Sensor"
|
||||
capability "Sensor"
|
||||
capability "Battery"
|
||||
capability "Health Check"
|
||||
|
||||
fingerprint profileId: "FC01", deviceId: "019A"
|
||||
fingerprint profileId: "FC01", deviceId: "0131", inClusters: "0000,0003", outClusters: "0003"
|
||||
@@ -114,11 +111,6 @@ def beep() {
|
||||
]
|
||||
}
|
||||
|
||||
def installed() {
|
||||
// Arrival sensors only goes OFFLINE when Hub is off
|
||||
sendEvent(name: "DeviceWatch-Enroll", value: JsonOutput.toJson([protocol: "zigbee", scheme:"untracked"]), displayed: false)
|
||||
}
|
||||
|
||||
def parse(String description) {
|
||||
def results
|
||||
if (isBatteryMessage(description)) {
|
||||
|
||||
@@ -9,7 +9,6 @@ metadata {
|
||||
capability "Configuration"
|
||||
capability "Refresh"
|
||||
capability "Sensor"
|
||||
capability "Health Check"
|
||||
|
||||
attribute "thermostatFanState", "string"
|
||||
|
||||
@@ -19,7 +18,6 @@ metadata {
|
||||
command "quickSetHeat"
|
||||
|
||||
fingerprint deviceId: "0x08", inClusters: "0x43,0x40,0x44,0x31,0x80,0x85,0x60"
|
||||
fingerprint mfr:"0098", prod:"6401", model:"0107", deviceJoinName: "2Gig CT100 Programmable Thermostat"
|
||||
}
|
||||
|
||||
// simulator metadata
|
||||
@@ -108,16 +106,6 @@ metadata {
|
||||
}
|
||||
}
|
||||
|
||||
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])
|
||||
}
|
||||
|
||||
def installed() {
|
||||
// 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])
|
||||
}
|
||||
|
||||
def parse(String description)
|
||||
{
|
||||
def result = []
|
||||
@@ -451,14 +439,6 @@ def setCoolingSetpoint(Double degrees, Integer delay = 30000) {
|
||||
], delay)
|
||||
}
|
||||
|
||||
/**
|
||||
* PING is used by Device-Watch in attempt to reach the Device
|
||||
* */
|
||||
def ping() {
|
||||
log.debug "ping() called"
|
||||
refresh()
|
||||
}
|
||||
|
||||
def configure() {
|
||||
delayBetween([
|
||||
zwave.thermostatModeV2.thermostatModeSupportedGet().format(),
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
*
|
||||
*/
|
||||
metadata {
|
||||
definition (name: "Dimmer Switch", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "oic.d.light") {
|
||||
definition (name: "Dimmer Switch", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "oic.d.switch") {
|
||||
capability "Switch Level"
|
||||
capability "Actuator"
|
||||
capability "Indicator"
|
||||
@@ -23,9 +23,9 @@ metadata {
|
||||
capability "Health Check"
|
||||
capability "Light"
|
||||
|
||||
fingerprint mfr:"0063", prod:"4457", deviceJoinName: "GE In-Wall Smart Dimmer"
|
||||
fingerprint mfr:"0063", prod:"4944", deviceJoinName: "GE In-Wall Smart Dimmer"
|
||||
fingerprint mfr:"0063", prod:"5044", deviceJoinName: "GE Plug-In Smart Dimmer"
|
||||
fingerprint mfr:"0063", prod:"4457", deviceJoinName: "GE In-Wall Smart Dimmer "
|
||||
fingerprint mfr:"0063", prod:"4944", deviceJoinName: "GE In-Wall Smart Dimmer "
|
||||
fingerprint mfr:"0063", prod:"5044", deviceJoinName: "GE Plug-In Smart Dimmer "
|
||||
fingerprint mfr:"0063", prod:"4944", model:"3034", deviceJoinName: "GE In-Wall Smart Fan Control"
|
||||
}
|
||||
|
||||
|
||||
@@ -1,163 +0,0 @@
|
||||
/**
|
||||
* Copyright 2017 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.
|
||||
*
|
||||
* Everspring ST815 Illuminance Sensor
|
||||
*
|
||||
* Author: SmartThings
|
||||
* Date: 2017-3-4
|
||||
*/
|
||||
|
||||
metadata {
|
||||
definition (name: "Everspring Illuminance Sensor", namespace: "smartthings", author: "SmartThings") {
|
||||
capability "Illuminance Measurement"
|
||||
capability "Battery"
|
||||
capability "Configuration"
|
||||
capability "Sensor"
|
||||
capability "Health Check"
|
||||
|
||||
fingerprint mfr:"0060", prod:"0007", model:"0001"
|
||||
}
|
||||
|
||||
simulator {
|
||||
for( int i = 0; i <= 100; i += 20 ) {
|
||||
status "illuminace ${i} lux": new physicalgraph.zwave.Zwave().sensorMultilevelV2.sensorMultilevelReport(
|
||||
scaledSensorValue: i, precision: 0, sensorType: 3, scale: 1).incomingMessage()
|
||||
}
|
||||
|
||||
for( int i = 0; i <= 100; i += 20 ) {
|
||||
status "battery ${i}%": new physicalgraph.zwave.Zwave().batteryV1.batteryReport(
|
||||
batteryLevel: i).incomingMessage()
|
||||
}
|
||||
|
||||
status "wakeup": "command: 8407, payload: "
|
||||
}
|
||||
|
||||
tiles(scale: 2) {
|
||||
valueTile("temperature", "device.temperature", inactiveLabel: false, width: 2, height: 2) {
|
||||
state "temperature", label:'${currentValue}°',
|
||||
backgroundColors:[
|
||||
[value: 32, color: "#153591"],
|
||||
[value: 44, color: "#1e9cbb"],
|
||||
[value: 59, color: "#90d2a7"],
|
||||
[value: 74, color: "#44b621"],
|
||||
[value: 84, color: "#f1d801"],
|
||||
[value: 92, color: "#d04e00"],
|
||||
[value: 98, color: "#bc2323"]
|
||||
]
|
||||
}
|
||||
valueTile("humidity", "device.humidity", inactiveLabel: false, width: 2, height: 2) {
|
||||
state "humidity", label:'${currentValue}% humidity', unit:""
|
||||
}
|
||||
valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
|
||||
state "battery", label:'${currentValue}% battery', unit:""
|
||||
}
|
||||
|
||||
main( ["temperature", "humidity"] )
|
||||
details( ["temperature", "humidity", "battery"] )
|
||||
}
|
||||
}
|
||||
|
||||
def updated() {
|
||||
state.configured = false
|
||||
}
|
||||
|
||||
def parse(String description) {
|
||||
def result = []
|
||||
|
||||
def cmd = zwave.parse(description, [0x20: 1, 0x31: 2, 0x70: 1, 0x71: 1, 0x80: 1, 0x84: 2, 0x85: 2])
|
||||
|
||||
if (cmd) {
|
||||
result = zwaveEvent(cmd)
|
||||
}
|
||||
|
||||
if (result instanceof List) {
|
||||
log.debug "Parsed '$description' to ${result.collect { it.respondsTo("toHubAction") ? it.toHubAction() : it }}"
|
||||
} else {
|
||||
log.debug "Parsed '$description' to ${result}"
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpNotification cmd) {
|
||||
def result = [
|
||||
createEvent(descriptionText: "${device.displayName} woke up", isStateChange: false)
|
||||
]
|
||||
if (state.configured) {
|
||||
result << response(zwave.batteryV1.batteryGet())
|
||||
} else {
|
||||
result << response(configure())
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.alarmv1.AlarmReport cmd) {
|
||||
if (cmd.alarmType == 1 && cmd.alarmType == 0xFF) {
|
||||
return createEvent(descriptionText: "${device.displayName} battery is low", isStateChange: true)
|
||||
} else if (cmd.alarmType == 2 && cmd.alarmLevel == 1) {
|
||||
return createEvent(descriptionText: "${device.displayName} powered up", isStateChange: false)
|
||||
}
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv2.SensorMultilevelReport cmd) {
|
||||
|
||||
def map = [:]
|
||||
switch( cmd.sensorType ) {
|
||||
case 3:
|
||||
// luminance
|
||||
map.value = cmd.scaledSensorValue.toInteger().toString()
|
||||
map.unit = "lux"
|
||||
map.name = "illuminance"
|
||||
break;
|
||||
}
|
||||
|
||||
return createEvent(map)
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
|
||||
def map = [ name: "battery", unit: "%" ]
|
||||
if (cmd.batteryLevel == 0xFF) {
|
||||
map.value = 1
|
||||
map.descriptionText = "${device.displayName} has a low battery"
|
||||
map.isStateChange = true
|
||||
} else {
|
||||
map.value = cmd.batteryLevel
|
||||
}
|
||||
|
||||
def response_cmds = []
|
||||
if (!currentTemperature) {
|
||||
response_cmds << zwave.sensorMultilevelV2.sensorMultilevelGet().format()
|
||||
response_cmds << "delay 1000"
|
||||
}
|
||||
response_cmds << zwave.wakeUpV1.wakeUpNoMoreInformation().format()
|
||||
|
||||
return [createEvent(map), response(response_cmds)]
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.Command cmd) {
|
||||
log.debug "Unhandled: ${cmd.toString()}"
|
||||
return [:]
|
||||
}
|
||||
|
||||
def configure() {
|
||||
state.configured = true
|
||||
sendEvent(name: "checkInterval", value: 8 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
||||
delayBetween([
|
||||
// Auto report time interval in minutes
|
||||
zwave.configurationV1.configurationSet(parameterNumber: 5, size: 2, scaledConfigurationValue: 20).format(),
|
||||
|
||||
// Auto report lux change threshold
|
||||
zwave.configurationV1.configurationSet(parameterNumber: 6, size: 2, scaledConfigurationValue: 30).format(),
|
||||
|
||||
// Get battery – report triggers WakeUpNMI
|
||||
zwave.batteryV1.batteryGet().format()
|
||||
])
|
||||
}
|
||||
@@ -1,188 +0,0 @@
|
||||
/**
|
||||
* Copyright 2017 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.
|
||||
*
|
||||
* Everspring ST814 Temperature/Humidity Sensor
|
||||
*
|
||||
* Author: SmartThings
|
||||
* Date: 2017-3-4
|
||||
*/
|
||||
|
||||
metadata {
|
||||
definition (name: "Everspring ST814", namespace: "smartthings", author: "SmartThings") {
|
||||
capability "Temperature Measurement"
|
||||
capability "Relative Humidity Measurement"
|
||||
capability "Battery"
|
||||
capability "Configuration"
|
||||
capability "Sensor"
|
||||
capability "Health Check"
|
||||
|
||||
fingerprint mfr:"0060", prod:"0006", model:"0001"
|
||||
}
|
||||
|
||||
simulator {
|
||||
for( int i = 0; i <= 100; i += 20 ) {
|
||||
status "temperature ${i}F": new physicalgraph.zwave.Zwave().sensorMultilevelV2.sensorMultilevelReport(
|
||||
scaledSensorValue: i, precision: 1, sensorType: 1, scale: 1).incomingMessage()
|
||||
}
|
||||
|
||||
for( int i = 0; i <= 100; i += 20 ) {
|
||||
status "humidity ${i}%": new physicalgraph.zwave.Zwave().sensorMultilevelV2.sensorMultilevelReport(
|
||||
scaledSensorValue: i, precision: 0, sensorType: 5).incomingMessage()
|
||||
}
|
||||
|
||||
for( int i = 0; i <= 100; i += 20 ) {
|
||||
status "battery ${i}%": new physicalgraph.zwave.Zwave().batteryV1.batteryReport(
|
||||
batteryLevel: i).incomingMessage()
|
||||
}
|
||||
status "wakeup": "command: 8407, payload: "
|
||||
}
|
||||
|
||||
tiles(scale: 2) {
|
||||
valueTile("temperature", "device.temperature", inactiveLabel: false, width: 2, height: 2) {
|
||||
state "temperature", label:'${currentValue}°',
|
||||
backgroundColors:[
|
||||
[value: 32, color: "#153591"],
|
||||
[value: 44, color: "#1e9cbb"],
|
||||
[value: 59, color: "#90d2a7"],
|
||||
[value: 74, color: "#44b621"],
|
||||
[value: 84, color: "#f1d801"],
|
||||
[value: 92, color: "#d04e00"],
|
||||
[value: 98, color: "#bc2323"]
|
||||
]
|
||||
}
|
||||
valueTile("humidity", "device.humidity", inactiveLabel: false, width: 2, height: 2) {
|
||||
state "humidity", label:'${currentValue}% humidity', unit:""
|
||||
}
|
||||
valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
|
||||
state "battery", label:'${currentValue}% battery', unit:""
|
||||
}
|
||||
|
||||
main( ["temperature", "humidity"] )
|
||||
details( ["temperature", "humidity", "battery"] )
|
||||
}
|
||||
}
|
||||
|
||||
def updated() {
|
||||
state.configured = false
|
||||
}
|
||||
|
||||
def parse(String description) {
|
||||
def result = []
|
||||
|
||||
def cmd = zwave.parse(description, [0x20: 1, 0x31: 2, 0x70: 1, 0x71: 1, 0x80: 1, 0x84: 2, 0x85: 2])
|
||||
|
||||
if (cmd) {
|
||||
result = zwaveEvent(cmd)
|
||||
}
|
||||
|
||||
if (result instanceof List) {
|
||||
log.debug "Parsed '$description' to ${result.collect { it.respondsTo("toHubAction") ? it.toHubAction() : it }}"
|
||||
} else {
|
||||
log.debug "Parsed '$description' to ${result}"
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpNotification cmd) {
|
||||
def result = [
|
||||
createEvent(descriptionText: "${device.displayName} woke up", isStateChange: false)
|
||||
]
|
||||
if (state.configured) {
|
||||
result << response(zwave.batteryV1.batteryGet())
|
||||
} else {
|
||||
result << response(configure())
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.alarmv1.AlarmReport cmd) {
|
||||
if (cmd.alarmType == 1 && cmd.alarmType == 0xFF) {
|
||||
return createEvent(descriptionText: "${device.displayName} battery is low", isStateChange: true)
|
||||
} else if (cmd.alarmType == 2 && cmd.alarmLevel == 1) {
|
||||
return createEvent(descriptionText: "${device.displayName} powered up", isStateChange: false)
|
||||
}
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv2.SensorMultilevelReport cmd) {
|
||||
|
||||
def map = [:]
|
||||
switch( cmd.sensorType ) {
|
||||
case 1:
|
||||
/* temperature */
|
||||
def cmdScale = cmd.scale == 1 ? "F" : "C"
|
||||
map.value = convertTemperatureIfNeeded(cmd.scaledSensorValue, cmdScale, cmd.precision)
|
||||
map.unit = getTemperatureScale()
|
||||
map.name = "temperature"
|
||||
break
|
||||
case 5:
|
||||
/* humidity */
|
||||
map.value = cmd.scaledSensorValue.toInteger().toString()
|
||||
map.unit = "%"
|
||||
map.name = "humidity"
|
||||
break
|
||||
}
|
||||
|
||||
return createEvent(map)
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
|
||||
def map = [ name: "battery", unit: "%" ]
|
||||
if (cmd.batteryLevel == 0xFF) {
|
||||
map.value = 1
|
||||
map.descriptionText = "${device.displayName} has a low battery"
|
||||
map.isStateChange = true
|
||||
} else {
|
||||
map.value = cmd.batteryLevel
|
||||
}
|
||||
|
||||
def response_cmds = []
|
||||
if (!currentTemperature) {
|
||||
response_cmds << zwave.sensorMultilevelV2.sensorMultilevelGet().format()
|
||||
response_cmds << "delay 1000"
|
||||
}
|
||||
response_cmds << zwave.wakeUpV1.wakeUpNoMoreInformation().format()
|
||||
|
||||
return [createEvent(map), response(response_cmds)]
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd) {
|
||||
def result = null
|
||||
def encapsulatedCommand = cmd.encapsulatedCommand([0x20: 1, 0x31: 2, 0x70: 1, 0x71: 1, 0x80: 1, 0x84: 2, 0x85: 2])
|
||||
log.debug ("Command from endpoint ${cmd.sourceEndPoint}: ${encapsulatedCommand}")
|
||||
if (encapsulatedCommand) {
|
||||
result = zwaveEvent(encapsulatedCommand)
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.Command cmd) {
|
||||
log.debug "Unhandled: ${cmd.toString()}"
|
||||
return [:]
|
||||
}
|
||||
|
||||
def configure() {
|
||||
state.configured = true
|
||||
sendEvent(name: "checkInterval", value: 8 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
||||
delayBetween([
|
||||
// Auto report time interval in minutes
|
||||
zwave.configurationV1.configurationSet(parameterNumber: 6, size: 2, scaledConfigurationValue: 20).format(),
|
||||
|
||||
// Auto report temperature change threshold
|
||||
zwave.configurationV1.configurationSet(parameterNumber: 7, size: 1, scaledConfigurationValue: 2).format(),
|
||||
|
||||
// Auto report humidity change threshold
|
||||
zwave.configurationV1.configurationSet(parameterNumber: 8, size: 1, scaledConfigurationValue: 5).format(),
|
||||
|
||||
// Get battery – report triggers WakeUpNMI
|
||||
zwave.batteryV1.batteryGet().format()
|
||||
])
|
||||
}
|
||||
@@ -68,8 +68,8 @@
|
||||
|
||||
tiles {
|
||||
standardTile("contact", "device.contact", width: 2, height: 2) {
|
||||
state "open", label: '${name}', icon: "st.contact.contact.open", backgroundColor: "#e86d13"
|
||||
state "closed", label: '${name}', icon: "st.contact.contact.closed", backgroundColor: "#00A0DC"
|
||||
state "open", label: '${name}', icon: "st.contact.contact.open", backgroundColor: "#ffa81e"
|
||||
state "closed", label: '${name}', icon: "st.contact.contact.closed", backgroundColor: "#79b821"
|
||||
}
|
||||
valueTile("temperature", "device.temperature", inactiveLabel: false) {
|
||||
state "temperature", label:'${currentValue}°',
|
||||
@@ -86,7 +86,7 @@
|
||||
}
|
||||
standardTile("tamper", "device.alarm") {
|
||||
state("secure", label:'secure', icon:"st.locks.lock.locked", backgroundColor:"#ffffff")
|
||||
state("tampered", label:'tampered', icon:"st.locks.lock.unlocked", backgroundColor:"#00a0dc")
|
||||
state("tampered", label:'tampered', icon:"st.locks.lock.unlocked", backgroundColor:"#53a7c0")
|
||||
}
|
||||
valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat") {
|
||||
state "battery", label:'${currentValue}% battery', unit:""
|
||||
|
||||
@@ -107,8 +107,8 @@
|
||||
state "configure", label:'', action:"configuration.configure", icon:"st.secondary.configure"
|
||||
}
|
||||
standardTile("acceleration", "device.acceleration") {
|
||||
state("active", label:'vibration', icon:"st.motion.acceleration.active", backgroundColor:"#00a0dc")
|
||||
state("inactive", label:'still', icon:"st.motion.acceleration.inactive", backgroundColor:"#cccccc")
|
||||
state("active", label:'vibration', icon:"st.motion.acceleration.active", backgroundColor:"#53a7c0")
|
||||
state("inactive", label:'still', icon:"st.motion.acceleration.inactive", backgroundColor:"#ffffff")
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -12,13 +12,13 @@
|
||||
*
|
||||
*/
|
||||
metadata {
|
||||
definition (name: "Fortrezz Water Valve", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "oic.d.watervalve") {
|
||||
definition (name: "Fortrezz Water Valve", namespace: "smartthings", author: "SmartThings") {
|
||||
capability "Actuator"
|
||||
capability "Health Check"
|
||||
capability "Valve"
|
||||
capability "Refresh"
|
||||
capability "Sensor"
|
||||
|
||||
|
||||
fingerprint deviceId: "0x1000", inClusters: "0x25,0x72,0x86,0x71,0x22,0x70"
|
||||
fingerprint mfr:"0084", prod:"0213", model:"0215", deviceJoinName: "FortrezZ Water Valve"
|
||||
}
|
||||
@@ -34,22 +34,19 @@ metadata {
|
||||
}
|
||||
|
||||
// tile definitions
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name:"valve", type: "generic", width: 6, height: 4, canChangeIcon: true){
|
||||
tileAttribute ("device.valve", key: "PRIMARY_CONTROL") {
|
||||
attributeState "open", label: '${name}', action: "valve.close", icon: "st.valves.water.open", backgroundColor: "#00A0DC", nextState:"closing"
|
||||
attributeState "closed", label: '${name}', action: "valve.open", icon: "st.valves.water.closed", backgroundColor: "#ffffff", nextState:"opening"
|
||||
attributeState "opening", label: '${name}', action: "valve.close", icon: "st.valves.water.open", backgroundColor: "#00A0DC"
|
||||
attributeState "closing", label: '${name}', action: "valve.open", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
|
||||
}
|
||||
tiles {
|
||||
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 "closed", label: '${name}', action: "valve.open", icon: "st.valves.water.closed", backgroundColor: "#ffffff", nextState:"opening"
|
||||
state "opening", label: '${name}', action: "valve.close", icon: "st.valves.water.open", backgroundColor: "#00A0DC"
|
||||
state "closing", label: '${name}', action: "valve.open", icon: "st.valves.water.closed", backgroundColor: "#ffffff"
|
||||
}
|
||||
|
||||
standardTile("refresh", "device.valve", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
|
||||
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") {
|
||||
state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
|
||||
}
|
||||
|
||||
main "valve"
|
||||
details(["valve","refresh"])
|
||||
main "contact"
|
||||
details(["contact","refresh"])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,23 +62,22 @@ def updated(){
|
||||
|
||||
def parse(String description) {
|
||||
log.trace description
|
||||
def result = null
|
||||
def cmd = zwave.parse(description)
|
||||
if (cmd) {
|
||||
return zwaveEvent(cmd)
|
||||
result = createEvent(zwaveEvent(cmd))
|
||||
}
|
||||
log.debug "Could not parse message"
|
||||
return null
|
||||
log.debug "Parse returned ${result?.descriptionText}"
|
||||
return result
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd) {
|
||||
def value = cmd.value ? "closed" : "open"
|
||||
|
||||
return [createEventWithDebug([name: "contact", value: value, descriptionText: "$device.displayName valve is $value"]),
|
||||
createEventWithDebug([name: "valve", value: value, descriptionText: "$device.displayName valve is $value"])]
|
||||
[name: "contact", value: value, descriptionText: "$device.displayName valve is $value"]
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.Command cmd) {
|
||||
return createEvent([:]) // Handles all Z-Wave commands we aren't interested in
|
||||
[:] // Handles all Z-Wave commands we aren't interested in
|
||||
}
|
||||
|
||||
def open() {
|
||||
@@ -102,9 +98,3 @@ def ping() {
|
||||
def refresh() {
|
||||
zwave.switchBinaryV1.switchBinaryGet().format()
|
||||
}
|
||||
|
||||
def createEventWithDebug(eventMap) {
|
||||
def event = createEvent(eventMap)
|
||||
log.debug "Event created with ${event?.descriptionText}"
|
||||
return event
|
||||
}
|
||||
|
||||
@@ -62,8 +62,8 @@ metadata {
|
||||
state "off", label: '${name}', action: "on", icon: "st.switches.switch.off", backgroundColor: "#ffffff"
|
||||
}
|
||||
standardTile("contact", "device.contact", inactiveLabel: false) {
|
||||
state "open", label: '${name}', icon: "st.contact.contact.open", backgroundColor: "#e86d13"
|
||||
state "closed", label: '${name}', icon: "st.contact.contact.closed", backgroundColor: "#00A0DC"
|
||||
state "open", label: '${name}', icon: "st.contact.contact.open", backgroundColor: "#ffa81e"
|
||||
state "closed", label: '${name}', icon: "st.contact.contact.closed", backgroundColor: "#79b821"
|
||||
}
|
||||
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") {
|
||||
state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
|
||||
|
||||
@@ -21,7 +21,7 @@ Works with:
|
||||
|
||||
## Device Health
|
||||
|
||||
Plant Link sensor is a ZigBee sleepy device and checks in every 15 minutes.
|
||||
Plant Link sensor is a Z-wave sleepy device and checks in every 15 minutes.
|
||||
Device-Watch allows 2 check-in misses from device plus some lag time. So Check-in interval = (2*15 + 2)mins = 32 mins.
|
||||
|
||||
* __32min__ checkInterval
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
.st-ignore
|
||||
README.md
|
||||
@@ -1,39 +0,0 @@
|
||||
# Smartalert Siren
|
||||
|
||||
Cloud Execution
|
||||
|
||||
Works with:
|
||||
|
||||
* [FortrezZ Siren Strobe Alarm](https://www.smartthings.com/products/fortrezz-siren-strobe-alarm)
|
||||
|
||||
## Table of contents
|
||||
|
||||
* [Capabilities](#capabilities)
|
||||
* [Health](#device-health)
|
||||
* [Troubleshooting](#troubleshooting)
|
||||
|
||||
## Capabilities
|
||||
|
||||
* **Actuator** - represents that a Device has commands
|
||||
* **Switch** - can detect state (possible values: on/off)
|
||||
* **Sensor** - detects sensor events
|
||||
* **Alarm** - allows for interacting with devices that serve as alarms
|
||||
* **Health Check** - indicates ability to get device health notifications
|
||||
|
||||
## Device Health
|
||||
|
||||
FortrezZ Siren Strobe Alarm is 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.
|
||||
Device-Watch allows 2 check-in misses from device plus some lag time. So Check-in interval = (2*15 + 2)mins = 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:
|
||||
* [FortrezZ Siren Strobe Alarm Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/202294760-FortrezZ-Siren-Strobe-Alarm)
|
||||
@@ -21,12 +21,10 @@ metadata {
|
||||
capability "Switch"
|
||||
capability "Sensor"
|
||||
capability "Alarm"
|
||||
capability "Health Check"
|
||||
|
||||
command "test"
|
||||
|
||||
fingerprint deviceId: "0x1100", inClusters: "0x26,0x71"
|
||||
fingerprint mfr:"0084", prod:"0313", model:"010B", deviceJoinName: "FortrezZ Siren Strobe Alarm"
|
||||
}
|
||||
|
||||
simulator {
|
||||
@@ -70,16 +68,6 @@ metadata {
|
||||
}
|
||||
}
|
||||
|
||||
def installed() {
|
||||
// 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])
|
||||
}
|
||||
|
||||
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])
|
||||
}
|
||||
|
||||
def on() {
|
||||
[
|
||||
zwave.basicV1.basicSet(value: 0xFF).format(),
|
||||
@@ -161,10 +149,3 @@ def createEvents(physicalgraph.zwave.commands.basicv1.BasicReport cmd)
|
||||
def zwaveEvent(physicalgraph.zwave.Command cmd) {
|
||||
log.warn "UNEXPECTED COMMAND: $cmd"
|
||||
}
|
||||
|
||||
/**
|
||||
* PING is used by Device-Watch in attempt to reach the Device
|
||||
* */
|
||||
def ping() {
|
||||
secure(zwave.basicV1.basicGet())
|
||||
}
|
||||
@@ -44,9 +44,9 @@ metadata {
|
||||
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:"#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 "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"
|
||||
}
|
||||
tileAttribute ("device.level", key: "SLIDER_CONTROL") {
|
||||
|
||||
@@ -29,7 +29,6 @@ metadata {
|
||||
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: "4257050-RZHAC", deviceJoinName: "Outlet"
|
||||
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 000F, 0B04", outClusters: "0019", manufacturer: "SmartThings", model: "outletv4", deviceJoinName: "Outlet"
|
||||
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0B04,0B05", outClusters: "0019"
|
||||
}
|
||||
|
||||
|
||||
@@ -56,8 +56,8 @@ metadata {
|
||||
state("closing", label:'${name}', icon:"st.doors.garage.garage-closing", backgroundColor:"#00A0DC")
|
||||
}
|
||||
standardTile("contact", "device.contact") {
|
||||
state("open", label:'${name}', icon:"st.contact.contact.open", backgroundColor:"#e86d13")
|
||||
state("closed", label:'${name}', icon:"st.contact.contact.closed", backgroundColor:"#00A0DC")
|
||||
state("open", label:'${name}', icon:"st.contact.contact.open", backgroundColor:"#ffa81e")
|
||||
state("closed", label:'${name}', icon:"st.contact.contact.closed", backgroundColor:"#79b821")
|
||||
}
|
||||
standardTile("acceleration", "device.acceleration", decoration: "flat") {
|
||||
state("active", label:'${name}', icon:"st.motion.acceleration.active", backgroundColor:"#00A0DC")
|
||||
|
||||
@@ -61,8 +61,8 @@ metadata {
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name: "motion", type: "generic", width: 6, height: 4) {
|
||||
tileAttribute("device.motion", key: "PRIMARY_CONTROL") {
|
||||
attributeState "active", label: 'motion', icon: "st.motion.motion.active", backgroundColor: "#00A0DC"
|
||||
attributeState "inactive", label: 'no motion', icon: "st.motion.motion.inactive", backgroundColor: "#cccccc"
|
||||
attributeState "active", label: 'motion', icon: "st.motion.motion.active", backgroundColor: "#53a7c0"
|
||||
attributeState "inactive", label: 'no motion', icon: "st.motion.motion.inactive", backgroundColor: "#ffffff"
|
||||
}
|
||||
}
|
||||
valueTile("temperature", "device.temperature", width: 2, height: 2) {
|
||||
|
||||
@@ -33,6 +33,7 @@ metadata {
|
||||
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05,FC02", outClusters: "0019", manufacturer: "CentraLite", model: "3320"
|
||||
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05,FC02", outClusters: "0019", manufacturer: "CentraLite", model: "3321"
|
||||
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05,FC02", outClusters: "0019", manufacturer: "CentraLite", model: "3321-S", deviceJoinName: "Multipurpose Sensor"
|
||||
fingerprint inClusters: "0000,0001,0003,0020,0402,0500,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3323-G", deviceJoinName: "Centralite Micro Door Sensor"
|
||||
fingerprint inClusters: "0000,0001,0003,000F,0020,0402,0500,FC02", outClusters: "0019", manufacturer: "SmartThings", model: "multiv4", deviceJoinName: "Multipurpose Sensor"
|
||||
|
||||
attribute "status", "string"
|
||||
@@ -87,8 +88,8 @@ metadata {
|
||||
state("closed", label: 'Closed', icon: "st.contact.contact.closed", backgroundColor: "#00a0dc")
|
||||
}
|
||||
standardTile("acceleration", "device.acceleration", width: 2, height: 2) {
|
||||
state("active", label: 'Active', icon: "st.motion.acceleration.active", backgroundColor: "#00a0dc")
|
||||
state("inactive", label: 'Inactive', icon: "st.motion.acceleration.inactive", backgroundColor: "#cccccc")
|
||||
state("active", label: 'Active', icon: "st.motion.acceleration.active", backgroundColor: "#53a7c0")
|
||||
state("inactive", label: 'Inactive', icon: "st.motion.acceleration.inactive", backgroundColor: "#ffffff")
|
||||
}
|
||||
valueTile("temperature", "device.temperature", width: 2, height: 2) {
|
||||
state("temperature", label: '${currentValue}°',
|
||||
@@ -178,7 +179,7 @@ private List<Map> handleAcceleration(descMap) {
|
||||
result += parseAxis(descMap.additionalAttrs)
|
||||
}
|
||||
} else if (descMap.clusterInt == 0xFC02 && descMap.attrInt == 0x0012) {
|
||||
def addAttrs = descMap.additionalAttrs ?: []
|
||||
def addAttrs = descMap.additionalAttrs
|
||||
addAttrs << ["attrInt": descMap.attrInt, "value": descMap.value]
|
||||
result += parseAxis(addAttrs)
|
||||
}
|
||||
@@ -191,10 +192,6 @@ private List<Map> parseAxis(List<Map> attrData) {
|
||||
def y = hexToSignedInt(attrData.find { it.attrInt == 0x0013 }?.value)
|
||||
def z = hexToSignedInt(attrData.find { it.attrInt == 0x0014 }?.value)
|
||||
|
||||
if ([x, y ,z].any { it == null }) {
|
||||
return []
|
||||
}
|
||||
|
||||
def xyzResults = [:]
|
||||
if (device.getDataValue("manufacturer") == "SmartThings") {
|
||||
// This mapping matches the current behavior of the Device Handler for the Centralite sensors
|
||||
@@ -375,10 +372,6 @@ def updated() {
|
||||
}
|
||||
|
||||
private hexToSignedInt(hexVal) {
|
||||
if (!hexVal) {
|
||||
return null
|
||||
}
|
||||
|
||||
def unsignedVal = hexToInt(hexVal)
|
||||
unsignedVal > 32767 ? unsignedVal - 65536 : unsignedVal
|
||||
}
|
||||
|
||||
@@ -31,7 +31,6 @@ metadata {
|
||||
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3300-S"
|
||||
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3300"
|
||||
fingerprint inClusters: "0000,0001,0003,0020,0402,0500,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3320-L", deviceJoinName: "Iris Contact Sensor"
|
||||
fingerprint inClusters: "0000,0001,0003,0020,0402,0500,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3323-G", deviceJoinName: "Centralite Micro Door Sensor"
|
||||
}
|
||||
|
||||
simulator {
|
||||
|
||||
@@ -22,16 +22,16 @@ metadata {
|
||||
|
||||
tiles {
|
||||
standardTile("contact", "device.contact", width: 2, height: 2) {
|
||||
state("closed", label:'${name}', icon:"st.contact.contact.closed", backgroundColor:"#00A0DC", action: "open")
|
||||
state("open", label:'${name}', icon:"st.contact.contact.open", backgroundColor:"#e86d13", action: "close")
|
||||
state("closed", label:'${name}', icon:"st.contact.contact.closed", backgroundColor:"#79b821", action: "open")
|
||||
state("open", label:'${name}', icon:"st.contact.contact.open", backgroundColor:"#ffa81e", action: "close")
|
||||
}
|
||||
standardTile("freezerDoor", "device.contact", width: 2, height: 2, decoration: "flat") {
|
||||
state("closed", label:'Freezer', icon:"st.contact.contact.closed", backgroundColor:"#00A0DC")
|
||||
state("open", label:'Freezer', icon:"st.contact.contact.open", backgroundColor:"#e86d13")
|
||||
state("closed", label:'Freezer', icon:"st.contact.contact.closed", backgroundColor:"#79b821")
|
||||
state("open", label:'Freezer', icon:"st.contact.contact.open", backgroundColor:"#ffa81e")
|
||||
}
|
||||
standardTile("mainDoor", "device.contact", width: 2, height: 2, decoration: "flat") {
|
||||
state("closed", label:'Fridge', icon:"st.contact.contact.closed", backgroundColor:"#00A0DC")
|
||||
state("open", label:'Fridge', icon:"st.contact.contact.open", backgroundColor:"#e86d13")
|
||||
state("closed", label:'Fridge', icon:"st.contact.contact.closed", backgroundColor:"#79b821")
|
||||
state("open", label:'Fridge', icon:"st.contact.contact.open", backgroundColor:"#ffa81e")
|
||||
}
|
||||
standardTile("control", "device.contact", width: 1, height: 1, decoration: "flat") {
|
||||
state("closed", label:'${name}', icon:"st.contact.contact.closed", action: "open")
|
||||
|
||||
@@ -25,8 +25,8 @@ metadata {
|
||||
|
||||
tiles(scale: 2) {
|
||||
standardTile("contact", "device.contact", width: 4, height: 4) {
|
||||
state("closed", label:'${name}', icon:"st.fridge.fridge-closed", backgroundColor:"#00A0DC")
|
||||
state("open", label:'${name}', icon:"st.fridge.fridge-open", backgroundColor:"#e86d13")
|
||||
state("closed", label:'${name}', icon:"st.fridge.fridge-closed", backgroundColor:"#79b821")
|
||||
state("open", label:'${name}', icon:"st.fridge.fridge-open", backgroundColor:"#ffa81e")
|
||||
}
|
||||
childDeviceTile("freezerDoor", "freezerDoor", height: 2, width: 2, childTileName: "freezerDoor")
|
||||
childDeviceTile("mainDoor", "mainDoor", height: 2, width: 2, childTileName: "mainDoor")
|
||||
|
||||
@@ -27,7 +27,7 @@ metadata {
|
||||
tileAttribute("device.switch", key: "PRIMARY_CONTROL") {
|
||||
attributeState "on", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#00A0DC", 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:"#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"
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,7 @@ metadata {
|
||||
tileAttribute("device.switch", key: "PRIMARY_CONTROL") {
|
||||
attributeState "on", label:'${name}', backgroundColor:"#00A0DC", nextState:"turningOff"
|
||||
attributeState "off", label:'${name}', backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
attributeState "turningOn", label:'${name}', backgroundColor:"#00A0DC", nextState:"turningOff"
|
||||
attributeState "turningOn", label:'${name}', backgroundColor:"#79b821", nextState:"turningOff"
|
||||
attributeState "turningOff", label:'${name}', backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
}
|
||||
tileAttribute("device.level", key: "SECONDARY_CONTROL") {
|
||||
@@ -59,7 +59,7 @@ metadata {
|
||||
tileAttribute("device.switch", key: "SECONDARY_CONTROL") {
|
||||
attributeState "on", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#00A0DC", nextState:"turningOff"
|
||||
attributeState "off", label:'${name}', action:"switch.on", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
attributeState "turningOn", label:'…', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#00A0DC", nextState:"turningOff"
|
||||
attributeState "turningOn", label:'…', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
attributeState "turningOff", label:'…', action:"switch.on", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
}
|
||||
tileAttribute("device.level", key: "VALUE_CONTROL") {
|
||||
|
||||
@@ -40,11 +40,11 @@ metadata {
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name:"rich-control", type: "switch", canChangeIcon: true){
|
||||
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
|
||||
attributeState "on", label:'${name}', action:"switch.off", icon:"st.Home.home30", backgroundColor:"#00A0DC", nextState:"turningOff"
|
||||
attributeState "on", label:'${name}', action:"switch.off", icon:"st.Home.home30", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
attributeState "off", label:'${name}', action:"switch.on", icon:"st.Home.home30", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.Home.home30", backgroundColor:"#00A0DC", nextState:"turningOff"
|
||||
attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.Home.home30", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.Home.home30", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
attributeState "offline", label:'${name}', icon:"st.Home.home30", backgroundColor:"#cccccc"
|
||||
attributeState "offline", label:'${name}', icon:"st.Home.home30", backgroundColor:"#ff0000"
|
||||
}
|
||||
tileAttribute ("currentIP", key: "SECONDARY_CONTROL") {
|
||||
attributeState "currentIP", label: ''
|
||||
|
||||
@@ -46,8 +46,8 @@
|
||||
}
|
||||
|
||||
standardTile("motion", "device.motion", width: 2, height: 2) {
|
||||
state("active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#00A0DC")
|
||||
state("inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#CCCCCC")
|
||||
state("active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#53a7c0")
|
||||
state("inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#ffffff")
|
||||
state("offline", label:'${name}', icon:"st.motion.motion.inactive", backgroundColor:"#ff0000")
|
||||
}
|
||||
|
||||
|
||||
@@ -38,11 +38,11 @@
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name:"rich-control", type: "switch", canChangeIcon: true){
|
||||
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
|
||||
attributeState "on", label:'${name}', action:"switch.off", icon:"st.switches.switch.off", backgroundColor:"#00A0DC", nextState:"turningOff"
|
||||
attributeState "on", label:'${name}', action:"switch.off", icon:"st.switches.switch.off", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
attributeState "off", label:'${name}', action:"switch.on", icon:"st.switches.switch.on", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.switches.switch.off", backgroundColor:"#00A0DC", nextState:"turningOff"
|
||||
attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.switches.switch.off", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.switches.switch.on", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
attributeState "offline", label:'${name}', icon:"st.switches.switch.off", backgroundColor:"#cccccc"
|
||||
attributeState "offline", label:'${name}', icon:"st.switches.switch.off", backgroundColor:"#ff0000"
|
||||
}
|
||||
tileAttribute ("currentIP", key: "SECONDARY_CONTROL") {
|
||||
attributeState "currentIP", label: ''
|
||||
@@ -50,11 +50,11 @@
|
||||
}
|
||||
|
||||
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
|
||||
state "on", label:'${name}', action:"switch.off", icon:"st.switches.switch.off", backgroundColor:"#00A0DC", nextState:"turningOff"
|
||||
state "on", label:'${name}', action:"switch.off", icon:"st.switches.switch.off", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
state "off", label:'${name}', action:"switch.on", icon:"st.switches.switch.on", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
state "turningOn", label:'${name}', action:"switch.off", icon:"st.switches.switch.off", backgroundColor:"#00A0DC", nextState:"turningOff"
|
||||
state "turningOn", label:'${name}', action:"switch.off", icon:"st.switches.switch.off", backgroundColor:"#79b821", nextState:"turningOff"
|
||||
state "turningOff", label:'${name}', action:"switch.on", icon:"st.switches.switch.on", backgroundColor:"#ffffff", nextState:"turningOn"
|
||||
state "offline", label:'${name}', icon:"st.switches.switch.off", backgroundColor:"#cccccc"
|
||||
state "offline", label:'${name}', icon:"st.switches.switch.off", backgroundColor:"#ff0000"
|
||||
}
|
||||
|
||||
standardTile("refresh", "device.switch", inactiveLabel: false, height: 2, width: 2, decoration: "flat") {
|
||||
|
||||
@@ -70,27 +70,19 @@ def parse(String description) {
|
||||
else {
|
||||
sendEvent(event)
|
||||
}
|
||||
} else {
|
||||
def descMap = zigbee.parseDescriptionAsMap(description)
|
||||
if (descMap && descMap.clusterInt == 0x0006 && descMap.commandInt == 0x07) {
|
||||
if (descMap.data[0] == "00") {
|
||||
}
|
||||
else {
|
||||
def cluster = zigbee.parse(description)
|
||||
if (cluster && cluster.clusterId == 0x0006 && cluster.command == 0x07) {
|
||||
if (cluster.data[0] == 0x00){
|
||||
log.debug "ON/OFF REPORTING CONFIG RESPONSE: " + cluster
|
||||
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
log.warn "ON/OFF REPORTING CONFIG FAILED- error code:${cluster.data[0]}"
|
||||
}
|
||||
} else if (device.getDataValue("manufacturer") == "sengled" && descMap && descMap.clusterInt == 0x0008 && descMap.attrInt == 0x0000) {
|
||||
// This is being done because the sengled element touch incorrectly uses the value 0xFF for the max level.
|
||||
// Per the ZCL spec for the UINT8 data type 0xFF is an invalid value, and 0xFE should be the max. Here we
|
||||
// manually handle the invalid attribute value since it will be ignored by getEvent as an invalid value.
|
||||
// We also set the level of the bulb to 0xFE so future level reports will be 0xFE until it is changed by
|
||||
// something else.
|
||||
if (descMap.value.toUpperCase() == "FF") {
|
||||
descMap.value = "FE"
|
||||
}
|
||||
sendHubCommand(zigbee.command(zigbee.LEVEL_CONTROL_CLUSTER, 0x00, "FE0000").collect { new physicalgraph.device.HubAction(it) }, 0)
|
||||
sendEvent(zigbee.getEventFromAttrData(descMap.clusterInt, descMap.attrInt, descMap.encoding, descMap.value))
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
log.warn "DID NOT PARSE MESSAGE for description : $description"
|
||||
log.debug zigbee.parseDescriptionAsMap(description)
|
||||
}
|
||||
@@ -122,17 +114,9 @@ def refresh() {
|
||||
|
||||
def configure() {
|
||||
log.debug "Configuring Reporting and Bindings."
|
||||
def cmds = []
|
||||
if (device.getDataValue("manufacturer") == "sengled") {
|
||||
def startLevel = 0xFE
|
||||
if ((device.currentState("level")?.value != null)) {
|
||||
startLevel = Math.round(Integer.parseInt(device.currentState("level").value) * 0xFE / 100)
|
||||
}
|
||||
// Level Control Cluster, command Move to Level, level start level, transition time 0
|
||||
cmds << zigbee.command(zigbee.LEVEL_CONTROL_CLUSTER, 0x00, sprintf("%02X0000", startLevel))
|
||||
}
|
||||
|
||||
// Device-Watch allows 2 check-in misses from device + ping (plus 1 min lag time)
|
||||
// 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])
|
||||
cmds + refresh()
|
||||
refresh()
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ metadata {
|
||||
|
||||
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008"
|
||||
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0B04, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY A19 ON/OFF/DIM", deviceJoinName: "SYLVANIA Smart A19 Soft White"
|
||||
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY A19 ON/OFF/DIM 10 Year", deviceJoinName: "SYLVANIA Smart 10-Year A19"
|
||||
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, FF00", outClusters: "0019", manufacturer: "MRVL", model: "MZ100", deviceJoinName: "Wemo Bulb"
|
||||
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, 0B05", outClusters: "0019", manufacturer: "OSRAM SYLVANIA", model: "iQBR30", deviceJoinName: "Sylvania Ultra iQ"
|
||||
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0008, FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY PAR38 ON/OFF/DIM", deviceJoinName: "SYLVANIA Smart PAR38 Soft White"
|
||||
|
||||
@@ -34,7 +34,7 @@ import physicalgraph.zigbee.zcl.DataType
|
||||
fingerprint profileId: "0104", inClusters: "0000,0001,0003,0009,000A,0101,0020", outClusters: "000A,0019", manufacturer: "Yale", model: "YRD210 PB DB", deviceJoinName: "Yale Push Button Deadbolt Lock"
|
||||
fingerprint profileId: "0104", inClusters: "0000,0001,0003,0009,000A,0101,0020", outClusters: "000A,0019", manufacturer: "Yale", model: "YRD220/240 TSDB", deviceJoinName: "Yale Touch Screen Deadbolt Lock"
|
||||
fingerprint profileId: "0104", inClusters: "0000,0001,0003,0009,000A,0101,0020", outClusters: "000A,0019", manufacturer: "Yale", model: "YRL210 PB LL", deviceJoinName: "Yale Push Button Lever Lock"
|
||||
fingerprint profileId: "0104", inClusters: "0000,0001,0003,0009,000A,0101,0020", outClusters: "000A,0019", manufacturer: "Yale", model: "YRD226/246 TSDB", deviceJoinName: "Yale Touch Screen Deadbolt Lock"
|
||||
fingerprint profileId: "0104", inClusters: "0000,0001,0003,0009,000A,0101,0020", outClusters: "000A,0019", manufacturer: "Yale", model: "YRD226/246 TSDB", deviceJoinName: "Yale Touch Screen Deadbolt Lock"
|
||||
}
|
||||
|
||||
tiles(scale: 2) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2017 SmartThings
|
||||
* Copyright 2016 SmartThings
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at:
|
||||
@@ -64,7 +64,6 @@ private getATTRIBUTE_HUE() { 0x0000 }
|
||||
private getATTRIBUTE_SATURATION() { 0x0001 }
|
||||
private getHUE_COMMAND() { 0x00 }
|
||||
private getSATURATION_COMMAND() { 0x03 }
|
||||
private getMOVE_TO_HUE_AND_SATURATION_COMMAND() { 0x06 }
|
||||
private getCOLOR_CONTROL_CLUSTER() { 0x0300 }
|
||||
|
||||
// Parse incoming device messages to generate events
|
||||
@@ -85,11 +84,11 @@ def parse(String description) {
|
||||
|
||||
if (zigbeeMap?.clusterInt == COLOR_CONTROL_CLUSTER) {
|
||||
if(zigbeeMap.attrInt == ATTRIBUTE_HUE){ //Hue Attribute
|
||||
def hueValue = Math.round(zigbee.convertHexToInt(zigbeeMap.value) / 0xfe * 100)
|
||||
def hueValue = Math.round(zigbee.convertHexToInt(zigbeeMap.value) / 255 * 100)
|
||||
sendEvent(name: "hue", value: hueValue, descriptionText: "Color has changed")
|
||||
}
|
||||
else if(zigbeeMap.attrInt == ATTRIBUTE_SATURATION){ //Saturation Attribute
|
||||
def saturationValue = Math.round(zigbee.convertHexToInt(zigbeeMap.value) / 0xfe * 100)
|
||||
def saturationValue = Math.round(zigbee.convertHexToInt(zigbeeMap.value) / 255 * 100)
|
||||
sendEvent(name: "saturation", value: saturationValue, descriptionText: "Color has changed", displayed: false)
|
||||
}
|
||||
}
|
||||
@@ -124,12 +123,7 @@ def ping() {
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
zigbee.onOffRefresh() +
|
||||
zigbee.levelRefresh() +
|
||||
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE) +
|
||||
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION) +
|
||||
zigbee.onOffConfig(0, 300) +
|
||||
zigbee.levelConfig()
|
||||
zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE) + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION) + zigbee.onOffConfig(0, 300) + zigbee.levelConfig() + zigbee.configureReporting(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE, DataType.UINT8, 1, 3600, 0x01) + zigbee.configureReporting(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION, DataType.UINT8, 1, 3600, 0x01)
|
||||
}
|
||||
|
||||
def configure() {
|
||||
@@ -139,38 +133,26 @@ def configure() {
|
||||
sendEvent(name: "checkInterval", value: 3 * 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.levelConfig() + zigbee.configureReporting(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE, DataType.UINT8, 1, 3600, 0x01) + zigbee.configureReporting(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION, DataType.UINT8, 1, 3600, 0x01) + zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE) + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION)
|
||||
}
|
||||
|
||||
def setLevel(value) {
|
||||
zigbee.setLevel(value)
|
||||
}
|
||||
|
||||
private getScaledHue(value) {
|
||||
zigbee.convertToHexString(Math.round(value * 0xfe / 100.0), 2)
|
||||
}
|
||||
|
||||
private getScaledSaturation(value) {
|
||||
zigbee.convertToHexString(Math.round(value * 0xfe / 100.0), 2)
|
||||
}
|
||||
|
||||
def setColor(value){
|
||||
log.trace "setColor($value)"
|
||||
zigbee.on() +
|
||||
zigbee.command(COLOR_CONTROL_CLUSTER, MOVE_TO_HUE_AND_SATURATION_COMMAND,
|
||||
getScaledHue(value.hue), getScaledSaturation(value.saturation), "0000") +
|
||||
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE) +
|
||||
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION)
|
||||
zigbee.on() + setHue(value.hue) + "delay 500" + setSaturation(value.saturation)
|
||||
}
|
||||
|
||||
def setHue(value) {
|
||||
zigbee.command(COLOR_CONTROL_CLUSTER, HUE_COMMAND, getScaledHue(value), "00", "0000") +
|
||||
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE)
|
||||
def scaledHueValue = zigbee.convertToHexString(Math.round(value * 0xfe / 100.0), 2)
|
||||
zigbee.command(COLOR_CONTROL_CLUSTER, HUE_COMMAND, scaledHueValue, "00", "0500") //payload-> hue value, direction (00-> shortest distance), transition time (1/10th second) (0500 in U16 reads 5)
|
||||
}
|
||||
|
||||
def setSaturation(value) {
|
||||
zigbee.command(COLOR_CONTROL_CLUSTER, SATURATION_COMMAND, getScaledSaturation(value), "0000") +
|
||||
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION)
|
||||
def scaledSatValue = zigbee.convertToHexString(Math.round(value * 0xfe / 100.0), 2)
|
||||
zigbee.command(COLOR_CONTROL_CLUSTER, SATURATION_COMMAND, scaledSatValue, "0500") + "delay 1000" + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION)
|
||||
}
|
||||
|
||||
def installed() {
|
||||
@@ -179,4 +161,4 @@ def installed() {
|
||||
sendEvent(name: "level", value: 100)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2017 SmartThings
|
||||
* Copyright 2016 SmartThings
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at:
|
||||
@@ -46,9 +46,9 @@ metadata {
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){
|
||||
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
|
||||
attributeState "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#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 "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"
|
||||
}
|
||||
tileAttribute ("device.level", key: "SLIDER_CONTROL") {
|
||||
@@ -78,7 +78,6 @@ private getATTRIBUTE_HUE() { 0x0000 }
|
||||
private getATTRIBUTE_SATURATION() { 0x0001 }
|
||||
private getHUE_COMMAND() { 0x00 }
|
||||
private getSATURATION_COMMAND() { 0x03 }
|
||||
private getMOVE_TO_HUE_AND_SATURATION_COMMAND() { 0x06 }
|
||||
private getCOLOR_CONTROL_CLUSTER() { 0x0300 }
|
||||
private getATTRIBUTE_COLOR_TEMPERATURE() { 0x0007 }
|
||||
|
||||
@@ -103,11 +102,11 @@ def parse(String description) {
|
||||
|
||||
if (zigbeeMap?.clusterInt == COLOR_CONTROL_CLUSTER) {
|
||||
if(zigbeeMap.attrInt == ATTRIBUTE_HUE){ //Hue Attribute
|
||||
def hueValue = Math.round(zigbee.convertHexToInt(zigbeeMap.value) / 0xfe * 100)
|
||||
def hueValue = Math.round(zigbee.convertHexToInt(zigbeeMap.value) / 255 * 100)
|
||||
sendEvent(name: "hue", value: hueValue, descriptionText: "Color has changed")
|
||||
}
|
||||
else if(zigbeeMap.attrInt == ATTRIBUTE_SATURATION){ //Saturation Attribute
|
||||
def saturationValue = Math.round(zigbee.convertHexToInt(zigbeeMap.value) / 0xfe * 100)
|
||||
def saturationValue = Math.round(zigbee.convertHexToInt(zigbeeMap.value) / 255 * 100)
|
||||
sendEvent(name: "saturation", value: saturationValue, descriptionText: "Color has changed", displayed: false)
|
||||
}
|
||||
}
|
||||
@@ -142,13 +141,7 @@ def ping() {
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
zigbee.onOffRefresh() +
|
||||
zigbee.levelRefresh() +
|
||||
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_COLOR_TEMPERATURE) +
|
||||
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE) +
|
||||
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION) +
|
||||
zigbee.onOffConfig(0, 300) +
|
||||
zigbee.levelConfig()
|
||||
zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_COLOR_TEMPERATURE) + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE) + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION) + zigbee.onOffConfig(0, 300) + zigbee.levelConfig() + zigbee.colorTemperatureConfig() + zigbee.configureReporting(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE, DataType.UINT8, 1, 3600, 0x01) + zigbee.configureReporting(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION, DataType.UINT8, 1, 3600, 0x01)
|
||||
}
|
||||
|
||||
def configure() {
|
||||
@@ -163,12 +156,7 @@ def configure() {
|
||||
|
||||
def setColorTemperature(value) {
|
||||
setGenericName(value)
|
||||
value = value as Integer
|
||||
def tempInMired = (1000000 / value) as Integer
|
||||
def finalHex = zigbee.swapEndianHex(zigbee.convertToHexString(tempInMired, 4))
|
||||
|
||||
zigbee.command(COLOR_CONTROL_CLUSTER, 0x0A, "$finalHex 0000") +
|
||||
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_COLOR_TEMPERATURE)
|
||||
zigbee.setColorTemperature(value)
|
||||
}
|
||||
|
||||
//Naming based on the wiki article here: http://en.wikipedia.org/wiki/Color_temperature
|
||||
@@ -192,31 +180,19 @@ def setLevel(value) {
|
||||
zigbee.setLevel(value)
|
||||
}
|
||||
|
||||
private getScaledHue(value) {
|
||||
zigbee.convertToHexString(Math.round(value * 0xfe / 100.0), 2)
|
||||
}
|
||||
|
||||
private getScaledSaturation(value) {
|
||||
zigbee.convertToHexString(Math.round(value * 0xfe / 100.0), 2)
|
||||
}
|
||||
|
||||
def setColor(value){
|
||||
log.trace "setColor($value)"
|
||||
zigbee.on() +
|
||||
zigbee.command(COLOR_CONTROL_CLUSTER, MOVE_TO_HUE_AND_SATURATION_COMMAND,
|
||||
getScaledHue(value.hue), getScaledSaturation(value.saturation), "0000") +
|
||||
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE) +
|
||||
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION)
|
||||
zigbee.on() + setHue(value.hue) + "delay 300" + setSaturation(value.saturation)
|
||||
}
|
||||
|
||||
def setHue(value) {
|
||||
zigbee.command(COLOR_CONTROL_CLUSTER, HUE_COMMAND, getScaledHue(value), "00", "0000") +
|
||||
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE)
|
||||
def scaledHueValue = zigbee.convertToHexString(Math.round(value * 0xfe / 100.0), 2)
|
||||
zigbee.command(COLOR_CONTROL_CLUSTER, HUE_COMMAND, scaledHueValue, "00", "0500") //payload-> hue value, direction (00-> shortest distance), transition time (1/10th second) (0500 in U16 reads 5)
|
||||
}
|
||||
|
||||
def setSaturation(value) {
|
||||
zigbee.command(COLOR_CONTROL_CLUSTER, SATURATION_COMMAND, getScaledSaturation(value), "0000") +
|
||||
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION)
|
||||
def scaledSatValue = zigbee.convertToHexString(Math.round(value * 0xfe / 100.0), 2)
|
||||
zigbee.command(COLOR_CONTROL_CLUSTER, SATURATION_COMMAND, scaledSatValue, "0500") + "delay 1000" + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION)
|
||||
}
|
||||
|
||||
def installed() {
|
||||
@@ -225,4 +201,4 @@ def installed() {
|
||||
sendEvent(name: "level", value: 100)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ metadata {
|
||||
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0702"
|
||||
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0702, 0B05", outClusters: "0003, 000A, 0019", manufacturer: "Jasco Products", model: "45853", deviceJoinName: "GE ZigBee Plug-In Switch"
|
||||
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0702, 0B05", outClusters: "000A, 0019", manufacturer: "Jasco Products", model: "45856", deviceJoinName: "GE ZigBee In-Wall Switch"
|
||||
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 000F, 0B04", outClusters: "0019", manufacturer: "SmartThings", model: "outletv4", deviceJoinName: "Outlet"
|
||||
}
|
||||
|
||||
tiles(scale: 2) {
|
||||
|
||||
@@ -41,9 +41,9 @@ metadata {
|
||||
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.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 "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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ metadata {
|
||||
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name:"valve", type: "generic", width: 6, height: 4, canChangeIcon: true){
|
||||
tileAttribute ("device.valve", key: "PRIMARY_CONTROL") {
|
||||
tileAttribute ("device.contact", key: "PRIMARY_CONTROL") {
|
||||
attributeState "open", label: '${name}', action: "valve.close", icon: "st.valves.water.open", backgroundColor: "#00A0DC", nextState:"closing"
|
||||
attributeState "closed", label: '${name}', action: "valve.open", icon: "st.valves.water.closed", backgroundColor: "#ffffff", nextState:"opening"
|
||||
attributeState "opening", label: '${name}', action: "valve.close", icon: "st.valves.water.open", backgroundColor: "#00A0DC", nextState:"closing"
|
||||
@@ -83,9 +83,6 @@ def parse(String description) {
|
||||
}
|
||||
}
|
||||
sendEvent(event)
|
||||
//handle valve attribute
|
||||
event.name = "valve"
|
||||
sendEvent(event)
|
||||
}
|
||||
else {
|
||||
def descMap = zigbee.parseDescriptionAsMap(description)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2017 SmartThings
|
||||
* Copyright 2015 SmartThings
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at:
|
||||
@@ -45,9 +45,9 @@ metadata {
|
||||
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.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 "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"
|
||||
}
|
||||
tileAttribute ("device.level", key: "SLIDER_CONTROL") {
|
||||
@@ -71,11 +71,6 @@ metadata {
|
||||
}
|
||||
}
|
||||
|
||||
// Globals
|
||||
private getMOVE_TO_COLOR_TEMPERATURE_COMMAND() { 0x0A }
|
||||
private getCOLOR_CONTROL_CLUSTER() { 0x0300 }
|
||||
private getATTRIBUTE_COLOR_TEMPERATURE() { 0x0007 }
|
||||
|
||||
// Parse incoming device messages to generate events
|
||||
def parse(String description) {
|
||||
log.debug "description is $description"
|
||||
@@ -128,11 +123,7 @@ def ping() {
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
zigbee.onOffRefresh() +
|
||||
zigbee.levelRefresh() +
|
||||
zigbee.colorTemperatureRefresh() +
|
||||
zigbee.onOffConfig(0, 300) +
|
||||
zigbee.levelConfig()
|
||||
zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.colorTemperatureRefresh() + zigbee.onOffConfig(0, 300) + zigbee.levelConfig() + zigbee.colorTemperatureConfig()
|
||||
}
|
||||
|
||||
def configure() {
|
||||
@@ -147,12 +138,7 @@ def configure() {
|
||||
|
||||
def setColorTemperature(value) {
|
||||
setGenericName(value)
|
||||
value = value as Integer
|
||||
def tempInMired = (1000000 / value) as Integer
|
||||
def finalHex = zigbee.swapEndianHex(zigbee.convertToHexString(tempInMired, 4))
|
||||
|
||||
zigbee.command(COLOR_CONTROL_CLUSTER, MOVE_TO_COLOR_TEMPERATURE_COMMAND, "$finalHex 0000") +
|
||||
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_COLOR_TEMPERATURE)
|
||||
zigbee.setColorTemperature(value)
|
||||
}
|
||||
|
||||
//Naming based on the wiki article here: http://en.wikipedia.org/wiki/Color_temperature
|
||||
|
||||
@@ -49,9 +49,9 @@ metadata {
|
||||
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.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 "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"
|
||||
}
|
||||
tileAttribute ("device.level", key: "SLIDER_CONTROL") {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2017 SmartThings
|
||||
* Copyright 2016 SmartThings
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at:
|
||||
@@ -55,7 +55,6 @@ private getATTRIBUTE_HUE() { 0x0000 }
|
||||
private getATTRIBUTE_SATURATION() { 0x0001 }
|
||||
private getHUE_COMMAND() { 0x00 }
|
||||
private getSATURATION_COMMAND() { 0x03 }
|
||||
private getMOVE_TO_HUE_AND_SATURATION_COMMAND() { 0x06 }
|
||||
private getCOLOR_CONTROL_CLUSTER() { 0x0300 }
|
||||
|
||||
// Parse incoming device messages to generate events
|
||||
@@ -73,11 +72,11 @@ def parse(String description) {
|
||||
|
||||
if (zigbeeMap?.clusterInt == COLOR_CONTROL_CLUSTER) {
|
||||
if(zigbeeMap.attrInt == ATTRIBUTE_HUE){ //Hue Attribute
|
||||
def hueValue = Math.round(zigbee.convertHexToInt(zigbeeMap.value) / 0xfe * 100)
|
||||
def hueValue = Math.round(zigbee.convertHexToInt(zigbeeMap.value) / 255 * 360)
|
||||
sendEvent(name: "hue", value: hueValue, displayed:false)
|
||||
}
|
||||
else if(zigbeeMap.attrInt == ATTRIBUTE_SATURATION){ //Saturation Attribute
|
||||
def saturationValue = Math.round(zigbee.convertHexToInt(zigbeeMap.value) / 0xfe * 100)
|
||||
def saturationValue = Math.round(zigbee.convertHexToInt(zigbeeMap.value) / 255 * 100)
|
||||
sendEvent(name: "saturation", value: saturationValue, displayed:false)
|
||||
}
|
||||
}
|
||||
@@ -109,46 +108,28 @@ def configure() {
|
||||
}
|
||||
|
||||
def configureAttributes() {
|
||||
zigbee.onOffConfig() +
|
||||
zigbee.levelConfig()
|
||||
zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.configureReporting(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE, DataType.UINT8, 1, 3600, 0x01) + zigbee.configureReporting(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION, DataType.UINT8, 1, 3600, 0x01)
|
||||
}
|
||||
|
||||
def refreshAttributes() {
|
||||
zigbee.onOffRefresh() +
|
||||
zigbee.levelRefresh() +
|
||||
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE) +
|
||||
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION)
|
||||
zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE) + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION)
|
||||
}
|
||||
|
||||
def setLevel(value) {
|
||||
zigbee.setLevel(value) + zigbee.onOffRefresh() + zigbee.levelRefresh() //adding refresh because of ZLL bulb not conforming to send-me-a-report
|
||||
}
|
||||
|
||||
private getScaledHue(value) {
|
||||
zigbee.convertToHexString(Math.round(value * 0xfe / 100.0), 2)
|
||||
}
|
||||
|
||||
private getScaledSaturation(value) {
|
||||
zigbee.convertToHexString(Math.round(value * 0xfe / 100.0), 2)
|
||||
}
|
||||
|
||||
def setColor(value){
|
||||
log.trace "setColor($value)"
|
||||
zigbee.on() +
|
||||
zigbee.command(COLOR_CONTROL_CLUSTER, MOVE_TO_HUE_AND_SATURATION_COMMAND,
|
||||
getScaledHue(value.hue), getScaledSaturation(value.saturation), "0000") +
|
||||
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE) +
|
||||
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION)
|
||||
zigbee.on() + setHue(value.hue) + ["delay 300"] + setSaturation(value.saturation) + ["delay 2000"] + refreshAttributes()
|
||||
}
|
||||
|
||||
def setHue(value) {
|
||||
//payload-> hue value, direction (00-> shortest distance), transition time (1/10th second)
|
||||
zigbee.command(COLOR_CONTROL_CLUSTER, HUE_COMMAND, getScaledHue(value), "00", "0000") +
|
||||
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE)
|
||||
def scaledHueValue = zigbee.convertToHexString(Math.round(value * 0xfe / 100.0), 2)
|
||||
zigbee.command(COLOR_CONTROL_CLUSTER, HUE_COMMAND, scaledHueValue, "00", "0500") + ["delay 1500"] + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE) //payload-> hue value, direction (00-> shortest distance), transition time (1/10th second) (0500 in U16 reads 5)
|
||||
}
|
||||
|
||||
def setSaturation(value) {
|
||||
//payload-> sat value, transition time
|
||||
zigbee.command(COLOR_CONTROL_CLUSTER, SATURATION_COMMAND, getScaledSaturation(value), "0000") +
|
||||
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION)
|
||||
def scaledSatValue = zigbee.convertToHexString(Math.round(value * 0xfe / 100.0), 2)
|
||||
zigbee.command(COLOR_CONTROL_CLUSTER, SATURATION_COMMAND, scaledSatValue, "0500") + ["delay 1500"] + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION) //payload-> sat value, transition time
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2017 SmartThings
|
||||
* Copyright 2016 SmartThings
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at:
|
||||
@@ -70,7 +70,6 @@ private getATTRIBUTE_HUE() { 0x0000 }
|
||||
private getATTRIBUTE_SATURATION() { 0x0001 }
|
||||
private getHUE_COMMAND() { 0x00 }
|
||||
private getSATURATION_COMMAND() { 0x03 }
|
||||
private getMOVE_TO_HUE_AND_SATURATION_COMMAND() { 0x06 }
|
||||
private getCOLOR_CONTROL_CLUSTER() { 0x0300 }
|
||||
private getATTRIBUTE_COLOR_TEMPERATURE() { 0x0007 }
|
||||
|
||||
@@ -89,11 +88,11 @@ def parse(String description) {
|
||||
|
||||
if (zigbeeMap?.clusterInt == COLOR_CONTROL_CLUSTER) {
|
||||
if(zigbeeMap.attrInt == ATTRIBUTE_HUE){ //Hue Attribute
|
||||
def hueValue = Math.round(zigbee.convertHexToInt(zigbeeMap.value) / 0xfe * 100)
|
||||
def hueValue = Math.round(zigbee.convertHexToInt(zigbeeMap.value) / 255 * 360)
|
||||
sendEvent(name: "hue", value: hueValue, displayed:false)
|
||||
}
|
||||
else if(zigbeeMap.attrInt == ATTRIBUTE_SATURATION){ //Saturation Attribute
|
||||
def saturationValue = Math.round(zigbee.convertHexToInt(zigbeeMap.value) / 0xfe * 100)
|
||||
def saturationValue = Math.round(zigbee.convertHexToInt(zigbeeMap.value) / 255 * 100)
|
||||
sendEvent(name: "saturation", value: saturationValue, displayed:false)
|
||||
}
|
||||
}
|
||||
@@ -125,16 +124,11 @@ def configure() {
|
||||
}
|
||||
|
||||
def configureAttributes() {
|
||||
zigbee.onOffConfig() +
|
||||
zigbee.levelConfig()
|
||||
zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.colorTemperatureConfig() + zigbee.configureReporting(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE, DataType.UINT8, 1, 3600, 0x01) + zigbee.configureReporting(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION, DataType.UINT8, 1, 3600, 0x01)
|
||||
}
|
||||
|
||||
def refreshAttributes() {
|
||||
zigbee.onOffRefresh() +
|
||||
zigbee.levelRefresh() +
|
||||
zigbee.colorTemperatureRefresh() +
|
||||
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE) +
|
||||
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION)
|
||||
zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.colorTemperatureRefresh() + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE) + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION)
|
||||
}
|
||||
|
||||
def setColorTemperature(value) {
|
||||
@@ -145,32 +139,17 @@ def setLevel(value) {
|
||||
zigbee.setLevel(value) + zigbee.onOffRefresh() + zigbee.levelRefresh() //adding refresh because of ZLL bulb not conforming to send-me-a-report
|
||||
}
|
||||
|
||||
private getScaledHue(value) {
|
||||
zigbee.convertToHexString(Math.round(value * 0xfe / 100.0), 2)
|
||||
}
|
||||
|
||||
private getScaledSaturation(value) {
|
||||
zigbee.convertToHexString(Math.round(value * 0xfe / 100.0), 2)
|
||||
}
|
||||
|
||||
def setColor(value){
|
||||
log.trace "setColor($value)"
|
||||
zigbee.on() +
|
||||
zigbee.command(COLOR_CONTROL_CLUSTER, MOVE_TO_HUE_AND_SATURATION_COMMAND,
|
||||
getScaledHue(value.hue), getScaledSaturation(value.saturation), "0000") +
|
||||
zigbee.onOffRefresh() +
|
||||
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE) +
|
||||
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION)
|
||||
zigbee.on() + setHue(value.hue) + ["delay 300"] + setSaturation(value.saturation) + ["delay 2000"] + refreshAttributes()
|
||||
}
|
||||
|
||||
def setHue(value) {
|
||||
//payload-> hue value, direction (00-> shortest distance), transition time (1/10th second)
|
||||
zigbee.command(COLOR_CONTROL_CLUSTER, HUE_COMMAND, getScaledHue(value), "00", "0000") +
|
||||
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE)
|
||||
def scaledHueValue = zigbee.convertToHexString(Math.round(value * 0xfe / 100.0), 2)
|
||||
zigbee.command(COLOR_CONTROL_CLUSTER, HUE_COMMAND, scaledHueValue, "00", "0500") + ["delay 1500"] + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE) //payload-> hue value, direction (00-> shortest distance), transition time (1/10th second) (0500 in U16 reads 5)
|
||||
}
|
||||
|
||||
def setSaturation(value) {
|
||||
//payload-> sat value, transition time
|
||||
zigbee.command(COLOR_CONTROL_CLUSTER, SATURATION_COMMAND, getScaledSaturation(value), "0000") +
|
||||
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION)
|
||||
def scaledSatValue = zigbee.convertToHexString(Math.round(value * 0xfe / 100.0), 2)
|
||||
zigbee.command(COLOR_CONTROL_CLUSTER, SATURATION_COMMAND, scaledSatValue, "0500") + ["delay 1500"] + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION) //payload-> sat value, transition time
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2017 SmartThings
|
||||
* Copyright 2016 SmartThings
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at:
|
||||
@@ -66,11 +66,6 @@ metadata {
|
||||
}
|
||||
}
|
||||
|
||||
// Globals
|
||||
private getMOVE_TO_COLOR_TEMPERATURE_COMMAND() { 0x0A }
|
||||
private getCOLOR_CONTROL_CLUSTER() { 0x0300 }
|
||||
private getATTRIBUTE_COLOR_TEMPERATURE() { 0x0007 }
|
||||
|
||||
// Parse incoming device messages to generate events
|
||||
def parse(String description) {
|
||||
log.debug "description is $description"
|
||||
@@ -100,14 +95,14 @@ def setLevel(value) {
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
def cmds = zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.colorTemperatureRefresh()
|
||||
def cmds = zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.colorTemperatureRefresh()
|
||||
|
||||
// Do NOT config if the device is the Eaton Halo_LT01, it responds with "switch:off" to onOffConfig, and maybe other weird things with the others
|
||||
// Do NOT config if the device is the Eaton Halo_LT01, it responds with "switch:off" to onOffConfig, and maybe other weird things with the others
|
||||
if (!((device.getDataValue("manufacturer") == "Eaton") && (device.getDataValue("model") == "Halo_LT01"))) {
|
||||
cmds += zigbee.onOffConfig() + zigbee.levelConfig()
|
||||
cmds = cmds + zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.colorTemperatureConfig()
|
||||
}
|
||||
|
||||
cmds
|
||||
cmds
|
||||
}
|
||||
|
||||
def poll() {
|
||||
@@ -143,7 +138,7 @@ def configure() {
|
||||
log.debug "configure()"
|
||||
configureHealthCheck()
|
||||
// Implementation note: for the Eaton Halo_LT01, it responds with "switch:off" to onOffConfig, so be sure this is before the call to onOffRefresh
|
||||
zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.colorTemperatureRefresh()
|
||||
zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.colorTemperatureConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.colorTemperatureRefresh()
|
||||
}
|
||||
|
||||
def updated() {
|
||||
@@ -153,12 +148,7 @@ def updated() {
|
||||
|
||||
def setColorTemperature(value) {
|
||||
setGenericName(value)
|
||||
value = value as Integer
|
||||
def tempInMired = (1000000 / value) as Integer
|
||||
def finalHex = zigbee.swapEndianHex(zigbee.convertToHexString(tempInMired, 4))
|
||||
|
||||
zigbee.command(COLOR_CONTROL_CLUSTER, MOVE_TO_COLOR_TEMPERATURE_COMMAND, "$finalHex 0000") +
|
||||
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_COLOR_TEMPERATURE)
|
||||
zigbee.setColorTemperature(value) + ["delay 1500"] + zigbee.colorTemperatureRefresh()
|
||||
}
|
||||
|
||||
//Naming based on the wiki article here: http://en.wikipedia.org/wiki/Color_temperature
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2017 SmartThings
|
||||
* Copyright 2016 SmartThings
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at:
|
||||
@@ -68,11 +68,6 @@ metadata {
|
||||
}
|
||||
}
|
||||
|
||||
// Globals
|
||||
private getMOVE_TO_COLOR_TEMPERATURE_COMMAND() { 0x0A }
|
||||
private getCOLOR_CONTROL_CLUSTER() { 0x0300 }
|
||||
private getATTRIBUTE_COLOR_TEMPERATURE() { 0x0007 }
|
||||
|
||||
// Parse incoming device messages to generate events
|
||||
def parse(String description) {
|
||||
log.debug "description is $description"
|
||||
@@ -99,11 +94,7 @@ def setLevel(value) {
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
zigbee.onOffRefresh() +
|
||||
zigbee.levelRefresh() +
|
||||
zigbee.colorTemperatureRefresh() +
|
||||
zigbee.onOffConfig() +
|
||||
zigbee.levelConfig()
|
||||
zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.colorTemperatureRefresh() + zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.colorTemperatureConfig()
|
||||
}
|
||||
|
||||
def poll() {
|
||||
@@ -138,7 +129,8 @@ def configureHealthCheck() {
|
||||
def configure() {
|
||||
log.debug "configure()"
|
||||
configureHealthCheck()
|
||||
refresh()
|
||||
zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.colorTemperatureConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.colorTemperatureRefresh()
|
||||
|
||||
}
|
||||
|
||||
def updated() {
|
||||
@@ -148,12 +140,7 @@ def updated() {
|
||||
|
||||
def setColorTemperature(value) {
|
||||
setGenericName(value)
|
||||
value = value as Integer
|
||||
def tempInMired = (1000000 / value) as Integer
|
||||
def finalHex = zigbee.swapEndianHex(zigbee.convertToHexString(tempInMired, 4))
|
||||
|
||||
zigbee.command(COLOR_CONTROL_CLUSTER, MOVE_TO_COLOR_TEMPERATURE_COMMAND, "$finalHex 0000") +
|
||||
zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_COLOR_TEMPERATURE)
|
||||
zigbee.setColorTemperature(value) + ["delay 1500"] + zigbee.colorTemperatureRefresh()
|
||||
}
|
||||
|
||||
//Naming based on the wiki article here: http://en.wikipedia.org/wiki/Color_temperature
|
||||
|
||||
@@ -75,10 +75,6 @@ def parse(String description) {
|
||||
return result
|
||||
}
|
||||
|
||||
def uninstalled() {
|
||||
sendEvent(name: "epEvent", value: "delete all", isStateChange: true, displayed: false, descriptionText: "Delete endpoint devices")
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.wakeupv1.WakeUpNotification cmd) {
|
||||
[ createEvent(descriptionText: "${device.displayName} woke up", isStateChange:true),
|
||||
response(["delay 2000", zwave.wakeUpV1.wakeUpNoMoreInformation().format()]) ]
|
||||
|
||||
@@ -30,15 +30,6 @@ metadata {
|
||||
fingerprint deviceId: "0x0701", inClusters: "0x5E,0x86,0x72,0x98", outClusters: "0x5A,0x82"
|
||||
fingerprint deviceId: "0x0701", inClusters: "0x5E,0x80,0x71,0x85,0x70,0x72,0x86,0x30,0x31,0x84,0x59,0x73,0x5A,0x8F,0x98,0x7A", outClusters:"0x20" // Philio multi+
|
||||
fingerprint mfr:"0086", prod:"0002", model:"001D", deviceJoinName: "Aeon Labs Door/Window Sensor (Gen 5)"
|
||||
fingerprint mfr:"0086", prod:"0102", model:"0070", deviceJoinName: "Aeon Labs Door/Window Sensor 6"
|
||||
fingerprint mfr:"0086", prod:"0102", model:"0059", deviceJoinName: "Aeon Labs Recessed Door Sensor"
|
||||
fingerprint mfr:"014A", prod:"0001", model:"0002", deviceJoinName: "Ecolink Door/Window Sensor"
|
||||
fingerprint mfr:"014A", prod:"0001", model:"0003", deviceJoinName: "Ecolink Tilt Sensor"
|
||||
fingerprint mfr:"011A", prod:"0601", model:"0903", deviceJoinName: "Enerwave Magnetic Door/Window Sensor"
|
||||
fingerprint mfr:"014F", prod:"2001", model:"0102", deviceJoinName: "Nortek GoControl Door/Window Sensor"
|
||||
fingerprint mfr:"0063", prod:"4953", model:"3031", deviceJoinName: "Jasco Hinge Pin Door Sensor"
|
||||
fingerprint mfr:"019A", prod:"0003", model:"0003", deviceJoinName: "Sensative Strips"
|
||||
|
||||
}
|
||||
|
||||
// simulator metadata
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Z-Wave Lock
|
||||
# Z-Wave Switch
|
||||
|
||||
Cloud Execution
|
||||
|
||||
@@ -6,6 +6,7 @@ Works with:
|
||||
|
||||
* [Yale Key Free Touchscreen Deadbolt (YRD240)](https://www.smartthings.com/works-with-smartthings/yale/yale-key-free-touchscreen-deadbolt-yrd240)
|
||||
|
||||
|
||||
## Table of contents
|
||||
|
||||
* [Capabilities](#capabilities)
|
||||
@@ -40,3 +41,5 @@ If the device doesn't pair when trying from the SmartThings mobile app, it is po
|
||||
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/ZigBee Yale Lock Troubleshooting](https://support.smartthings.com/hc/en-us/articles/205138400-How-to-connect-Yale-locks)
|
||||
|
||||
|
||||
|
||||
@@ -26,24 +26,7 @@ metadata {
|
||||
|
||||
fingerprint deviceId: "0x4003", inClusters: "0x98"
|
||||
fingerprint deviceId: "0x4004", inClusters: "0x98"
|
||||
fingerprint mfr:"0090", prod:"0001", model:"0236", deviceJoinName: "KwikSet SmartCode 910 Deadbolt Door Lock"
|
||||
fingerprint mfr:"0090", prod:"0003", model:"0238", deviceJoinName: "KwikSet SmartCode 910 Deadbolt Door Lock"
|
||||
fingerprint mfr:"0090", prod:"0001", model:"0001", deviceJoinName: "KwikSet SmartCode 910 Contemporary Deadbolt Door Lock"
|
||||
fingerprint mfr:"0090", prod:"0003", model:"0339", deviceJoinName: "KwikSet SmartCode 912 Lever Door Lock"
|
||||
fingerprint mfr:"0090", prod:"0003", model:"4006", deviceJoinName: "KwikSet SmartCode 914 Deadbolt Door Lock" //backlit version
|
||||
fingerprint mfr:"0090", prod:"0003", model:"0440", deviceJoinName: "KwikSet SmartCode 914 Deadbolt Door Lock"
|
||||
fingerprint mfr:"0090", prod:"0001", model:"0642", deviceJoinName: "KwikSet SmartCode 916 Touchscreen Deadbolt Door Lock"
|
||||
fingerprint mfr:"0090", prod:"0003", model:"0642", deviceJoinName: "KwikSet SmartCode 916 Touchscreen Deadbolt Door Lock"
|
||||
fingerprint mfr:"003B", prod:"6341", model:"0544", deviceJoinName: "Schlage Camelot Touchscreen Deadbolt Door Lock"
|
||||
fingerprint mfr:"003B", prod:"6341", model:"5044", deviceJoinName: "Schlage Century Touchscreen Deadbolt Door Lock"
|
||||
fingerprint mfr:"003B", prod:"634B", model:"504C", deviceJoinName: "Schlage Connected Keypad Lever Door Lock"
|
||||
fingerprint mfr:"0129", prod:"0002", model:"0800", deviceJoinName: "Yale Touchscreen Deadbolt Door Lock" // YRD120
|
||||
fingerprint mfr:"0129", prod:"0002", model:"0000", deviceJoinName: "Yale Touchscreen Deadbolt Door Lock" // YRD220, YRD240
|
||||
fingerprint mfr:"0129", prod:"0002", model:"FFFF", deviceJoinName: "Yale Touchscreen Lever Door Lock" // YRD220
|
||||
fingerprint mfr:"0129", prod:"0004", model:"0800", deviceJoinName: "Yale Push Button Deadbolt Door Lock" // YRD110
|
||||
fingerprint mfr:"0129", prod:"0004", model:"0000", deviceJoinName: "Yale Push Button Deadbolt Door Lock" // YRD210
|
||||
fingerprint mfr:"0129", prod:"0001", model:"0000", deviceJoinName: "Yale Push Button Lever Door Lock" // YRD210
|
||||
fingerprint mfr:"0129", prod:"8002", model:"0600", deviceJoinName: "Yale Assure Lock with Bluetooth"
|
||||
fingerprint mfr:"0129", prod:"0002", model:"0000", deviceJoinName: "Yale Key Free Touchscreen Deadbolt"
|
||||
}
|
||||
|
||||
simulator {
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
*/
|
||||
metadata {
|
||||
definition (name: "Z-Wave Metering Dimmer", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "oic.d.light") {
|
||||
definition (name: "Z-Wave Metering Dimmer", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "oic.d.switch") {
|
||||
capability "Switch"
|
||||
capability "Polling"
|
||||
capability "Power Meter"
|
||||
|
||||
@@ -29,7 +29,7 @@ metadata {
|
||||
fingerprint mfr: "0060", prod: "0001", model: "0002", deviceJoinName: "Everspring Motion Sensor" // Everspring SP814
|
||||
fingerprint mfr: "0060", prod: "0001", model: "0003", deviceJoinName: "Everspring Motion Sensor" // Everspring HSP02
|
||||
fingerprint mfr: "011A", prod: "0601", model: "0901", deviceJoinName: "Enerwave Motion Sensor" // Enerwave ZWN-BPC
|
||||
fingerprint mfr: "0063", prod: "4953", model: "3133", deviceJoinName: "GE Portable Smart Motion Sensor"
|
||||
fingerprint mfr: "0063", prod: "4953", model: "3133", deviceJoinName: "GE Smart Motion Sensor"
|
||||
}
|
||||
|
||||
simulator {
|
||||
|
||||
@@ -37,8 +37,8 @@ metadata {
|
||||
|
||||
tiles {
|
||||
standardTile("motion", "device.motion", width: 3, height: 2) {
|
||||
state "active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#00A0DC"
|
||||
state "inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#CCCCCC"
|
||||
state "active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#53a7c0"
|
||||
state "inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#ffffff"
|
||||
}
|
||||
|
||||
valueTile("temperature", "device.temperature", inactiveLabel: false) {
|
||||
|
||||
@@ -22,7 +22,6 @@ metadata {
|
||||
attribute "alarmState", "string"
|
||||
|
||||
fingerprint deviceId: "0xA100", inClusters: "0x20,0x80,0x70,0x85,0x71,0x72,0x86"
|
||||
fingerprint mfr:"0138", prod:"0001", model:"0001", deviceJoinName: "First Alert Smoke Detector"
|
||||
fingerprint mfr:"0138", prod:"0001", model:"0002", deviceJoinName: "First Alert Smoke Detector and Carbon Monoxide Alarm (ZCOMBO)"
|
||||
}
|
||||
|
||||
|
||||
@@ -22,9 +22,9 @@ metadata {
|
||||
capability "Health Check"
|
||||
capability "Light"
|
||||
|
||||
fingerprint mfr:"0063", prod:"4952", deviceJoinName: "GE Wall Switch"
|
||||
fingerprint mfr:"0063", prod:"5257", deviceJoinName: "GE Wall Switch"
|
||||
fingerprint mfr:"0063", prod:"5052", deviceJoinName: "GE Plug-In Switch"
|
||||
fingerprint mfr:"0063", prod:"4952", deviceJoinName: "Z-Wave Wall Switch"
|
||||
fingerprint mfr:"0063", prod:"5257", deviceJoinName: "Z-Wave Wall Switch"
|
||||
fingerprint mfr:"0063", prod:"5052", deviceJoinName: "Z-Wave Plug-In Switch"
|
||||
fingerprint mfr:"0113", prod:"5257", deviceJoinName: "Z-Wave Wall Switch"
|
||||
}
|
||||
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
.st-ignore
|
||||
README.md
|
||||
@@ -1,38 +0,0 @@
|
||||
# Z-Wave Water Valve
|
||||
|
||||
Cloud Execution
|
||||
|
||||
Works with:
|
||||
|
||||
* [Leak Intelligence Leak Gopher Water Shutoff Valve](https://www.smartthings.com/works-with-smartthings/other/leak-intelligence-leak-gopher-water-shutoff-valve)
|
||||
|
||||
|
||||
## Table of contents
|
||||
|
||||
* [Capabilities](#capabilities)
|
||||
* [Health](#device-health)
|
||||
* [Troubleshooting](#Troubleshooting)
|
||||
|
||||
## Capabilities
|
||||
|
||||
* **Actuator** - represents that a Device has commands
|
||||
* **Health Check** - indicates ability to get device health notifications
|
||||
* **Valve** - allows for the control of a valve device
|
||||
* **Polling** - represents that poll() can be implemented for the device
|
||||
* **Refresh** - _refresh()_ command for status updates
|
||||
* **Sensor** - detects sensor events
|
||||
|
||||
## Device Health
|
||||
|
||||
SmartThings platform will ping the device after `checkInterval` seconds of inactivity in last attempt to reach the device before marking it `OFFLINE`
|
||||
|
||||
* __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:
|
||||
* [Leak Intelligence Leak Gopher Water Shutoff Valve Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/209631423-Leak-Gopher-Z-Wave-Valve-Control)
|
||||
|
||||
|
||||
@@ -12,16 +12,14 @@
|
||||
*
|
||||
*/
|
||||
metadata {
|
||||
definition (name: "Z-Wave Water Valve", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "oic.d.watervalve") {
|
||||
definition (name: "Z-Wave Water Valve", namespace: "smartthings", author: "SmartThings") {
|
||||
capability "Actuator"
|
||||
capability "Health Check"
|
||||
capability "Valve"
|
||||
capability "Polling"
|
||||
capability "Refresh"
|
||||
capability "Sensor"
|
||||
|
||||
fingerprint deviceId: "0x1006", inClusters: "0x25"
|
||||
fingerprint mfr:"0173", prod:"0003", model:"0002", deviceJoinName: "Leak Intelligence Leak Gopher Water Shutoff Valve"
|
||||
}
|
||||
|
||||
// simulator metadata
|
||||
@@ -37,7 +35,7 @@ metadata {
|
||||
// tile definitions
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name:"valve", type: "generic", width: 6, height: 4, canChangeIcon: true){
|
||||
tileAttribute ("device.valve", key: "PRIMARY_CONTROL") {
|
||||
tileAttribute ("device.contact", key: "PRIMARY_CONTROL") {
|
||||
attributeState "open", label: '${name}', action: "valve.close", icon: "st.valves.water.open", backgroundColor: "#00A0DC", nextState:"closing"
|
||||
attributeState "closed", label: '${name}', action: "valve.open", icon: "st.valves.water.closed", backgroundColor: "#ffffff", nextState:"opening"
|
||||
attributeState "opening", label: '${name}', action: "valve.close", icon: "st.valves.water.open", backgroundColor: "#00A0DC"
|
||||
@@ -45,7 +43,7 @@ metadata {
|
||||
}
|
||||
}
|
||||
|
||||
standardTile("refresh", "device.valve", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
|
||||
standardTile("refresh", "device.contact", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
|
||||
state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
|
||||
}
|
||||
|
||||
@@ -55,32 +53,24 @@ metadata {
|
||||
|
||||
}
|
||||
|
||||
def installed() {
|
||||
// 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])
|
||||
}
|
||||
|
||||
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])
|
||||
response(refresh())
|
||||
}
|
||||
|
||||
def parse(String description) {
|
||||
log.trace "parse description : $description"
|
||||
def result = null
|
||||
def cmd = zwave.parse(description, [0x20: 1])
|
||||
if (cmd) {
|
||||
return zwaveEvent(cmd)
|
||||
result = createEvent(zwaveEvent(cmd))
|
||||
}
|
||||
log.debug "Could not parse message"
|
||||
return null
|
||||
log.debug "Parse returned ${result?.descriptionText}"
|
||||
return result
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) {
|
||||
def value = cmd.value == 0xFF ? "open" : cmd.value == 0x00 ? "closed" : "unknown"
|
||||
|
||||
return [createEventWithDebug([name: "contact", value: value, descriptionText: "$device.displayName valve is $value"]),
|
||||
createEventWithDebug([name: "valve", value: value, descriptionText: "$device.displayName valve is $value"])]
|
||||
def value = cmd.value == 0xFF ? "open" : cmd.value == 0x00 ? "closed" : "unknown"
|
||||
[name: "contact", value: value, descriptionText: "$device.displayName valve is $value"]
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) { //TODO should show MSR when device is discovered
|
||||
@@ -90,22 +80,20 @@ def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerS
|
||||
log.debug "productTypeId: ${cmd.productTypeId}"
|
||||
def msr = String.format("%04X-%04X-%04X", cmd.manufacturerId, cmd.productTypeId, cmd.productId)
|
||||
updateDataValue("MSR", msr)
|
||||
return createEventWithDebug([descriptionText: "$device.displayName MSR: $msr", isStateChange: false])
|
||||
[descriptionText: "$device.displayName MSR: $msr", isStateChange: false]
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.deviceresetlocallyv1.DeviceResetLocallyNotification cmd) {
|
||||
return createEventWithDebug([descriptionText: cmd.toString(), isStateChange: true, displayed: true])
|
||||
[descriptionText: cmd.toString(), isStateChange: true, displayed: true]
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd) {
|
||||
def value = cmd.value == 0xFF ? "open" : cmd.value == 0x00 ? "closed" : "unknown"
|
||||
|
||||
return [createEventWithDebug([name: "contact", value: value, descriptionText: "$device.displayName valve is $value"]),
|
||||
createEventWithDebug([name: "valve", value: value, descriptionText: "$device.displayName valve is $value"])]
|
||||
[name: "contact", value: value, descriptionText: "$device.displayName valve is $value"]
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.Command cmd) {
|
||||
return createEvent([:]) // Handles all Z-Wave commands we aren't interested in
|
||||
[:] // Handles all Z-Wave commands we aren't interested in
|
||||
}
|
||||
|
||||
def open() {
|
||||
@@ -126,13 +114,6 @@ def poll() {
|
||||
zwave.switchBinaryV1.switchBinaryGet().format()
|
||||
}
|
||||
|
||||
/**
|
||||
* PING is used by Device-Watch in attempt to reach the Device
|
||||
* */
|
||||
def ping() {
|
||||
refresh()
|
||||
}
|
||||
|
||||
def refresh() {
|
||||
log.debug "refresh() is called"
|
||||
def commands = [zwave.switchBinaryV1.switchBinaryGet().format()]
|
||||
@@ -141,9 +122,3 @@ def refresh() {
|
||||
}
|
||||
delayBetween(commands,100)
|
||||
}
|
||||
|
||||
def createEventWithDebug(eventMap) {
|
||||
def event = createEvent(eventMap)
|
||||
log.debug "Event created with ${event?.descriptionText}"
|
||||
return event
|
||||
}
|
||||
|
||||
@@ -0,0 +1,568 @@
|
||||
/* TraneXL624.device.groovy
|
||||
*
|
||||
* Variation of the stock SmartThings "Zwave-Thermostat" & Better-Thermostat by todd@wackford.net
|
||||
*
|
||||
*
|
||||
*
|
||||
* Original Modified Code:
|
||||
* twack@wackware.net
|
||||
* 20140209
|
||||
*
|
||||
* Modified to add Humidity & Clock Set
|
||||
* Justin Waymire
|
||||
* justin@waymirenet.com
|
||||
* 04-01-15
|
||||
*
|
||||
*/
|
||||
metadata {
|
||||
// Automatically generated. Make future change here.
|
||||
definition (name: "Trane XL624 Thermostat", author: "justin@waymirenet.com") {
|
||||
capability "Relative Humidity Measurement"
|
||||
capability "Temperature Measurement"
|
||||
capability "Refresh"
|
||||
capability "Thermostat"
|
||||
capability "Configuration"
|
||||
capability "Polling"
|
||||
|
||||
command "heatLevelUp"
|
||||
command "heatLevelDown"
|
||||
command "coolLevelUp"
|
||||
command "coolLevelDown"
|
||||
command "switchMode"
|
||||
command "switchFanMode"
|
||||
}
|
||||
|
||||
// simulator metadata
|
||||
simulator {
|
||||
status "off" : "command: 4003, payload: 00"
|
||||
status "heat" : "command: 4003, payload: 01"
|
||||
status "cool" : "command: 4003, payload: 02"
|
||||
status "auto" : "command: 4003, payload: 03"
|
||||
status "emergencyHeat" : "command: 4003, payload: 04"
|
||||
|
||||
status "fanAuto" : "command: 4403, payload: 00"
|
||||
status "fanOn" : "command: 4403, payload: 01"
|
||||
status "fanCirculate" : "command: 4403, payload: 06"
|
||||
|
||||
status "heat 60" : "command: 4303, payload: 01 01 3C"
|
||||
status "heat 68" : "command: 4303, payload: 01 01 44"
|
||||
status "heat 72" : "command: 4303, payload: 01 01 48"
|
||||
|
||||
status "cool 72" : "command: 4303, payload: 02 01 48"
|
||||
status "cool 76" : "command: 4303, payload: 02 01 4C"
|
||||
status "cool 80" : "command: 4303, payload: 02 01 50"
|
||||
|
||||
status "temp 58" : "command: 3105, payload: 01 22 02 44"
|
||||
status "temp 62" : "command: 3105, payload: 01 22 02 6C"
|
||||
status "temp 70" : "command: 3105, payload: 01 22 02 BC"
|
||||
status "temp 74" : "command: 3105, payload: 01 22 02 E4"
|
||||
status "temp 78" : "command: 3105, payload: 01 22 03 0C"
|
||||
status "temp 82" : "command: 3105, payload: 01 22 03 34"
|
||||
|
||||
status "idle" : "command: 4203, payload: 00"
|
||||
status "heating" : "command: 4203, payload: 01"
|
||||
status "cooling" : "command: 4203, payload: 02"
|
||||
status "fan only" : "command: 4203, payload: 03"
|
||||
status "pending heat" : "command: 4203, payload: 04"
|
||||
status "pending cool" : "command: 4203, payload: 05"
|
||||
status "vent economizer": "command: 4203, payload: 06"
|
||||
|
||||
// reply messages
|
||||
reply "2502": "command: 2503, payload: FF"
|
||||
}
|
||||
|
||||
tiles {
|
||||
valueTile("temperature", "device.temperature", width: 2, height: 2) {
|
||||
state("temperature", label:'${currentValue}°', unit:'F',
|
||||
backgroundColors:[
|
||||
[value: 31, color: "#153591"],
|
||||
[value: 44, color: "#1e9cbb"],
|
||||
[value: 59, color: "#90d2a7"],
|
||||
[value: 74, color: "#44b621"],
|
||||
[value: 84, color: "#f1d801"],
|
||||
[value: 95, color: "#d04e00"],
|
||||
[value: 96, color: "#bc2323"]
|
||||
]
|
||||
)
|
||||
}
|
||||
valueTile("humidity", "device.humidity", inactiveLabel: false, decoration: "flat") {
|
||||
state "humidity", label:'Humidity ${currentValue}%', backgroundColor:"#ffffff"
|
||||
}
|
||||
standardTile("mode", "device.thermostatMode", inactiveLabel: false, decoration: "flat") {
|
||||
state "off", label:'', action:"switchMode", icon:"st.thermostat.heating-cooling-off"
|
||||
state "heat", label:'', action:"switchMode", icon:"st.thermostat.heat"
|
||||
state "emergencyHeat", label:'', action:"switchMode", icon:"st.thermostat.emergency-heat"
|
||||
state "cool", label:'', action:"switchMode", icon:"st.thermostat.cool"
|
||||
state "auto", label:'', action:"switchMode", icon:"st.thermostat.auto"
|
||||
}
|
||||
standardTile("fanMode", "device.thermostatFanMode", inactiveLabel: false, decoration: "flat") {
|
||||
state "fanAuto", label:'', action:"switchFanMode", icon:"st.thermostat.fan-auto"
|
||||
state "fanOn", label:'', action:"switchFanMode", icon:"st.thermostat.fan-on"
|
||||
state "fanCirculate", label:' ', action:"switchFanMode", icon:"st.thermostat.fan-circulate"
|
||||
}
|
||||
valueTile("heatingSetpoint", "device.heatingSetpoint", inactiveLabel: false, decoration: "flat") {
|
||||
state "heat", label:'${currentValue}° heat', unit:"F", backgroundColor:"#ffffff"
|
||||
}
|
||||
valueTile("coolingSetpoint", "device.coolingSetpoint", inactiveLabel: false, decoration: "flat") {
|
||||
state "cool", label:'${currentValue}° cool', unit:"F", backgroundColor:"#ffffff"
|
||||
}
|
||||
standardTile("refresh", "device.thermostatMode", inactiveLabel: false, decoration: "flat") {
|
||||
state "default", action:"polling.poll", icon:"st.secondary.refresh"
|
||||
}
|
||||
standardTile("configure", "device.configure", inactiveLabel: false, decoration: "flat") {
|
||||
state "configure", label:' ', action:"configuration.configure", icon:"st.secondary.configure"
|
||||
}
|
||||
standardTile("heatLevelUp", "device.heatingSetpoint", canChangeIcon: false, inactiveLabel: false, decoration: "flat") {
|
||||
state "heatLevelUp", label:' ', action:"heatLevelUp", icon:"st.thermostat.thermostat-up"
|
||||
}
|
||||
standardTile("heatLevelDown", "device.heatingSetpoint", canChangeIcon: false, inactiveLabel: false, decoration: "flat") {
|
||||
state "heatLevelDown", label:' ', action:"heatLevelDown", icon:"st.thermostat.thermostat-down"
|
||||
}
|
||||
standardTile("coolLevelUp", "device.heatingSetpoint", canChangeIcon: false, inactiveLabel: false, decoration: "flat") {
|
||||
state "coolLevelUp", label:' ', action:"coolLevelUp", icon:"st.thermostat.thermostat-up"
|
||||
}
|
||||
standardTile("coolLevelDown", "device.heatingSetpoint", canChangeIcon: false, inactiveLabel: false, decoration: "flat") {
|
||||
state "coolLevelDown", label:' ', action:"coolLevelDown", icon:"st.thermostat.thermostat-down"
|
||||
}
|
||||
|
||||
main (["temperature", "humidity"])
|
||||
details(["temperature", "humidity", "mode", "heatLevelDown", "heatingSetpoint", "heatLevelUp", "coolLevelDown", "coolingSetpoint", "coolLevelUp", "refresh", "configure"])
|
||||
}
|
||||
}
|
||||
|
||||
def coolLevelUp(){
|
||||
int nextLevel = device.currentValue("coolingSetpoint") + 1
|
||||
|
||||
if( nextLevel > 99){
|
||||
nextLevel = 99
|
||||
}
|
||||
log.debug "Setting cool set point up to: ${nextLevel}"
|
||||
setCoolingSetpoint(nextLevel)
|
||||
}
|
||||
|
||||
def coolLevelDown(){
|
||||
int nextLevel = device.currentValue("coolingSetpoint") - 1
|
||||
|
||||
if( nextLevel < 50){
|
||||
nextLevel = 50
|
||||
}
|
||||
log.debug "Setting cool set point down to: ${nextLevel}"
|
||||
setCoolingSetpoint(nextLevel)
|
||||
}
|
||||
|
||||
def heatLevelUp(){
|
||||
int nextLevel = device.currentValue("heatingSetpoint") + 1
|
||||
|
||||
if( nextLevel > 90){
|
||||
nextLevel = 90
|
||||
}
|
||||
log.debug "Setting heat set point up to: ${nextLevel}"
|
||||
setHeatingSetpoint(nextLevel)
|
||||
}
|
||||
|
||||
def heatLevelDown(){
|
||||
int nextLevel = device.currentValue("heatingSetpoint") - 1
|
||||
|
||||
if( nextLevel < 40){
|
||||
nextLevel = 40
|
||||
}
|
||||
log.debug "Setting heat set point down to: ${nextLevel}"
|
||||
setHeatingSetpoint(nextLevel)
|
||||
}
|
||||
|
||||
def parse(String description)
|
||||
{
|
||||
def map = createEvent(zwaveEvent(zwave.parse(description, [0x42:1, 0x43:2, 0x31: 3])))
|
||||
if (!map) {
|
||||
return null
|
||||
}
|
||||
|
||||
def result = [map]
|
||||
if (map.isStateChange && map.name in ["heatingSetpoint","coolingSetpoint","thermostatMode"]) {
|
||||
def map2 = [
|
||||
name: "thermostatSetpoint",
|
||||
unit: "F"
|
||||
]
|
||||
if (map.name == "thermostatMode") {
|
||||
updateState("lastTriedMode", map.value)
|
||||
if (map.value == "cool") {
|
||||
map2.value = device.latestValue("coolingSetpoint")
|
||||
log.info "THERMOSTAT, latest cooling setpoint = ${map2.value}"
|
||||
}
|
||||
else {
|
||||
map2.value = device.latestValue("heatingSetpoint")
|
||||
log.info "THERMOSTAT, latest heating setpoint = ${map2.value}"
|
||||
}
|
||||
}
|
||||
else {
|
||||
def mode = device.latestValue("thermostatMode")
|
||||
log.info "THERMOSTAT, latest mode = ${mode}"
|
||||
if ((map.name == "heatingSetpoint" && mode == "heat") || (map.name == "coolingSetpoint" && mode == "cool")) {
|
||||
map2.value = map.value
|
||||
map2.unit = map.unit
|
||||
}
|
||||
}
|
||||
if (map2.value != null) {
|
||||
log.debug "THERMOSTAT, adding setpoint event: $map"
|
||||
result << createEvent(map2)
|
||||
}
|
||||
} else if (map.name == "thermostatFanMode" && map.isStateChange) {
|
||||
updateState("lastTriedFanMode", map.value)
|
||||
}
|
||||
log.debug "Parse returned $result"
|
||||
result
|
||||
}
|
||||
|
||||
// Event Generation
|
||||
def zwaveEvent(physicalgraph.zwave.commands.thermostatsetpointv2.ThermostatSetpointReport cmd)
|
||||
{
|
||||
def map = [:]
|
||||
map.value = cmd.scaledValue.toString()
|
||||
map.unit = cmd.scale == 1 ? "F" : "C"
|
||||
map.displayed = false
|
||||
switch (cmd.setpointType) {
|
||||
case 1:
|
||||
map.name = "heatingSetpoint"
|
||||
break;
|
||||
case 2:
|
||||
map.name = "coolingSetpoint"
|
||||
break;
|
||||
default:
|
||||
return [:]
|
||||
}
|
||||
// So we can respond with same format
|
||||
state.size = cmd.size
|
||||
state.scale = cmd.scale
|
||||
state.precision = cmd.precision
|
||||
map
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv3.SensorMultilevelReport cmd)
|
||||
{
|
||||
def map = [:]
|
||||
switch (cmd.sensorType) {
|
||||
case 1:
|
||||
// temperature
|
||||
map.value = cmd.scaledSensorValue.toString()
|
||||
map.unit = cmd.scale == 1 ? "F" : "C"
|
||||
map.name = "temperature"
|
||||
break;
|
||||
case 5:
|
||||
// humidity
|
||||
map.value = cmd.scaledSensorValue.toInteger().toString()
|
||||
map.unit = "%"
|
||||
map.name = "humidity"
|
||||
break;
|
||||
}
|
||||
map
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.thermostatoperatingstatev1.ThermostatOperatingStateReport cmd)
|
||||
{
|
||||
def map = [:]
|
||||
switch (cmd.operatingState) {
|
||||
case physicalgraph.zwave.commands.thermostatoperatingstatev1.ThermostatOperatingStateReport.OPERATING_STATE_IDLE:
|
||||
map.value = "idle"
|
||||
break
|
||||
case physicalgraph.zwave.commands.thermostatoperatingstatev1.ThermostatOperatingStateReport.OPERATING_STATE_HEATING:
|
||||
map.value = "heating"
|
||||
break
|
||||
case physicalgraph.zwave.commands.thermostatoperatingstatev1.ThermostatOperatingStateReport.OPERATING_STATE_COOLING:
|
||||
map.value = "cooling"
|
||||
break
|
||||
case physicalgraph.zwave.commands.thermostatoperatingstatev1.ThermostatOperatingStateReport.OPERATING_STATE_FAN_ONLY:
|
||||
map.value = "fan only"
|
||||
break
|
||||
case physicalgraph.zwave.commands.thermostatoperatingstatev1.ThermostatOperatingStateReport.OPERATING_STATE_PENDING_HEAT:
|
||||
map.value = "pending heat"
|
||||
break
|
||||
case physicalgraph.zwave.commands.thermostatoperatingstatev1.ThermostatOperatingStateReport.OPERATING_STATE_PENDING_COOL:
|
||||
map.value = "pending cool"
|
||||
break
|
||||
case physicalgraph.zwave.commands.thermostatoperatingstatev1.ThermostatOperatingStateReport.OPERATING_STATE_VENT_ECONOMIZER:
|
||||
map.value = "vent economizer"
|
||||
break
|
||||
}
|
||||
map.name = "thermostatOperatingState"
|
||||
map
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport cmd) {
|
||||
def map = [:]
|
||||
switch (cmd.mode) {
|
||||
case physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport.MODE_OFF:
|
||||
map.value = "off"
|
||||
break
|
||||
case physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport.MODE_HEAT:
|
||||
map.value = "heat"
|
||||
break
|
||||
case physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport.MODE_AUXILIARY_HEAT:
|
||||
map.value = "emergencyHeat"
|
||||
break
|
||||
case physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport.MODE_COOL:
|
||||
map.value = "cool"
|
||||
break
|
||||
case physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeReport.MODE_AUTO:
|
||||
map.value = "auto"
|
||||
break
|
||||
}
|
||||
map.name = "thermostatMode"
|
||||
map
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeReport cmd) {
|
||||
def map = [:]
|
||||
switch (cmd.fanMode) {
|
||||
case physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeReport.FAN_MODE_AUTO_LOW:
|
||||
map.value = "fanAuto"
|
||||
break
|
||||
case physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeReport.FAN_MODE_LOW:
|
||||
map.value = "fanOn"
|
||||
break
|
||||
case physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeReport.FAN_MODE_CIRCULATION:
|
||||
map.value = "fanCirculate"
|
||||
break
|
||||
}
|
||||
map.name = "thermostatFanMode"
|
||||
map.displayed = false
|
||||
map
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeSupportedReport cmd) {
|
||||
def supportedModes = ""
|
||||
if(cmd.off) { supportedModes += "off " }
|
||||
if(cmd.heat) { supportedModes += "heat " }
|
||||
if(cmd.auxiliaryemergencyHeat) { supportedModes += "emergencyHeat " }
|
||||
if(cmd.cool) { supportedModes += "cool " }
|
||||
if(cmd.auto) { supportedModes += "auto " }
|
||||
|
||||
updateState("supportedModes", supportedModes)
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeSupportedReport cmd) {
|
||||
def supportedFanModes = ""
|
||||
if(cmd.auto) { supportedFanModes += "fanAuto " }
|
||||
if(cmd.low) { supportedFanModes += "fanOn " }
|
||||
if(cmd.circulation) { supportedFanModes += "fanCirculate " }
|
||||
|
||||
updateState("supportedFanModes", supportedFanModes)
|
||||
}
|
||||
|
||||
def updateState(String name, String value) {
|
||||
state[name] = value
|
||||
device.updateDataValue(name, value)
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) {
|
||||
log.debug "Zwave event received: $cmd"
|
||||
}
|
||||
|
||||
def zwaveEvent(physicalgraph.zwave.Command cmd) {
|
||||
log.warn "Unexpected zwave command $cmd"
|
||||
}
|
||||
|
||||
// Command Implementations
|
||||
def poll() {
|
||||
delayBetween([
|
||||
zwave.sensorMultilevelV3.sensorMultilevelGet().format(), // current temperature
|
||||
zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 1).format(),
|
||||
zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 2).format(),
|
||||
zwave.thermostatModeV2.thermostatModeGet().format(),
|
||||
zwave.thermostatFanModeV3.thermostatFanModeGet().format(),
|
||||
zwave.thermostatOperatingStateV1.thermostatOperatingStateGet().format(),
|
||||
setClock()
|
||||
], 2300)
|
||||
}
|
||||
|
||||
private setClock() { //once a day
|
||||
def nowTime = new Date().time
|
||||
def ageInMinutes = state.lastClockSet ? (nowTime - state.lastClockSet)/60000 : 1440
|
||||
log.debug "Clock set age: ${ageInMinutes} minutes"
|
||||
if (ageInMinutes >= 1440) {
|
||||
log.debug "Setting clock"
|
||||
state.lastClockSet = nowTime
|
||||
def nowCal = Calendar.getInstance(TimeZone.getTimeZone("America/Chicago"));
|
||||
zwave.clockV1.clockSet(hour: nowCal.get(Calendar.HOUR_OF_DAY), minute: nowCal.get(Calendar.MINUTE), weekday: nowCal.get(Calendar.DAY_OF_WEEK)).format()
|
||||
} else "delay 87"
|
||||
}
|
||||
|
||||
def setHeatingSetpoint(degreesF) {
|
||||
setHeatingSetpoint(degreesF.toDouble())
|
||||
}
|
||||
|
||||
def setHeatingSetpoint(Double degreesF) {
|
||||
def p = (state.precision == null) ? 1 : state.precision
|
||||
delayBetween([
|
||||
zwave.thermostatSetpointV1.thermostatSetpointSet(setpointType: 1, scale: 1, precision: p, scaledValue: degreesF).format(),
|
||||
zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 1).format()
|
||||
])
|
||||
}
|
||||
|
||||
def setCoolingSetpoint(degreesF) {
|
||||
setCoolingSetpoint(degreesF.toDouble())
|
||||
}
|
||||
|
||||
def setCoolingSetpoint(Double degreesF) {
|
||||
def p = (state.precision == null) ? 1 : state.precision
|
||||
delayBetween([
|
||||
zwave.thermostatSetpointV1.thermostatSetpointSet(setpointType: 2, scale: 1, precision: p, scaledValue: degreesF).format(),
|
||||
zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 2).format()
|
||||
])
|
||||
}
|
||||
|
||||
def configure() {
|
||||
delayBetween([
|
||||
zwave.thermostatModeV2.thermostatModeSupportedGet().format(),
|
||||
zwave.thermostatFanModeV3.thermostatFanModeSupportedGet().format(),
|
||||
zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:[zwaveHubNodeId]).format()
|
||||
], 2300)
|
||||
}
|
||||
|
||||
def modes() {
|
||||
["off", "auto", "emergencyHeat", "heat", "cool"]
|
||||
}
|
||||
|
||||
def switchMode() {
|
||||
def currentMode = device.currentState("thermostatMode")?.value
|
||||
def lastTriedMode = getDataByName("lastTriedMode") ?: currentMode ?: "off"
|
||||
def supportedModes = getDataByName("supportedModes")
|
||||
def modeOrder = modes()
|
||||
def next = { modeOrder[modeOrder.indexOf(it) + 1] ?: modeOrder[0] }
|
||||
def nextMode = next(lastTriedMode)
|
||||
if (supportedModes?.contains(currentMode)) {
|
||||
while (!supportedModes.contains(nextMode) && nextMode != "off") {
|
||||
nextMode = next(nextMode)
|
||||
}
|
||||
}
|
||||
log.debug "Switching to mode: ${nextMode}"
|
||||
switchToMode(nextMode)
|
||||
}
|
||||
|
||||
def switchToMode(nextMode) {
|
||||
def supportedModes = getDataByName("supportedModes")
|
||||
if(supportedModes && !supportedModes.contains(nextMode)) log.warn "thermostat mode '$nextMode' is not supported"
|
||||
if (nextMode in modes()) {
|
||||
updateState("lastTriedMode", nextMode)
|
||||
return "$nextMode"()
|
||||
} else {
|
||||
log.debug("no mode method '$nextMode'")
|
||||
}
|
||||
}
|
||||
|
||||
def switchFanMode() {
|
||||
def currentMode = device.currentState("thermostatFanMode")?.value
|
||||
def lastTriedMode = getDataByName("lastTriedFanMode") ?: currentMode ?: "off"
|
||||
def supportedModes = getDataByName("supportedFanModes") ?: "fanAuto fanOn"
|
||||
def modeOrder = ["fanAuto", "fanCirculate", "fanOn"]
|
||||
def next = { modeOrder[modeOrder.indexOf(it) + 1] ?: modeOrder[0] }
|
||||
def nextMode = next(lastTriedMode)
|
||||
while (!supportedModes?.contains(nextMode) && nextMode != "fanAuto") {
|
||||
nextMode = next(nextMode)
|
||||
}
|
||||
switchToFanMode(nextMode)
|
||||
}
|
||||
|
||||
def switchToFanMode(nextMode) {
|
||||
def supportedFanModes = getDataByName("supportedFanModes")
|
||||
if(supportedFanModes && !supportedFanModes.contains(nextMode)) log.warn "thermostat mode '$nextMode' is not supported"
|
||||
|
||||
def returnCommand
|
||||
if (nextMode == "fanAuto") {
|
||||
returnCommand = fanAuto()
|
||||
} else if (nextMode == "fanOn") {
|
||||
returnCommand = fanOn()
|
||||
} else if (nextMode == "fanCirculate") {
|
||||
returnCommand = fanCirculate()
|
||||
} else {
|
||||
log.debug("no fan mode '$nextMode'")
|
||||
}
|
||||
if(returnCommand) updateState("lastTriedFanMode", nextMode)
|
||||
returnCommand
|
||||
}
|
||||
|
||||
def getDataByName(String name) {
|
||||
state[name] ?: device.getDataValue(name)
|
||||
}
|
||||
|
||||
def getModeMap() { [
|
||||
"off": 0,
|
||||
"heat": 1,
|
||||
"cool": 2,
|
||||
"emergency heat": 4
|
||||
]}
|
||||
|
||||
def setThermostatMode(String value) {
|
||||
delayBetween([
|
||||
zwave.thermostatModeV2.thermostatModeSet(mode: modeMap[value]).format(),
|
||||
zwave.thermostatModeV2.thermostatModeGet().format()
|
||||
])
|
||||
}
|
||||
|
||||
def getFanModeMap() { [
|
||||
"auto": 0,
|
||||
"on": 1,
|
||||
"circulate": 6
|
||||
]}
|
||||
|
||||
def setThermostatFanMode(String value) {
|
||||
delayBetween([
|
||||
zwave.thermostatFanModeV3.thermostatFanModeSet(fanMode: fanModeMap[value]).format(),
|
||||
zwave.thermostatFanModeV3.thermostatFanModeGet().format()
|
||||
])
|
||||
}
|
||||
|
||||
def off() {
|
||||
delayBetween([
|
||||
zwave.thermostatModeV2.thermostatModeSet(mode: 0).format(),
|
||||
zwave.thermostatModeV2.thermostatModeGet().format()
|
||||
])
|
||||
}
|
||||
|
||||
def heat() {
|
||||
delayBetween([
|
||||
zwave.thermostatModeV2.thermostatModeSet(mode: 1).format(),
|
||||
zwave.thermostatModeV2.thermostatModeGet().format()
|
||||
])
|
||||
}
|
||||
|
||||
def emergencyHeat() {
|
||||
delayBetween([
|
||||
zwave.thermostatModeV2.thermostatModeSet(mode: 4).format(),
|
||||
zwave.thermostatModeV2.thermostatModeGet().format()
|
||||
])
|
||||
}
|
||||
|
||||
def cool() {
|
||||
delayBetween([
|
||||
zwave.thermostatModeV2.thermostatModeSet(mode: 2).format(),
|
||||
zwave.thermostatModeV2.thermostatModeGet().format()
|
||||
])
|
||||
}
|
||||
|
||||
def auto() {
|
||||
delayBetween([
|
||||
zwave.thermostatModeV2.thermostatModeSet(mode: 3).format(),
|
||||
zwave.thermostatModeV2.thermostatModeGet().format()
|
||||
])
|
||||
}
|
||||
|
||||
def fanOn() {
|
||||
delayBetween([
|
||||
zwave.thermostatFanModeV3.thermostatFanModeSet(fanMode: 1).format(),
|
||||
zwave.thermostatFanModeV3.thermostatFanModeGet().format()
|
||||
])
|
||||
}
|
||||
|
||||
def fanAuto() {
|
||||
delayBetween([
|
||||
zwave.thermostatFanModeV3.thermostatFanModeSet(fanMode: 0).format(),
|
||||
zwave.thermostatFanModeV3.thermostatFanModeGet().format()
|
||||
])
|
||||
}
|
||||
|
||||
def fanCirculate() {
|
||||
delayBetween([
|
||||
zwave.thermostatFanModeV3.thermostatFanModeSet(fanMode: 6).format(),
|
||||
zwave.thermostatFanModeV3.thermostatFanModeGet().format()
|
||||
])
|
||||
}
|
||||
@@ -765,6 +765,7 @@ def turnOffSwitch() {
|
||||
} else {
|
||||
|
||||
device.off();
|
||||
|
||||
return [Device_id: params.id, result_action: "200"]
|
||||
}
|
||||
}
|
||||
@@ -788,7 +789,6 @@ def getTempSensorsStatus(id) {
|
||||
return []
|
||||
} else {
|
||||
def bat = getBatteryStatus(device.id)
|
||||
def scale = [Scale: location.temperatureScale]
|
||||
return [temperature: device.currentValue('temperature')] + bat + scale
|
||||
return [temperature: device.currentValue('temperature')] + bat
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,66 +15,66 @@ definition(
|
||||
)
|
||||
|
||||
preferences {
|
||||
section("Light switches to turn off") {
|
||||
input "switches", "capability.switch", title: "Choose light switches", multiple: true
|
||||
}
|
||||
section("Turn off when there is no motion and presence") {
|
||||
input "motionSensor", "capability.motionSensor", title: "Choose motion sensor"
|
||||
input "presenceSensors", "capability.presenceSensor", title: "Choose presence sensors", multiple: true
|
||||
}
|
||||
section("Delay before turning off") {
|
||||
input "delayMins", "number", title: "Minutes of inactivity?"
|
||||
}
|
||||
section("Light switches to turn off") {
|
||||
input "switches", "capability.switch", title: "Choose light switches", multiple: true
|
||||
}
|
||||
section("Turn off when there is no motion and presence") {
|
||||
input "motionSensor", "capability.motionSensor", title: "Choose motion sensor"
|
||||
input "presenceSensors", "capability.presenceSensor", title: "Choose presence sensors", multiple: true
|
||||
}
|
||||
section("Delay before turning off") {
|
||||
input "delayMins", "number", title: "Minutes of inactivity?"
|
||||
}
|
||||
}
|
||||
|
||||
def installed() {
|
||||
subscribe(motionSensor, "motion", motionHandler)
|
||||
subscribe(presenceSensors, "presence", presenceHandler)
|
||||
subscribe(motionSensor, "motion", motionHandler)
|
||||
subscribe(presenceSensors, "presence", presenceHandler)
|
||||
}
|
||||
|
||||
def updated() {
|
||||
unsubscribe()
|
||||
subscribe(motionSensor, "motion", motionHandler)
|
||||
subscribe(presenceSensors, "presence", presenceHandler)
|
||||
unsubscribe()
|
||||
subscribe(motionSensor, "motion", motionHandler)
|
||||
subscribe(presenceSensors, "presence", presenceHandler)
|
||||
}
|
||||
|
||||
def motionHandler(evt) {
|
||||
log.debug "handler $evt.name: $evt.value"
|
||||
if (evt.value == "inactive") {
|
||||
runIn(delayMins * 60, scheduleCheck, [overwrite: true])
|
||||
}
|
||||
log.debug "handler $evt.name: $evt.value"
|
||||
if (evt.value == "inactive") {
|
||||
runIn(delayMins * 60, scheduleCheck, [overwrite: false])
|
||||
}
|
||||
}
|
||||
|
||||
def presenceHandler(evt) {
|
||||
log.debug "handler $evt.name: $evt.value"
|
||||
if (evt.value == "not present") {
|
||||
runIn(delayMins * 60, scheduleCheck, [overwrite: true])
|
||||
}
|
||||
log.debug "handler $evt.name: $evt.value"
|
||||
if (evt.value == "not present") {
|
||||
runIn(delayMins * 60, scheduleCheck, [overwrite: false])
|
||||
}
|
||||
}
|
||||
|
||||
def isActivePresence() {
|
||||
// check all the presence sensors, make sure none are present
|
||||
def noPresence = presenceSensors.find{it.currentPresence == "present"} == null
|
||||
!noPresence
|
||||
// check all the presence sensors, make sure none are present
|
||||
def noPresence = presenceSensors.find{it.currentPresence == "present"} == null
|
||||
!noPresence
|
||||
}
|
||||
|
||||
def scheduleCheck() {
|
||||
log.debug "scheduled check"
|
||||
def motionState = motionSensor.currentState("motion")
|
||||
log.debug "scheduled check"
|
||||
def motionState = motionSensor.currentState("motion")
|
||||
if (motionState.value == "inactive") {
|
||||
def elapsed = now() - motionState.rawDateCreated.time
|
||||
def threshold = 1000 * 60 * delayMins - 1000
|
||||
if (elapsed >= threshold) {
|
||||
if (!isActivePresence()) {
|
||||
log.debug "Motion has stayed inactive since last check ($elapsed ms) and no presence: turning lights off"
|
||||
switches.off()
|
||||
} else {
|
||||
log.debug "Presence is active: do nothing"
|
||||
def elapsed = now() - motionState.rawDateCreated.time
|
||||
def threshold = 1000 * 60 * delayMins - 1000
|
||||
if (elapsed >= threshold) {
|
||||
if (!isActivePresence()) {
|
||||
log.debug "Motion has stayed inactive since last check ($elapsed ms) and no presence: turning lights off"
|
||||
switches.off()
|
||||
} else {
|
||||
log.debug "Presence is active: do nothing"
|
||||
}
|
||||
} else {
|
||||
log.debug "Motion has not stayed inactive long enough since last check ($elapsed ms): do nothing"
|
||||
}
|
||||
} else {
|
||||
log.debug "Motion has not stayed inactive long enough since last check ($elapsed ms): do nothing"
|
||||
}
|
||||
} else {
|
||||
log.debug "Motion is active: do nothing"
|
||||
log.debug "Motion is active: do nothing"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,3 @@
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.security.InvalidKeyException;
|
||||
|
||||
/**
|
||||
* OpenT2T SmartApp Test
|
||||
*
|
||||
@@ -43,7 +39,7 @@ definition(
|
||||
* garageDoors | door | open, close | unknown, closed, open, closing, opening
|
||||
* cameras | image | take | <String>
|
||||
* thermostats | thermostat | setHeatingSetpoint, | temperature, heatingSetpoint, coolingSetpoint,
|
||||
* | | setCoolingSetpoint, | thermostatSetpoint, thermostatMode,
|
||||
* | | setCoolingSetpoint, | thermostatSetpoint, thermostatMode,
|
||||
* | | off, heat, cool, auto,| thermostatFanMode, thermostatOperatingState
|
||||
* | | emergencyHeat, |
|
||||
* | | setThermostatMode, |
|
||||
@@ -56,109 +52,86 @@ definition(
|
||||
//Device Inputs
|
||||
preferences {
|
||||
section("Allow OpenT2T to control these things...") {
|
||||
input "contactSensors", "capability.contactSensor", title: "Which Contact Sensors", multiple: true, required: false, hideWhenEmpty: true
|
||||
input "garageDoors", "capability.garageDoorControl", title: "Which Garage Doors?", multiple: true, required: false, hideWhenEmpty: true
|
||||
input "locks", "capability.lock", title: "Which Locks?", multiple: true, required: false, hideWhenEmpty: true
|
||||
input "cameras", "capability.videoCapture", title: "Which Cameras?", multiple: true, required: false, hideWhenEmpty: true
|
||||
input "motionSensors", "capability.motionSensor", title: "Which Motion Sensors?", multiple: true, required: false, hideWhenEmpty: true
|
||||
input "presenceSensors", "capability.presenceSensor", title: "Which Presence Sensors", multiple: true, required: false, hideWhenEmpty: true
|
||||
input "switches", "capability.switch", title: "Which Switches and Lights?", multiple: true, required: false, hideWhenEmpty: true
|
||||
input "thermostats", "capability.thermostat", title: "Which Thermostat?", multiple: true, required: false, hideWhenEmpty: true
|
||||
input "waterSensors", "capability.waterSensor", title: "Which Water Leak Sensors?", multiple: true, required: false, hideWhenEmpty: true
|
||||
input "contactSensors", "capability.contactSensor", title: "Which Contact Sensors", multiple: true, required: false
|
||||
input "garageDoors", "capability.garageDoorControl", title: "Which Garage Doors?", multiple: true, required: false
|
||||
input "locks", "capability.lock", title: "Which Locks?", multiple: true, required: false
|
||||
input "cameras", "capability.videoCapture", title: "Which Cameras?", multiple: true, required: false
|
||||
input "motionSensors", "capability.motionSensor", title: "Which Motion Sensors?", multiple: true, required: false
|
||||
input "presenceSensors", "capability.presenceSensor", title: "Which Presence Sensors", multiple: true, required: false
|
||||
input "switches", "capability.switch", title: "Which Switches and Lights?", multiple: true, required: false
|
||||
input "thermostats", "capability.thermostat", title: "Which Thermostat?", multiple: true, required: false
|
||||
input "waterSensors", "capability.waterSensor", title: "Which Water Leak Sensors?", multiple: true, required: false
|
||||
}
|
||||
}
|
||||
|
||||
def getInputs() {
|
||||
def inputList = []
|
||||
inputList += contactSensors ?: []
|
||||
inputList += garageDoors ?: []
|
||||
inputList += locks ?: []
|
||||
inputList += cameras ?: []
|
||||
inputList += motionSensors ?: []
|
||||
inputList += presenceSensors ?: []
|
||||
inputList += switches ?: []
|
||||
inputList += thermostats ?: []
|
||||
inputList += waterSensors ?: []
|
||||
inputList += contactSensors?: []
|
||||
inputList += garageDoors?: []
|
||||
inputList += locks?: []
|
||||
inputList += cameras?: []
|
||||
inputList += motionSensors?: []
|
||||
inputList += presenceSensors?: []
|
||||
inputList += switches?: []
|
||||
inputList += thermostats?: []
|
||||
inputList += waterSensors?: []
|
||||
return inputList
|
||||
}
|
||||
|
||||
//API external Endpoints
|
||||
mappings {
|
||||
path("/subscriptionURL/:url") {
|
||||
action: [
|
||||
PUT: "updateEndpointURL"
|
||||
]
|
||||
}
|
||||
path("/connectionId/:connId") {
|
||||
action: [
|
||||
PUT: "updateConnectionId"
|
||||
]
|
||||
}
|
||||
path("/devices") {
|
||||
action:
|
||||
[
|
||||
action: [
|
||||
GET: "getDevices"
|
||||
]
|
||||
}
|
||||
path("/devices/:id") {
|
||||
action:
|
||||
[
|
||||
action: [
|
||||
GET: "getDevice"
|
||||
]
|
||||
}
|
||||
path("/update/:id") {
|
||||
action:
|
||||
[
|
||||
action: [
|
||||
PUT: "updateDevice"
|
||||
]
|
||||
}
|
||||
path("/deviceSubscription") {
|
||||
action:
|
||||
[
|
||||
POST : "registerDeviceChange",
|
||||
path("/subscription/:id") {
|
||||
action: [
|
||||
POST: "registerDeviceChange",
|
||||
DELETE: "unregisterDeviceChange"
|
||||
]
|
||||
}
|
||||
path("/locationSubscription") {
|
||||
action:
|
||||
[
|
||||
POST : "registerDeviceGraph",
|
||||
DELETE: "unregisterDeviceGraph"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
def installed() {
|
||||
log.debug "Installing with settings: ${settings}"
|
||||
log.debug "Installed with settings: ${settings}"
|
||||
initialize()
|
||||
}
|
||||
|
||||
def updated() {
|
||||
log.debug "Updating with settings: ${settings}"
|
||||
|
||||
//Initialize state variables if didn't exist.
|
||||
if (state.deviceSubscriptionMap == null) {
|
||||
state.deviceSubscriptionMap = [:]
|
||||
log.debug "deviceSubscriptionMap created."
|
||||
}
|
||||
if (state.locationSubscriptionMap == null) {
|
||||
state.locationSubscriptionMap = [:]
|
||||
log.debug "locationSubscriptionMap created."
|
||||
}
|
||||
if (state.verificationKeyMap == null) {
|
||||
state.verificationKeyMap = [:]
|
||||
log.debug "verificationKeyMap created."
|
||||
}
|
||||
|
||||
log.debug "Updated with settings: ${settings}"
|
||||
unsubscribe()
|
||||
registerAllDeviceSubscriptions()
|
||||
registerSubscriptions()
|
||||
}
|
||||
|
||||
def initialize() {
|
||||
log.debug "Initializing with settings: ${settings}"
|
||||
state.deviceSubscriptionMap = [:]
|
||||
log.debug "deviceSubscriptionMap created."
|
||||
state.locationSubscriptionMap = [:]
|
||||
log.debug "locationSubscriptionMap created."
|
||||
state.verificationKeyMap = [:]
|
||||
log.debug "verificationKeyMap created."
|
||||
registerAllDeviceSubscriptions()
|
||||
state.connectionId = ""
|
||||
state.endpointURL = "https://ifs.windows-int.com/v1/cb/81C7E77B-EABC-488A-B2BF-FEC42F0DABD2/notify"
|
||||
registerSubscriptions()
|
||||
}
|
||||
|
||||
/*** Subscription Functions ***/
|
||||
|
||||
//Subscribe events for all devices
|
||||
def registerAllDeviceSubscriptions() {
|
||||
def registerSubscriptions() {
|
||||
registerChangeHandler(inputs)
|
||||
}
|
||||
|
||||
@@ -166,249 +139,102 @@ def registerAllDeviceSubscriptions() {
|
||||
def registerChangeHandler(myList) {
|
||||
myList.each { myDevice ->
|
||||
def theAtts = myDevice.supportedAttributes
|
||||
theAtts.each { att ->
|
||||
subscribe(myDevice, att.name, deviceEventHandler)
|
||||
log.info "Registering for ${myDevice.displayName}.${att.name}"
|
||||
theAtts.each {att ->
|
||||
subscribe(myDevice, att.name, eventHandler)
|
||||
log.info "Registering ${myDevice.displayName}.${att.name}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Endpoints function: Subscribe to events from a specific device
|
||||
def registerDeviceChange() {
|
||||
def subscriptionEndpt = params.subscriptionURL
|
||||
def deviceId = params.deviceId
|
||||
def myDevice = findDevice(deviceId)
|
||||
|
||||
if (myDevice == null) {
|
||||
httpError(404, "Cannot find device with device ID ${deviceId}.")
|
||||
}
|
||||
|
||||
def myDevice = findDevice(params.id)
|
||||
def theAtts = myDevice.supportedAttributes
|
||||
try {
|
||||
theAtts.each { att ->
|
||||
subscribe(myDevice, att.name, deviceEventHandler)
|
||||
}
|
||||
log.info "Subscribing for ${myDevice.displayName}"
|
||||
|
||||
if (subscriptionEndpt != null) {
|
||||
if (state.deviceSubscriptionMap[deviceId] == null) {
|
||||
state.deviceSubscriptionMap.put(deviceId, [subscriptionEndpt])
|
||||
log.info "Added subscription URL: ${subscriptionEndpt} for ${myDevice.displayName}"
|
||||
} else if (!state.deviceSubscriptionMap[deviceId].contains(subscriptionEndpt)) {
|
||||
state.deviceSubscriptionMap[deviceId] << subscriptionEndpt
|
||||
log.info "Added subscription URL: ${subscriptionEndpt} for ${myDevice.displayName}"
|
||||
}
|
||||
|
||||
if (params.key != null) {
|
||||
state.verificationKeyMap[subscriptionEndpt] = params.key
|
||||
log.info "Added verification key: ${params.key} for ${subscriptionEndpt}"
|
||||
}
|
||||
theAtts.each {att ->
|
||||
subscribe(myDevice, att.name, eventHandler)
|
||||
log.info "Registering ${myDevice.displayName}.${att.name}"
|
||||
}
|
||||
return ["succeed"]
|
||||
} catch (e) {
|
||||
httpError(500, "something went wrong: $e")
|
||||
}
|
||||
|
||||
log.info "Current subscription map is ${state.deviceSubscriptionMap}"
|
||||
log.info "Current verification key map is ${state.verificationKeyMap}"
|
||||
return ["succeed"]
|
||||
}
|
||||
|
||||
//Endpoints function: Unsubscribe to events from a specific device
|
||||
def unregisterDeviceChange() {
|
||||
def subscriptionEndpt = params.subscriptionURL
|
||||
def deviceId = params.deviceId
|
||||
def myDevice = findDevice(deviceId)
|
||||
|
||||
if (myDevice == null) {
|
||||
httpError(404, "Cannot find device with device ID ${deviceId}.")
|
||||
}
|
||||
|
||||
def myDevice = findDevice(params.id)
|
||||
try {
|
||||
if (subscriptionEndpt != null && subscriptionEndpt != "undefined") {
|
||||
if (state.deviceSubscriptionMap[deviceId]?.contains(subscriptionEndpt)) {
|
||||
if (state.deviceSubscriptionMap[deviceId].size() == 1) {
|
||||
state.deviceSubscriptionMap.remove(deviceId)
|
||||
} else {
|
||||
state.deviceSubscriptionMap[deviceId].remove(subscriptionEndpt)
|
||||
}
|
||||
state.verificationKeyMap.remove(subscriptionEndpt)
|
||||
log.info "Removed subscription URL: ${subscriptionEndpt} for ${myDevice.displayName}"
|
||||
}
|
||||
} else {
|
||||
state.deviceSubscriptionMap.remove(deviceId)
|
||||
log.info "Unsubscriping for ${myDevice.displayName}"
|
||||
}
|
||||
} catch (e) {
|
||||
httpError(500, "something went wrong: $e")
|
||||
}
|
||||
|
||||
log.info "Current subscription map is ${state.deviceSubscriptionMap}"
|
||||
log.info "Current verification key map is ${state.verificationKeyMap}"
|
||||
}
|
||||
|
||||
//Endpoints function: Subscribe to device additiona/removal updated in a location
|
||||
def registerDeviceGraph() {
|
||||
def subscriptionEndpt = params.subscriptionURL
|
||||
|
||||
if (subscriptionEndpt != null && subscriptionEndpt != "undefined") {
|
||||
subscribe(location, "DeviceCreated", locationEventHandler, [filterEvents: false])
|
||||
subscribe(location, "DeviceUpdated", locationEventHandler, [filterEvents: false])
|
||||
subscribe(location, "DeviceDeleted", locationEventHandler, [filterEvents: false])
|
||||
|
||||
if (state.locationSubscriptionMap[location.id] == null) {
|
||||
state.locationSubscriptionMap.put(location.id, [subscriptionEndpt])
|
||||
log.info "Added subscription URL: ${subscriptionEndpt} for Location ${location.name}"
|
||||
} else if (!state.locationSubscriptionMap[location.id].contains(subscriptionEndpt)) {
|
||||
state.locationSubscriptionMap[location.id] << subscriptionEndpt
|
||||
log.info "Added subscription URL: ${subscriptionEndpt} for Location ${location.name}"
|
||||
}
|
||||
|
||||
if (params.key != null) {
|
||||
state.verificationKeyMap[subscriptionEndpt] = params.key
|
||||
log.info "Added verification key: ${params.key} for ${subscriptionEndpt}"
|
||||
}
|
||||
|
||||
log.info "Current location subscription map is ${state.locationSubscriptionMap}"
|
||||
log.info "Current verification key map is ${state.verificationKeyMap}"
|
||||
unsubscribe(myDevice)
|
||||
log.info "Unregistering ${myDevice.displayName}"
|
||||
return ["succeed"]
|
||||
} else {
|
||||
httpError(400, "missing input parameter: subscriptionURL")
|
||||
}
|
||||
}
|
||||
|
||||
//Endpoints function: Unsubscribe to events from a specific device
|
||||
def unregisterDeviceGraph() {
|
||||
def subscriptionEndpt = params.subscriptionURL
|
||||
|
||||
try {
|
||||
if (subscriptionEndpt != null && subscriptionEndpt != "undefined") {
|
||||
if (state.locationSubscriptionMap[location.id]?.contains(subscriptionEndpt)) {
|
||||
if (state.locationSubscriptionMap[location.id].size() == 1) {
|
||||
state.locationSubscriptionMap.remove(location.id)
|
||||
} else {
|
||||
state.locationSubscriptionMap[location.id].remove(subscriptionEndpt)
|
||||
}
|
||||
state.verificationKeyMap.remove(subscriptionEndpt)
|
||||
log.info "Removed subscription URL: ${subscriptionEndpt} for Location ${location.name}"
|
||||
}
|
||||
} else {
|
||||
httpError(400, "missing input parameter: subscriptionURL")
|
||||
}
|
||||
} catch (e) {
|
||||
httpError(500, "something went wrong: $e")
|
||||
}
|
||||
|
||||
log.info "Current location subscription map is ${state.locationSubscriptionMap}"
|
||||
log.info "Current verification key map is ${state.verificationKeyMap}"
|
||||
}
|
||||
|
||||
//When events are triggered, send HTTP post to web socket servers
|
||||
def deviceEventHandler(evt) {
|
||||
def evtDevice = evt.device
|
||||
def evtDeviceType = getDeviceType(evtDevice)
|
||||
def deviceData = [];
|
||||
def eventHandler(evt) {
|
||||
def evt_device_id = evt.deviceId
|
||||
def evt_device_value = evt.value
|
||||
def evt_name = evt.name
|
||||
def evt_device = evt.device
|
||||
def evt_deviceType = getDeviceType(evt_device);
|
||||
def deviceInfo
|
||||
|
||||
if (evt.data != null) {
|
||||
def evtData = parseJson(evt.data)
|
||||
log.info "Received event for ${evtDevice.displayName}, data: ${evtData}, description: ${evt.descriptionText}"
|
||||
if(evt_deviceType == "thermostat")
|
||||
{
|
||||
deviceInfo = [name: evt_device.displayName, id: evt_device.id, status:evt_device.getStatus(), deviceType:evt_deviceType, manufacturer:evt_device.getManufacturerName(), model:evt_device.getModelName(), attributes: deviceAttributeList(evt_device), locationMode: getLocationModeInfo()]
|
||||
}
|
||||
else
|
||||
{
|
||||
deviceInfo = [name: evt_device.displayName, id: evt_device.id, status:evt_device.getStatus(), deviceType:evt_deviceType, manufacturer:evt_device.getManufacturerName(), model:evt_device.getModelName(), attributes: deviceAttributeList(evt_device)]
|
||||
}
|
||||
|
||||
if (evtDeviceType == "thermostat") {
|
||||
deviceData = [name: evtDevice.displayName, id: evtDevice.id, status: evtDevice.status, deviceType: evtDeviceType, manufacturer: evtDevice.manufacturerName, model: evtDevice.modelName, attributes: deviceAttributeList(evtDevice, evtDeviceType), locationMode: getLocationModeInfo(), locationId: location.id]
|
||||
} else {
|
||||
deviceData = [name: evtDevice.displayName, id: evtDevice.id, status: evtDevice.status, deviceType: evtDeviceType, manufacturer: evtDevice.manufacturerName, model: evtDevice.modelName, attributes: deviceAttributeList(evtDevice, evtDeviceType), locationId: location.id]
|
||||
}
|
||||
|
||||
def params = [body: deviceData]
|
||||
|
||||
//send event to all subscriptions urls
|
||||
log.debug "Current subscription urls for ${evtDevice.displayName} is ${state.deviceSubscriptionMap[evtDevice.id]}"
|
||||
state.deviceSubscriptionMap[evtDevice.id].each {
|
||||
params.uri = "${it}"
|
||||
if (state.verificationKeyMap[it] != null) {
|
||||
def key = state.verificationKeyMap[it]
|
||||
params.header = [Signature: ComputHMACValue(key, groovy.json.JsonOutput.toJson(params.body))]
|
||||
}
|
||||
log.trace "POST URI: ${params.uri}"
|
||||
log.trace "Header: ${params.header}"
|
||||
log.trace "Payload: ${params.body}"
|
||||
try {
|
||||
httpPostJson(params) { resp ->
|
||||
log.trace "response status code: ${resp.status}"
|
||||
log.trace "response data: ${resp.data}"
|
||||
}
|
||||
} catch (e) {
|
||||
log.error "something went wrong: $e"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def locationEventHandler(evt) {
|
||||
log.info "Received event for location ${location.name}/${location.id}, Event: ${evt.name}, description: ${evt.descriptionText}, apiServerUrl: ${apiServerUrl("")}"
|
||||
switch (evt.name) {
|
||||
case "DeviceCreated":
|
||||
case "DeviceDeleted":
|
||||
def evtDevice = evt.device
|
||||
def evtDeviceType = getDeviceType(evtDevice)
|
||||
def params = [body: [eventType: evt.name, deviceId: evtDevice.id, locationId: location.id]]
|
||||
|
||||
if (evt.name == "DeviceDeleted" && state.deviceSubscriptionMap[deviceId] != null) {
|
||||
state.deviceSubscriptionMap.remove(evtDevice.id)
|
||||
}
|
||||
|
||||
state.locationSubscriptionMap[location.id].each {
|
||||
params.uri = "${it}"
|
||||
if (state.verificationKeyMap[it] != null) {
|
||||
def key = state.verificationKeyMap[it]
|
||||
params.header = [Signature: ComputHMACValue(key, groovy.json.JsonOutput.toJson(params.body))]
|
||||
}
|
||||
log.trace "POST URI: ${params.uri}"
|
||||
log.trace "Header: ${params.header}"
|
||||
log.trace "Payload: ${params.body}"
|
||||
try {
|
||||
httpPostJson(params) { resp ->
|
||||
log.trace "response status code: ${resp.status}"
|
||||
log.trace "response data: ${resp.data}"
|
||||
}
|
||||
} catch (e) {
|
||||
log.error "something went wrong: $e"
|
||||
}
|
||||
}
|
||||
case "DeviceUpdated":
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
private ComputHMACValue(key, data) {
|
||||
def params = [
|
||||
uri: "${state.endpointURL}/${state.connectionId}",
|
||||
body: [ deviceInfo ]
|
||||
]
|
||||
try {
|
||||
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA1")
|
||||
Mac mac = Mac.getInstance("HmacSHA1")
|
||||
mac.init(secretKeySpec)
|
||||
byte[] digest = mac.doFinal(data.getBytes("UTF-8"))
|
||||
return byteArrayToString(digest)
|
||||
} catch (InvalidKeyException e) {
|
||||
log.error "Invalid key exception while converting to HMac SHA1"
|
||||
log.trace "POST URI: ${params.uri}"
|
||||
log.trace "Payload: ${params.body}"
|
||||
httpPostJson(params) { resp ->
|
||||
resp.headers.each {
|
||||
log.debug "${it.name} : ${it.value}"
|
||||
}
|
||||
log.trace "response status code: ${resp.status}"
|
||||
log.trace "response data: ${resp.data}"
|
||||
}
|
||||
} catch (e) {
|
||||
log.debug "something went wrong: $e"
|
||||
}
|
||||
}
|
||||
|
||||
private def byteArrayToString(byte[] data) {
|
||||
BigInteger bigInteger = new BigInteger(1, data)
|
||||
String hash = bigInteger.toString(16)
|
||||
return hash
|
||||
//Endpoints function: update subcription endpoint url [state.endpoint]
|
||||
void updateEndpointURL() {
|
||||
state.endpointURL = params.url
|
||||
log.info "Updated EndpointURL to ${state.endpointURL}"
|
||||
}
|
||||
|
||||
/*** Device Query/Update Functions ***/
|
||||
//Endpoints function: update global variable [state.connectionId]
|
||||
void updateConnectionId() {
|
||||
def connId = params.connId
|
||||
state.connectionId = connId
|
||||
log.info "Updated ConnectionID to ${state.connectionId}"
|
||||
}
|
||||
|
||||
//Endpoints function: return all device data in json format
|
||||
def getDevices() {
|
||||
def deviceData = []
|
||||
inputs?.each {
|
||||
def deviceType = getDeviceType(it)
|
||||
if (deviceType == "thermostat") {
|
||||
deviceData << [name: it.displayName, id: it.id, status: it.status, deviceType: deviceType, manufacturer: it.manufacturerName, model: it.modelName, attributes: deviceAttributeList(it, deviceType), locationMode: getLocationModeInfo()]
|
||||
} else {
|
||||
deviceData << [name: it.displayName, id: it.id, status: it.status, deviceType: deviceType, manufacturer: it.manufacturerName, model: it.modelName, attributes: deviceAttributeList(it, deviceType)]
|
||||
if(deviceType == "thermostat")
|
||||
{
|
||||
deviceData << [name: it.displayName, id: it.id, status:it.getStatus(), deviceType:deviceType, manufacturer:it.getManufacturerName(), model:it.getModelName(), attributes: deviceAttributeList(it), locationMode: getLocationModeInfo()]
|
||||
}
|
||||
else
|
||||
{
|
||||
deviceData << [name: it.displayName, id: it.id, status:it.getStatus(), deviceType:deviceType, manufacturer:it.getManufacturerName(), model:it.getModelName(), attributes: deviceAttributeList(it)]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -421,12 +247,14 @@ def getDevice() {
|
||||
def it = findDevice(params.id)
|
||||
def deviceType = getDeviceType(it)
|
||||
def device
|
||||
if (deviceType == "thermostat") {
|
||||
device = [name: it.displayName, id: it.id, status: it.status, deviceType: deviceType, manufacturer: it.manufacturerName, model: it.modelName, attributes: deviceAttributeList(it, deviceType), locationMode: getLocationModeInfo()]
|
||||
} else {
|
||||
device = [name: it.displayName, id: it.id, status: it.status, deviceType: deviceType, manufacturer: it.manufacturerName, model: it.modelName, attributes: deviceAttributeList(it, deviceType)]
|
||||
if(deviceType == "thermostat")
|
||||
{
|
||||
device = [name: it.displayName, id: it.id, status:it.getStatus(), deviceType:deviceType, manufacturer:it.getManufacturerName(), model:it.getModelName(), attributes: deviceAttributeList(it), locationMode: getLocationModeInfo()]
|
||||
}
|
||||
else
|
||||
{
|
||||
device = [name: it.displayName, id: it.id, status:it.getStatus(), deviceType:deviceType, manufacturer:it.getManufacturerName(), model:it.getModelName(), attributes: deviceAttributeList(it)]
|
||||
}
|
||||
|
||||
log.debug "getDevice, return: ${device}"
|
||||
return device
|
||||
}
|
||||
@@ -437,18 +265,18 @@ void updateDevice() {
|
||||
request.JSON.each {
|
||||
def command = it.key
|
||||
def value = it.value
|
||||
if (command) {
|
||||
if (command){
|
||||
def commandList = mapDeviceCommands(command, value)
|
||||
command = commandList[0]
|
||||
value = commandList[1]
|
||||
|
||||
if (command == "setAwayMode") {
|
||||
log.info "Setting away mode to ${value}"
|
||||
if (location.modes?.find { it.name == value }) {
|
||||
if (location.modes?.find {it.name == value}) {
|
||||
location.setMode(value)
|
||||
}
|
||||
} else if (command == "thermostatSetpoint") {
|
||||
switch (device.currentThermostatMode) {
|
||||
}else if (command == "thermostatSetpoint"){
|
||||
switch(device.currentThermostatMode){
|
||||
case "cool":
|
||||
log.info "Update: ${device.displayName}, [${command}, ${value}]"
|
||||
device.setCoolingSetpoint(value)
|
||||
@@ -462,7 +290,7 @@ void updateDevice() {
|
||||
httpError(501, "this mode: ${device.currentThermostatMode} does not allow changing thermostat setpoint.")
|
||||
break
|
||||
}
|
||||
} else if (!device) {
|
||||
}else if (!device) {
|
||||
log.error "updateDevice, Device not found"
|
||||
httpError(404, "Device not found")
|
||||
} else if (!device.hasCommand(command)) {
|
||||
@@ -472,11 +300,11 @@ void updateDevice() {
|
||||
if (command == "setColor") {
|
||||
log.info "Update: ${device.displayName}, [${command}, ${value}]"
|
||||
device."$command"(hex: value)
|
||||
} else if (value.isNumber()) {
|
||||
} else if(value.isNumber()) {
|
||||
def intValue = value as Integer
|
||||
log.info "Update: ${device.displayName}, [${command}, ${intValue}(int)]"
|
||||
device."$command"(intValue)
|
||||
} else if (value) {
|
||||
} else if (value){
|
||||
log.info "Update: ${device.displayName}, [${command}, ${value}]"
|
||||
device."$command"(value)
|
||||
} else {
|
||||
@@ -503,16 +331,17 @@ private getDeviceType(device) {
|
||||
log.debug "supported commands: [${device}, ${device.supportedCommands}]"
|
||||
|
||||
//Loop through the device capability list to determine the device type.
|
||||
capabilities.each { capability ->
|
||||
switch (capability.name.toLowerCase()) {
|
||||
capabilities.each {capability ->
|
||||
switch(capability.name.toLowerCase())
|
||||
{
|
||||
case "switch":
|
||||
deviceType = "switch"
|
||||
|
||||
//If the device also contains "Switch Level" capability, identify it as a "light" device.
|
||||
if (capabilities.any { it.name.toLowerCase() == "switch level" }) {
|
||||
if (capabilities.any{it.name.toLowerCase() == "switch level"}){
|
||||
|
||||
//If the device also contains "Power Meter" capability, identify it as a "dimmerSwitch" device.
|
||||
if (capabilities.any { it.name.toLowerCase() == "power meter" }) {
|
||||
if (capabilities.any{it.name.toLowerCase() == "power meter"}){
|
||||
deviceType = "dimmerSwitch"
|
||||
return deviceType
|
||||
} else {
|
||||
@@ -521,6 +350,9 @@ private getDeviceType(device) {
|
||||
}
|
||||
}
|
||||
break
|
||||
case "contact sensor":
|
||||
deviceType = "contactSensor"
|
||||
return deviceType
|
||||
case "garageDoorControl":
|
||||
deviceType = "garageDoor"
|
||||
return deviceType
|
||||
@@ -530,15 +362,17 @@ private getDeviceType(device) {
|
||||
case "video camera":
|
||||
deviceType = "camera"
|
||||
return deviceType
|
||||
case "motion sensor":
|
||||
deviceType = "motionSensor"
|
||||
return deviceType
|
||||
case "presence sensor":
|
||||
deviceType = "presenceSensor"
|
||||
return deviceType
|
||||
case "thermostat":
|
||||
deviceType = "thermostat"
|
||||
return deviceType
|
||||
case "acceleration sensor":
|
||||
case "contact sensor":
|
||||
case "motion sensor":
|
||||
case "presence sensor":
|
||||
case "water sensor":
|
||||
deviceType = "genericSensor"
|
||||
deviceType = "waterSensor"
|
||||
return deviceType
|
||||
default:
|
||||
break
|
||||
@@ -553,33 +387,14 @@ private findDevice(deviceId) {
|
||||
}
|
||||
|
||||
//Return a list of device attributes
|
||||
private deviceAttributeList(device, deviceType) {
|
||||
def attributeList = [:]
|
||||
def allAttributes = device.supportedAttributes
|
||||
allAttributes.each { attribute ->
|
||||
private deviceAttributeList(device) {
|
||||
device.supportedAttributes.collectEntries { attribute->
|
||||
try {
|
||||
def currentState = device.currentState(attribute.name)
|
||||
if (currentState != null) {
|
||||
switch (attribute.name) {
|
||||
case 'temperature':
|
||||
attributeList.putAll([(attribute.name): currentState.value, 'temperatureScale': location.temperatureScale])
|
||||
break;
|
||||
default:
|
||||
attributeList.putAll([(attribute.name): currentState.value])
|
||||
break;
|
||||
}
|
||||
if (deviceType == "genericSensor") {
|
||||
def key = attribute.name + "_lastUpdated"
|
||||
attributeList.putAll([(key): currentState.isoDate])
|
||||
}
|
||||
} else {
|
||||
attributeList.putAll([(attribute.name): null]);
|
||||
}
|
||||
} catch (e) {
|
||||
attributeList.putAll([(attribute.name): null]);
|
||||
[ (attribute.name): device.currentValue(attribute.name) ]
|
||||
} catch(e) {
|
||||
[ (attribute.name): null ]
|
||||
}
|
||||
}
|
||||
return attributeList
|
||||
}
|
||||
|
||||
//Map device command and value.
|
||||
@@ -649,7 +464,8 @@ private mapDeviceCommands(command, value) {
|
||||
if (value == 1 || value == "1" || value == "lock") {
|
||||
resultCommand = "lock"
|
||||
resultValue = ""
|
||||
} else if (value == 0 || value == "0" || value == "unlock") {
|
||||
}
|
||||
else if (value == 0 || value == "0" || value == "unlock") {
|
||||
resultCommand = "unlock"
|
||||
resultValue = ""
|
||||
}
|
||||
@@ -658,5 +474,5 @@ private mapDeviceCommands(command, value) {
|
||||
break
|
||||
}
|
||||
|
||||
return [resultCommand, resultValue]
|
||||
return [resultCommand,resultValue]
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ def bridgeDiscovery(params = [:]) {
|
||||
}
|
||||
|
||||
ssdpSubscribe()
|
||||
log.trace "bridgeRefreshCount: $bridgeRefreshCount"
|
||||
|
||||
//bridge discovery request every 15 //25 seconds
|
||||
if ((bridgeRefreshCount % 5) == 0) {
|
||||
discoverBridges()
|
||||
@@ -207,7 +207,6 @@ def bulbDiscovery() {
|
||||
}
|
||||
|
||||
private discoverBridges() {
|
||||
log.trace "Sending Hue Discovery message to the hub"
|
||||
sendHubCommand(new physicalgraph.device.HubAction("lan discovery urn:schemas-upnp-org:device:basic:1", physicalgraph.device.Protocol.LAN))
|
||||
}
|
||||
|
||||
|
||||
@@ -27,82 +27,84 @@ definition(
|
||||
|
||||
preferences {
|
||||
|
||||
section("Monitor this door or window") {
|
||||
input "contact", "capability.contactSensor"
|
||||
}
|
||||
|
||||
section("And notify me if it's open for more than this many minutes (default 10)") {
|
||||
input "openThreshold", "number", description: "Number of minutes", required: false
|
||||
}
|
||||
|
||||
section("Delay between notifications (default 10 minutes") {
|
||||
input "frequency", "number", title: "Number of minutes", description: "", required: false
|
||||
}
|
||||
|
||||
section("Via text message at this number (or via push notification if not specified") {
|
||||
input("recipients", "contact", title: "Send notifications to") {
|
||||
input "phone", "phone", title: "Phone number (optional)", required: false
|
||||
section("Monitor this door or window") {
|
||||
input "contact", "capability.contactSensor"
|
||||
}
|
||||
section("And notify me if it's open for more than this many minutes (default 10)") {
|
||||
input "openThreshold", "number", description: "Number of minutes", required: false
|
||||
}
|
||||
section("Delay between notifications (default 10 minutes") {
|
||||
input "frequency", "number", title: "Number of minutes", description: "", required: false
|
||||
}
|
||||
}
|
||||
section("Via text message at this number (or via push notification if not specified") {
|
||||
input("recipients", "contact", title: "Send notifications to") {
|
||||
input "phone", "phone", title: "Phone number (optional)", required: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def installed() {
|
||||
log.trace "installed()"
|
||||
subscribe()
|
||||
log.trace "installed()"
|
||||
subscribe()
|
||||
}
|
||||
|
||||
def updated() {
|
||||
log.trace "updated()"
|
||||
unsubscribe()
|
||||
subscribe()
|
||||
log.trace "updated()"
|
||||
unsubscribe()
|
||||
subscribe()
|
||||
}
|
||||
|
||||
def subscribe() {
|
||||
subscribe(contact, "contact.open", doorOpen)
|
||||
subscribe(contact, "contact.closed", doorClosed)
|
||||
subscribe(contact, "contact.open", doorOpen)
|
||||
subscribe(contact, "contact.closed", doorClosed)
|
||||
}
|
||||
|
||||
def doorOpen(evt) {
|
||||
log.trace "doorOpen($evt.name: $evt.value)"
|
||||
def delay = (openThreshold != null && openThreshold != "") ? openThreshold * 60 : 600
|
||||
runIn(delay, doorOpenTooLong, [overwrite: true])
|
||||
def doorOpen(evt)
|
||||
{
|
||||
log.trace "doorOpen($evt.name: $evt.value)"
|
||||
def t0 = now()
|
||||
def delay = (openThreshold != null && openThreshold != "") ? openThreshold * 60 : 600
|
||||
runIn(delay, doorOpenTooLong, [overwrite: false])
|
||||
log.debug "scheduled doorOpenTooLong in ${now() - t0} msec"
|
||||
}
|
||||
|
||||
def doorClosed(evt) {
|
||||
log.trace "doorClosed($evt.name: $evt.value)"
|
||||
unschedule(doorOpenTooLong)
|
||||
def doorClosed(evt)
|
||||
{
|
||||
log.trace "doorClosed($evt.name: $evt.value)"
|
||||
}
|
||||
|
||||
def doorOpenTooLong() {
|
||||
def contactState = contact.currentState("contact")
|
||||
def freq = (frequency != null && frequency != "") ? frequency * 60 : 600
|
||||
def contactState = contact.currentState("contact")
|
||||
def freq = (frequency != null && frequency != "") ? frequency * 60 : 600
|
||||
|
||||
if (contactState.value == "open") {
|
||||
def elapsed = now() - contactState.rawDateCreated.time
|
||||
def threshold = ((openThreshold != null && openThreshold != "") ? openThreshold * 60000 : 60000) - 1000
|
||||
if (elapsed >= threshold) {
|
||||
log.debug "Contact has stayed open long enough since last check ($elapsed ms): calling sendMessage()"
|
||||
sendMessage()
|
||||
runIn(freq, doorOpenTooLong, [overwrite: false])
|
||||
} else {
|
||||
log.debug "Contact has not stayed open long enough since last check ($elapsed ms): doing nothing"
|
||||
}
|
||||
} else {
|
||||
log.warn "doorOpenTooLong() called but contact is closed: doing nothing"
|
||||
}
|
||||
if (contactState.value == "open") {
|
||||
def elapsed = now() - contactState.rawDateCreated.time
|
||||
def threshold = ((openThreshold != null && openThreshold != "") ? openThreshold * 60000 : 60000) - 1000
|
||||
if (elapsed >= threshold) {
|
||||
log.debug "Contact has stayed open long enough since last check ($elapsed ms): calling sendMessage()"
|
||||
sendMessage()
|
||||
runIn(freq, doorOpenTooLong, [overwrite: false])
|
||||
} else {
|
||||
log.debug "Contact has not stayed open long enough since last check ($elapsed ms): doing nothing"
|
||||
}
|
||||
} else {
|
||||
log.warn "doorOpenTooLong() called but contact is closed: doing nothing"
|
||||
}
|
||||
}
|
||||
|
||||
void sendMessage() {
|
||||
def minutes = (openThreshold != null && openThreshold != "") ? openThreshold : 10
|
||||
def msg = "${contact.displayName} has been left open for ${minutes} minutes."
|
||||
log.info msg
|
||||
if (location.contactBookEnabled) {
|
||||
sendNotificationToContacts(msg, recipients)
|
||||
} else {
|
||||
if (phone) {
|
||||
sendSms phone, msg
|
||||
} else {
|
||||
sendPush msg
|
||||
void sendMessage()
|
||||
{
|
||||
def minutes = (openThreshold != null && openThreshold != "") ? openThreshold : 10
|
||||
def msg = "${contact.displayName} has been left open for ${minutes} minutes."
|
||||
log.info msg
|
||||
if (location.contactBookEnabled) {
|
||||
sendNotificationToContacts(msg, recipients)
|
||||
}
|
||||
else {
|
||||
if (phone) {
|
||||
sendSms phone, msg
|
||||
} else {
|
||||
sendPush msg
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,6 @@ def getCallbackUrl() { return "${getServerUrl()}/oauth/callback" }
|
||||
def apiURL(path = '/') { return "https://api.lifx.com/v1${path}" }
|
||||
def getSecretKey() { return appSettings.secretKey }
|
||||
def getClientId() { return appSettings.clientId }
|
||||
private getVendorName() { "LIFX" }
|
||||
|
||||
def authPage() {
|
||||
log.debug "authPage test1"
|
||||
@@ -77,7 +76,6 @@ def authPage() {
|
||||
return dynamicPage(name:"Credentials", title:"", nextPage:"", install:true, uninstall: true) {
|
||||
section("Select your location") {
|
||||
input "selectedLocationId", "enum", required:true, title:"Select location ({{count}} found)", messageArgs: [count: count], multiple:false, options:options, submitOnChange: true
|
||||
paragraph "Devices will be added automatically from your ${vendorName} account. To add or delete devices please use the Official ${vendorName} App."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ def motionHandler(evt) {
|
||||
else {
|
||||
state.motionStopTime = now()
|
||||
if(delayMinutes) {
|
||||
runIn(delayMinutes*60, turnOffMotionAfterDelay, [overwrite: true])
|
||||
runIn(delayMinutes*60, turnOffMotionAfterDelay, [overwrite: false])
|
||||
} else {
|
||||
turnOffMotionAfterDelay()
|
||||
}
|
||||
|
||||
@@ -136,20 +136,10 @@ def getDataForChild(child, startDate, endDate) {
|
||||
|
||||
def wattvisionURL = wattvisionURL(child.deviceNetworkId, startDate, endDate)
|
||||
if (wattvisionURL) {
|
||||
try {
|
||||
httpGet(uri: wattvisionURL) { response ->
|
||||
def json = new org.json.JSONObject(response.data.toString())
|
||||
child.addWattvisionData(json)
|
||||
return "success"
|
||||
}
|
||||
} catch (groovyx.net.http.HttpResponseException httpE) {
|
||||
log.error "Wattvision getDataForChild HttpResponseException: ${httpE} -> ${httpE.response.data}"
|
||||
//log.debug "wattvisionURL = ${wattvisionURL}"
|
||||
return "fail"
|
||||
} catch (e) {
|
||||
log.error "Wattvision getDataForChild General Exception: ${e}"
|
||||
//log.debug "wattvisionURL = ${wattvisionURL}"
|
||||
return "fail"
|
||||
httpGet(uri: wattvisionURL) { response ->
|
||||
def json = new org.json.JSONObject(response.data.toString())
|
||||
child.addWattvisionData(json)
|
||||
return "success"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -174,14 +164,9 @@ def wattvisionURL(senorId, startDate, endDate) {
|
||||
if (diff > 259200000) { // 3 days in milliseconds
|
||||
// Wattvision only allows pulling 3 hours of data at a time
|
||||
startDate = new Date(hours: endDate.hours - 3)
|
||||
} else if (diff < 10000) { // 10 seconds in milliseconds
|
||||
// Wattvision throws errors when the difference between start_time and end_time is 5 seconds or less
|
||||
// So we are going to make sure that we have a few more seconds of breathing room
|
||||
use (groovy.time.TimeCategory) {
|
||||
startDate = endDate - 10.seconds
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def params = [
|
||||
"sensor_id" : senorId,
|
||||
"api_id" : wattvisionApiAccess.id,
|
||||
@@ -495,3 +480,4 @@ def connectionSuccessful(deviceName, iconSrc) {
|
||||
|
||||
render contentType: 'text/html', data: html
|
||||
}
|
||||
|
||||
|
||||
@@ -125,19 +125,19 @@
|
||||
if(allOk) {
|
||||
|
||||
if(everyoneIsAway() && (state.sunMode == "sunrise")) {
|
||||
log.debug("Home is Empty Setting New Away Mode")
|
||||
log.info("Home is Empty Setting New Away Mode")
|
||||
def delay = (falseAlarmThreshold != null && falseAlarmThreshold != "") ? falseAlarmThreshold * 60 : 10 * 60
|
||||
runIn(delay, "setAway")
|
||||
}
|
||||
|
||||
if(everyoneIsAway() && (state.sunMode == "sunset")) {
|
||||
log.debug("Home is Empty Setting New Away Mode")
|
||||
log.info("Home is Empty Setting New Away Mode")
|
||||
def delay = (falseAlarmThreshold != null && falseAlarmThreshold != "") ? falseAlarmThreshold * 60 : 10 * 60
|
||||
runIn(delay, "setAway")
|
||||
}
|
||||
|
||||
else {
|
||||
log.debug("Home is Occupied Setting New Home Mode")
|
||||
log.info("Home is Occupied Setting New Home Mode")
|
||||
setHome()
|
||||
|
||||
|
||||
@@ -152,7 +152,7 @@
|
||||
log.debug("Checking if everyone is away")
|
||||
|
||||
if(everyoneIsAway()) {
|
||||
log.debug("Nobody is home, running away sequence")
|
||||
log.info("Nobody is home, running away sequence")
|
||||
def delay = (falseAlarmThreshold != null && falseAlarmThreshold != "") ? falseAlarmThreshold * 60 : 10 * 60
|
||||
runIn(delay, "setAway")
|
||||
}
|
||||
@@ -161,7 +161,7 @@
|
||||
else {
|
||||
def lastTime = state[evt.deviceId]
|
||||
if (lastTime == null || now() - lastTime >= 1 * 60000) {
|
||||
log.debug("Someone is home, running home sequence")
|
||||
log.info("Someone is home, running home sequence")
|
||||
setHome()
|
||||
}
|
||||
state[evt.deviceId] = now()
|
||||
@@ -175,14 +175,14 @@
|
||||
if(everyoneIsAway()) {
|
||||
if(state.sunMode == "sunset") {
|
||||
def message = "Performing \"${awayNight}\" for you as requested."
|
||||
log.debug(message)
|
||||
log.info(message)
|
||||
sendAway(message)
|
||||
location.helloHome.execute(settings.awayNight)
|
||||
}
|
||||
|
||||
else if(state.sunMode == "sunrise") {
|
||||
def message = "Performing \"${awayDay}\" for you as requested."
|
||||
log.debug(message)
|
||||
log.info(message)
|
||||
sendAway(message)
|
||||
location.helloHome.execute(settings.awayDay)
|
||||
}
|
||||
@@ -192,19 +192,19 @@
|
||||
}
|
||||
|
||||
else {
|
||||
log.debug("Somebody returned home before we set to '${newAwayMode}'")
|
||||
log.info("Somebody returned home before we set to '${newAwayMode}'")
|
||||
}
|
||||
}
|
||||
|
||||
//set home mode when house is occupied
|
||||
def setHome() {
|
||||
sendOutOfDateNotification()
|
||||
log.debug("Setting Home Mode!!")
|
||||
log.info("Setting Home Mode!!")
|
||||
if(anyoneIsHome()) {
|
||||
if(state.sunMode == "sunset"){
|
||||
if (location.mode != "${homeModeNight}"){
|
||||
def message = "Performing \"${homeNight}\" for you as requested."
|
||||
log.debug(message)
|
||||
log.info(message)
|
||||
sendHome(message)
|
||||
location.helloHome.execute(settings.homeNight)
|
||||
}
|
||||
@@ -213,7 +213,7 @@
|
||||
if(state.sunMode == "sunrise"){
|
||||
if (location.mode != "${homeModeDay}"){
|
||||
def message = "Performing \"${homeDay}\" for you as requested."
|
||||
log.debug(message)
|
||||
log.info(message)
|
||||
sendHome(message)
|
||||
location.helloHome.execute(settings.homeDay)
|
||||
}
|
||||
@@ -329,4 +329,4 @@
|
||||
sendNotification("Your version of Hello, Home Phrase Director is currently out of date. Please look for the new version of Hello, Home Phrase Director now called 'Routine Director' in the marketplace.")
|
||||
state.lastTime = (new Date() + 31).getTime()
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user