mirror of
https://github.com/mtan93/SmartThingsPublic.git
synced 2026-03-24 21:04:00 +00:00
Compare commits
1 Commits
MSA-1903-1
...
MSA-1899-2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ae3a01a7f7 |
@@ -1,426 +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
|
|
||||||
//
|
|
||||||
// 2017-04-10 - DrZwave (with help from Don 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
|
|
||||||
|
|
||||||
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:"#79b821", 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:"#79b821"
|
|
||||||
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:"#53a7c0"
|
|
||||||
state "inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#ffffff"
|
|
||||||
}
|
|
||||||
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: "#1010ff"], // blue=cold
|
|
||||||
[value: 65, color: "#a0a0f0"],
|
|
||||||
[value: 70, color: "#e0e050"],
|
|
||||||
[value: 75, color: "#f0d030"], // yellow
|
|
||||||
[value: 80, color: "#fbf020"],
|
|
||||||
[value: 85, color: "#fbdc01"],
|
|
||||||
[value: 90, color: "#fb3a01"],
|
|
||||||
[value: 95, color: "#fb0801"] // 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 == "" || lum == null || 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 == "" || lum == null || 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) {
|
|
||||||
if (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=60
|
|
||||||
}
|
|
||||||
def liteTempVal = value.toInteger()
|
|
||||||
switch (liteTempVal) {
|
|
||||||
case { it < 0 }:
|
|
||||||
return 60 // 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=10
|
|
||||||
}
|
|
||||||
def onTimeVal = value.toInteger()
|
|
||||||
switch (onTimeVal) {
|
|
||||||
case { it < 0 }:
|
|
||||||
return 10 // 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
|
|
||||||
}
|
|
||||||
@@ -1,839 +0,0 @@
|
|||||||
/**
|
|
||||||
*
|
|
||||||
* Fibaro Dimmer 2 (US)
|
|
||||||
*
|
|
||||||
* github: Eric Maycock (erocm123)
|
|
||||||
* email: erocmail@gmail.com
|
|
||||||
* Date: 2016-07-31 8:03PM
|
|
||||||
* Copyright Eric Maycock
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
|
||||||
* in compliance with the License. You may obtain a copy of the License at:
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
|
|
||||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
|
|
||||||
* for the specific language governing permissions and limitations under the License.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
metadata {
|
|
||||||
definition (name: "Fibaro Dimmer 2", namespace: "erocm123", author: "Eric Maycock") {
|
|
||||||
capability "Actuator"
|
|
||||||
capability "Switch"
|
|
||||||
capability "Switch Level"
|
|
||||||
capability "Refresh"
|
|
||||||
capability "Configuration"
|
|
||||||
capability "Sensor"
|
|
||||||
capability "Polling"
|
|
||||||
capability "Energy Meter"
|
|
||||||
capability "Power Meter"
|
|
||||||
capability "Button"
|
|
||||||
capability "Health Check"
|
|
||||||
|
|
||||||
attribute "needUpdate", "string"
|
|
||||||
|
|
||||||
fingerprint deviceId: "0x1101", inClusters: "0x72,0x86,0x70,0x85,0x8E,0x26,0x7A,0x27,0x73,0xEF,0x26,0x2B"
|
|
||||||
fingerprint deviceId: "0x1101", inClusters: "0x5E,0x20,0x86,0x72,0x26,0x5A,0x59,0x85,0x73,0x98,0x7A,0x56,0x70,0x31,0x32,0x8E,0x60,0x75,0x71,0x27"
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
preferences {
|
|
||||||
input description: "Once you change values on this page, the corner of the \"configuration\" icon will change orange until all configuration parameters are updated.", title: "Settings", displayDuringSetup: false, type: "paragraph", element: "paragraph"
|
|
||||||
generate_preferences(configuration_model())
|
|
||||||
}
|
|
||||||
|
|
||||||
simulator {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
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 "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 "turningOff", label:'${name}', action:"switch.on", icon:"st.switches.light.off", backgroundColor:"#ffffff", nextState:"turningOn"
|
|
||||||
}
|
|
||||||
tileAttribute ("device.level", key: "SLIDER_CONTROL") {
|
|
||||||
attributeState "level", action:"switch level.setLevel"
|
|
||||||
}
|
|
||||||
tileAttribute ("statusText", key: "SECONDARY_CONTROL") {
|
|
||||||
attributeState "statusText", label:'${currentValue}'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
|
|
||||||
state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
|
|
||||||
}
|
|
||||||
valueTile("power", "device.power", decoration: "flat", width: 2, height: 2) {
|
|
||||||
state "default", label:'${currentValue} W'
|
|
||||||
}
|
|
||||||
valueTile("energy", "device.energy", decoration: "flat", width: 2, height: 2) {
|
|
||||||
state "default", label:'${currentValue} kWh'
|
|
||||||
}
|
|
||||||
standardTile("reset", "device.energy", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
|
|
||||||
state "default", label:'reset kWh', action:"reset"
|
|
||||||
}
|
|
||||||
standardTile("configure", "device.needUpdate", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
|
|
||||||
state "NO" , label:'', action:"configuration.configure", icon:"st.secondary.configure"
|
|
||||||
state "YES", label:'', action:"configuration.configure", icon:"https://github.com/erocm123/SmartThingsPublic/raw/master/devicetypes/erocm123/qubino-flush-1d-relay.src/configure@2x.png"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
main "switch"
|
|
||||||
details (["switch", "power", "energy", "refresh", "configure", "reset"])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def parse(String description) {
|
|
||||||
def result = null
|
|
||||||
if (description.startsWith("Err")) {
|
|
||||||
result = createEvent(descriptionText:description, isStateChange:true)
|
|
||||||
} else if (description != "updated") {
|
|
||||||
def cmd = zwave.parse(description, [0x20: 1, 0x84: 1, 0x98: 1, 0x56: 1, 0x60: 3])
|
|
||||||
if (cmd) {
|
|
||||||
result = zwaveEvent(cmd)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def statusTextmsg = ""
|
|
||||||
if (device.currentState('power') && device.currentState('energy')) statusTextmsg = "${device.currentState('power').value} W ${device.currentState('energy').value} kWh"
|
|
||||||
sendEvent(name:"statusText", value:statusTextmsg, displayed:false)
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) {
|
|
||||||
logging("BasicReport: $cmd")
|
|
||||||
def events = []
|
|
||||||
if (cmd.value == 0) {
|
|
||||||
events << createEvent(name: "switch", value: "off")
|
|
||||||
} else if (cmd.value == 255) {
|
|
||||||
events << createEvent(name: "switch", value: "on")
|
|
||||||
} else {
|
|
||||||
events << createEvent(name: "switch", value: "on")
|
|
||||||
events << createEvent(name: "switchLevel", value: cmd.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
def request = update_needed_settings()
|
|
||||||
|
|
||||||
if(request != []){
|
|
||||||
return [response(commands(request)), events]
|
|
||||||
} else {
|
|
||||||
return events
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.sceneactivationv1.SceneActivationSet cmd) {
|
|
||||||
logging("SceneActivationSet: $cmd")
|
|
||||||
logging("sceneId: $cmd.sceneId")
|
|
||||||
logging("dimmingDuration: $cmd.dimmingDuration")
|
|
||||||
logging("Configuration for preference \"Switch Type\" is set to ${settings."20"}")
|
|
||||||
|
|
||||||
if (settings."20" == "2") {
|
|
||||||
logging("Switch configured as Roller blinds")
|
|
||||||
switch (cmd.sceneId) {
|
|
||||||
// Roller blinds S1
|
|
||||||
case 10: // Turn On (1x click)
|
|
||||||
buttonEvent(1, "pushed")
|
|
||||||
break
|
|
||||||
case 13: // Release
|
|
||||||
buttonEvent(1, "held")
|
|
||||||
break
|
|
||||||
case 14: // 2x click
|
|
||||||
buttonEvent(2, "pushed")
|
|
||||||
break
|
|
||||||
case 17: // Brightening
|
|
||||||
buttonEvent(2, "held")
|
|
||||||
break
|
|
||||||
// Roller blinds S2
|
|
||||||
case 11: // Turn Off
|
|
||||||
buttonEvent(3, "pushed")
|
|
||||||
break
|
|
||||||
case 13: // Release
|
|
||||||
buttonEvent(3, "held")
|
|
||||||
break
|
|
||||||
case 14: // 2x click
|
|
||||||
buttonEvent(4, "pushed")
|
|
||||||
break
|
|
||||||
case 15: // 3x click
|
|
||||||
buttonEvent(5, "pushed")
|
|
||||||
break
|
|
||||||
case 18: // Dimming
|
|
||||||
buttonEvent(4, "held")
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
logging("Unhandled SceneActivationSet: ${cmd}")
|
|
||||||
break
|
|
||||||
}
|
|
||||||
} else if (settings."20" == "1") {
|
|
||||||
logging("Switch configured as Toggle")
|
|
||||||
switch (cmd.sceneId) {
|
|
||||||
// Toggle S1
|
|
||||||
case 10: // Off to On
|
|
||||||
buttonEvent(1, "pushed")
|
|
||||||
break
|
|
||||||
case 11: // On to Off
|
|
||||||
buttonEvent(1, "held")
|
|
||||||
break
|
|
||||||
case 14: // 2x click
|
|
||||||
buttonEvent(2, "pushed")
|
|
||||||
break
|
|
||||||
// Toggle S2
|
|
||||||
case 20: // Off to On
|
|
||||||
buttonEvent(3, "pushed")
|
|
||||||
break
|
|
||||||
case 21: // On to Off
|
|
||||||
buttonEvent(3, "held")
|
|
||||||
break
|
|
||||||
case 24: // 2x click
|
|
||||||
buttonEvent(4, "pushed")
|
|
||||||
break
|
|
||||||
case 25: // 3x click
|
|
||||||
buttonEvent(5, "pushed")
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
logging("Unhandled SceneActivationSet: ${cmd}")
|
|
||||||
break
|
|
||||||
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (settings."20" == "0") logging("Switch configured as Momentary") else logging("Switch type not configured")
|
|
||||||
switch (cmd.sceneId) {
|
|
||||||
// Momentary S1
|
|
||||||
case 16: // 1x click
|
|
||||||
buttonEvent(1, "pushed")
|
|
||||||
break
|
|
||||||
case 14: // 2x click
|
|
||||||
buttonEvent(2, "pushed")
|
|
||||||
break
|
|
||||||
case 12: // held
|
|
||||||
buttonEvent(1, "held")
|
|
||||||
break
|
|
||||||
case 13: // release
|
|
||||||
buttonEvent(2, "held")
|
|
||||||
break
|
|
||||||
// Momentary S2
|
|
||||||
case 26: // 1x click
|
|
||||||
buttonEvent(3, "pushed")
|
|
||||||
break
|
|
||||||
case 24: // 2x click
|
|
||||||
buttonEvent(4, "pushed")
|
|
||||||
break
|
|
||||||
case 25: // 3x click
|
|
||||||
buttonEvent(5, "pushed")
|
|
||||||
break
|
|
||||||
case 22: // held
|
|
||||||
buttonEvent(3, "held")
|
|
||||||
break
|
|
||||||
case 23: // release
|
|
||||||
buttonEvent(4, "held")
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
logging("Unhandled SceneActivationSet: ${cmd}")
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def buttonEvent(button, value) {
|
|
||||||
logging("buttonEvent() Button:$button, Value:$value")
|
|
||||||
createEvent(name: "button", value: value, data: [buttonNumber: button], descriptionText: "$device.displayName button $button was $value", isStateChange: true)
|
|
||||||
}
|
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv3.SwitchMultilevelReport cmd) {
|
|
||||||
logging(cmd)
|
|
||||||
dimmerEvents(cmd)
|
|
||||||
}
|
|
||||||
|
|
||||||
def dimmerEvents(physicalgraph.zwave.Command cmd) {
|
|
||||||
logging(cmd)
|
|
||||||
def result = []
|
|
||||||
def value = (cmd.value ? "on" : "off")
|
|
||||||
def switchEvent = createEvent(name: "switch", value: value, descriptionText: "$device.displayName was turned $value")
|
|
||||||
result << switchEvent
|
|
||||||
if (cmd.value) {
|
|
||||||
result << createEvent(name: "level", value: cmd.value, unit: "%")
|
|
||||||
}
|
|
||||||
if (switchEvent.isStateChange) {
|
|
||||||
result << response(["delay 3000", zwave.meterV2.meterGet(scale: 2).format()])
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.associationv2.AssociationReport cmd) {
|
|
||||||
logging("AssociationReport $cmd")
|
|
||||||
state."association${cmd.groupingIdentifier}" = cmd.nodeId[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) {
|
|
||||||
def encapsulatedCommand = cmd.encapsulatedCommand([0x20: 1, 0x84: 1])
|
|
||||||
if (encapsulatedCommand) {
|
|
||||||
state.sec = 1
|
|
||||||
def result = zwaveEvent(encapsulatedCommand)
|
|
||||||
result = result.collect {
|
|
||||||
if (it instanceof physicalgraph.device.HubAction && !it.toString().startsWith("9881")) {
|
|
||||||
response(cmd.CMD + "00" + it.toString())
|
|
||||||
} else {
|
|
||||||
it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.crc16encapv1.Crc16Encap cmd) {
|
|
||||||
def versions = [0x31: 5, 0x30: 1, 0x9C: 1, 0x70: 2, 0x85: 2]
|
|
||||||
def version = versions[cmd.commandClass as Integer]
|
|
||||||
def ccObj = version ? zwave.commandClass(cmd.commandClass, version) : zwave.commandClass(cmd.commandClass)
|
|
||||||
def encapsulatedCommand = ccObj?.command(cmd.command)?.parse(cmd.data)
|
|
||||||
if (encapsulatedCommand) {
|
|
||||||
zwaveEvent(encapsulatedCommand)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.Command cmd) {
|
|
||||||
logging("Unhandled Z-Wave Event: $cmd")
|
|
||||||
}
|
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.meterv3.MeterReport cmd) {
|
|
||||||
logging(cmd)
|
|
||||||
if (cmd.meterType == 1) {
|
|
||||||
if (cmd.scale == 0) {
|
|
||||||
return createEvent(name: "energy", value: cmd.scaledMeterValue, unit: "kWh")
|
|
||||||
} else if (cmd.scale == 1) {
|
|
||||||
return createEvent(name: "energy", value: cmd.scaledMeterValue, unit: "kVAh")
|
|
||||||
} else if (cmd.scale == 2) {
|
|
||||||
return createEvent(name: "power", value: Math.round(cmd.scaledMeterValue), unit: "W")
|
|
||||||
} else {
|
|
||||||
return createEvent(name: "electric", value: cmd.scaledMeterValue, unit: ["pulses", "V", "A", "R/Z", ""][cmd.scale - 3])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelReport cmd){
|
|
||||||
logging("SensorMultilevelReport: $cmd")
|
|
||||||
def map = [:]
|
|
||||||
switch (cmd.sensorType) {
|
|
||||||
case 4:
|
|
||||||
map.name = "power"
|
|
||||||
map.value = cmd.scaledSensorValue.toInteger().toString()
|
|
||||||
map.unit = cmd.scale == 1 ? "Btu/h" : "W"
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
map.descriptionText = cmd.toString()
|
|
||||||
}
|
|
||||||
createEvent(map)
|
|
||||||
}
|
|
||||||
|
|
||||||
def on() {
|
|
||||||
commands([zwave.basicV1.basicSet(value: 0xFF), zwave.basicV1.basicGet()])
|
|
||||||
}
|
|
||||||
|
|
||||||
def off() {
|
|
||||||
commands([zwave.basicV1.basicSet(value: 0x00), zwave.basicV1.basicGet()])
|
|
||||||
}
|
|
||||||
|
|
||||||
def refresh() {
|
|
||||||
logging("$device.displayName refresh()")
|
|
||||||
|
|
||||||
def cmds = []
|
|
||||||
if (state.lastRefresh != null && now() - state.lastRefresh < 5000) {
|
|
||||||
logging("Refresh Double Press")
|
|
||||||
def configuration = parseXml(configuration_model())
|
|
||||||
configuration.Value.each
|
|
||||||
{
|
|
||||||
if ( "${it.@setting_type}" == "zwave" ) {
|
|
||||||
cmds << zwave.configurationV1.configurationGet(parameterNumber: "${it.@index}".toInteger())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cmds << zwave.firmwareUpdateMdV2.firmwareMdGet()
|
|
||||||
} else {
|
|
||||||
cmds << zwave.meterV2.meterGet(scale: 0)
|
|
||||||
cmds << zwave.meterV2.meterGet(scale: 2)
|
|
||||||
cmds << zwave.basicV1.basicGet()
|
|
||||||
}
|
|
||||||
|
|
||||||
state.lastRefresh = now()
|
|
||||||
|
|
||||||
commands(cmds)
|
|
||||||
}
|
|
||||||
|
|
||||||
def ping() {
|
|
||||||
logging("$device.displayName ping()")
|
|
||||||
|
|
||||||
def cmds = []
|
|
||||||
|
|
||||||
cmds << zwave.meterV2.meterGet(scale: 0)
|
|
||||||
cmds << zwave.meterV2.meterGet(scale: 2)
|
|
||||||
cmds << zwave.basicV1.basicGet()
|
|
||||||
|
|
||||||
commands(cmds)
|
|
||||||
}
|
|
||||||
|
|
||||||
def setLevel(level) {
|
|
||||||
if(level > 99) level = 99
|
|
||||||
if(level < 1) level = 1
|
|
||||||
def cmds = []
|
|
||||||
cmds << zwave.basicV1.basicSet(value: level)
|
|
||||||
cmds << zwave.switchMultilevelV1.switchMultilevelGet()
|
|
||||||
|
|
||||||
commands(cmds)
|
|
||||||
}
|
|
||||||
|
|
||||||
def updated()
|
|
||||||
{
|
|
||||||
state.enableDebugging = settings.enableDebugging
|
|
||||||
logging("updated() is being called")
|
|
||||||
sendEvent(name: "checkInterval", value: 2 * 30 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
|
|
||||||
state.needfwUpdate = ""
|
|
||||||
|
|
||||||
def cmds = update_needed_settings()
|
|
||||||
|
|
||||||
sendEvent(name:"needUpdate", value: device.currentValue("needUpdate"), displayed:false, isStateChange: true)
|
|
||||||
|
|
||||||
response(commands(cmds))
|
|
||||||
}
|
|
||||||
|
|
||||||
private command(physicalgraph.zwave.Command cmd) {
|
|
||||||
if (state.sec) {
|
|
||||||
zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format()
|
|
||||||
} else {
|
|
||||||
cmd.format()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private commands(commands, delay=1500) {
|
|
||||||
delayBetween(commands.collect{ command(it) }, delay)
|
|
||||||
}
|
|
||||||
|
|
||||||
def generate_preferences(configuration_model)
|
|
||||||
{
|
|
||||||
def configuration = parseXml(configuration_model)
|
|
||||||
|
|
||||||
configuration.Value.each
|
|
||||||
{
|
|
||||||
switch(it.@type)
|
|
||||||
{
|
|
||||||
case ["byte","short","four"]:
|
|
||||||
input "${it.@index}", "number",
|
|
||||||
title:"${it.@label}\n" + "${it.Help}",
|
|
||||||
range: "${it.@min}..${it.@max}",
|
|
||||||
defaultValue: "${it.@value}",
|
|
||||||
displayDuringSetup: "${it.@displayDuringSetup}"
|
|
||||||
break
|
|
||||||
case "list":
|
|
||||||
def items = []
|
|
||||||
it.Item.each { items << ["${it.@value}":"${it.@label}"] }
|
|
||||||
input "${it.@index}", "enum",
|
|
||||||
title:"${it.@label}\n" + "${it.Help}",
|
|
||||||
defaultValue: "${it.@value}",
|
|
||||||
displayDuringSetup: "${it.@displayDuringSetup}",
|
|
||||||
options: items
|
|
||||||
break
|
|
||||||
case "decimal":
|
|
||||||
input "${it.@index}", "decimal",
|
|
||||||
title:"${it.@label}\n" + "${it.Help}",
|
|
||||||
range: "${it.@min}..${it.@max}",
|
|
||||||
defaultValue: "${it.@value}",
|
|
||||||
displayDuringSetup: "${it.@displayDuringSetup}"
|
|
||||||
break
|
|
||||||
case "boolean":
|
|
||||||
input "${it.@index}", "boolean",
|
|
||||||
title:"${it.@label}\n" + "${it.Help}",
|
|
||||||
defaultValue: "${it.@value}",
|
|
||||||
displayDuringSetup: "${it.@displayDuringSetup}"
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def update_current_properties(cmd)
|
|
||||||
{
|
|
||||||
def currentProperties = state.currentProperties ?: [:]
|
|
||||||
|
|
||||||
currentProperties."${cmd.parameterNumber}" = cmd.configurationValue
|
|
||||||
|
|
||||||
if (settings."${cmd.parameterNumber}" != null)
|
|
||||||
{
|
|
||||||
if (settings."${cmd.parameterNumber}".toInteger() == convertParam("${cmd.parameterNumber}".toInteger(), cmd2Integer(cmd.configurationValue)))
|
|
||||||
{
|
|
||||||
sendEvent(name:"needUpdate", value:"NO", displayed:false, isStateChange: true)
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sendEvent(name:"needUpdate", value:"YES", displayed:false, isStateChange: true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
state.currentProperties = currentProperties
|
|
||||||
}
|
|
||||||
|
|
||||||
def update_needed_settings()
|
|
||||||
{
|
|
||||||
def cmds = []
|
|
||||||
def currentProperties = state.currentProperties ?: [:]
|
|
||||||
|
|
||||||
def configuration = parseXml(configuration_model())
|
|
||||||
def isUpdateNeeded = "NO"
|
|
||||||
|
|
||||||
if(!state.needfwUpdate || state.needfwUpdate == ""){
|
|
||||||
logging("Requesting device firmware version")
|
|
||||||
cmds << zwave.firmwareUpdateMdV2.firmwareMdGet()
|
|
||||||
}
|
|
||||||
if(!state.association1 || state.association1 == "" || state.association1 == "1"){
|
|
||||||
logging("Setting association group 1")
|
|
||||||
cmds << zwave.associationV2.associationSet(groupingIdentifier:1, nodeId:zwaveHubNodeId)
|
|
||||||
cmds << zwave.associationV2.associationGet(groupingIdentifier:1)
|
|
||||||
}
|
|
||||||
if(!state.association2 || state.association2 == "" || state.association1 == "2"){
|
|
||||||
logging("Setting association group 2")
|
|
||||||
cmds << zwave.associationV2.associationSet(groupingIdentifier:2, nodeId:zwaveHubNodeId)
|
|
||||||
cmds << zwave.associationV2.associationGet(groupingIdentifier:2)
|
|
||||||
}
|
|
||||||
|
|
||||||
configuration.Value.each
|
|
||||||
{
|
|
||||||
if ("${it.@setting_type}" == "zwave"){
|
|
||||||
if (currentProperties."${it.@index}" == null)
|
|
||||||
{
|
|
||||||
if (device.currentValue("currentFirmware") == null || "${it.@fw}".indexOf(device.currentValue("currentFirmware")) >= 0){
|
|
||||||
isUpdateNeeded = "YES"
|
|
||||||
logging("Current value of parameter ${it.@index} is unknown")
|
|
||||||
cmds << zwave.configurationV1.configurationGet(parameterNumber: it.@index.toInteger())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (settings."${it.@index}" != null && convertParam(it.@index.toInteger(), cmd2Integer(currentProperties."${it.@index}")) != settings."${it.@index}".toInteger())
|
|
||||||
{
|
|
||||||
if (device.currentValue("currentFirmware") == null || "${it.@fw}".indexOf(device.currentValue("currentFirmware")) >= 0){
|
|
||||||
isUpdateNeeded = "YES"
|
|
||||||
|
|
||||||
logging("Parameter ${it.@index} will be updated to " + settings."${it.@index}")
|
|
||||||
def convertedConfigurationValue = convertParam(it.@index.toInteger(), settings."${it.@index}".toInteger())
|
|
||||||
cmds << zwave.configurationV1.configurationSet(configurationValue: integer2Cmd(convertedConfigurationValue, it.@byteSize.toInteger()), parameterNumber: it.@index.toInteger(), size: it.@byteSize.toInteger())
|
|
||||||
cmds << zwave.configurationV1.configurationGet(parameterNumber: it.@index.toInteger())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sendEvent(name:"needUpdate", value: isUpdateNeeded, displayed:false, isStateChange: true)
|
|
||||||
return cmds
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert 1 and 2 bytes values to integer
|
|
||||||
*/
|
|
||||||
def cmd2Integer(array) {
|
|
||||||
|
|
||||||
switch(array.size()) {
|
|
||||||
case 1:
|
|
||||||
array[0]
|
|
||||||
break
|
|
||||||
case 2:
|
|
||||||
((array[0] & 0xFF) << 8) | (array[1] & 0xFF)
|
|
||||||
break
|
|
||||||
case 3:
|
|
||||||
((array[0] & 0xFF) << 16) | ((array[1] & 0xFF) << 8) | (array[2] & 0xFF)
|
|
||||||
break
|
|
||||||
case 4:
|
|
||||||
((array[0] & 0xFF) << 24) | ((array[1] & 0xFF) << 16) | ((array[2] & 0xFF) << 8) | (array[3] & 0xFF)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def integer2Cmd(value, size) {
|
|
||||||
switch(size) {
|
|
||||||
case 1:
|
|
||||||
[value]
|
|
||||||
break
|
|
||||||
case 2:
|
|
||||||
def short value1 = value & 0xFF
|
|
||||||
def short value2 = (value >> 8) & 0xFF
|
|
||||||
[value2, value1]
|
|
||||||
break
|
|
||||||
case 3:
|
|
||||||
def short value1 = value & 0xFF
|
|
||||||
def short value2 = (value >> 8) & 0xFF
|
|
||||||
def short value3 = (value >> 16) & 0xFF
|
|
||||||
[value3, value2, value1]
|
|
||||||
break
|
|
||||||
case 4:
|
|
||||||
def short value1 = value & 0xFF
|
|
||||||
def short value2 = (value >> 8) & 0xFF
|
|
||||||
def short value3 = (value >> 16) & 0xFF
|
|
||||||
def short value4 = (value >> 24) & 0xFF
|
|
||||||
[value4, value3, value2, value1]
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.configurationv2.ConfigurationReport cmd) {
|
|
||||||
update_current_properties(cmd)
|
|
||||||
logging("${device.displayName} parameter '${cmd.parameterNumber}' with a byte size of '${cmd.size}' is set to '${cmd2Integer(cmd.configurationValue)}'")
|
|
||||||
}
|
|
||||||
|
|
||||||
def zwaveEvent(physicalgraph.zwave.commands.firmwareupdatemdv2.FirmwareMdReport cmd){
|
|
||||||
logging("Firmware Report ${cmd.toString()}")
|
|
||||||
def firmwareVersion
|
|
||||||
switch(cmd.checksum){
|
|
||||||
case "3281":
|
|
||||||
firmwareVersion = "3.08"
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
firmwareVersion = cmd.checksum
|
|
||||||
}
|
|
||||||
state.needfwUpdate = "false"
|
|
||||||
updateDataValue("firmware", firmwareVersion.toString())
|
|
||||||
createEvent(name: "currentFirmware", value: firmwareVersion)
|
|
||||||
}
|
|
||||||
|
|
||||||
def configure() {
|
|
||||||
state.enableDebugging = settings.enableDebugging
|
|
||||||
logging("Configuring Device For SmartThings Use")
|
|
||||||
def cmds = []
|
|
||||||
|
|
||||||
cmds = update_needed_settings()
|
|
||||||
|
|
||||||
if (cmds != []) commands(cmds)
|
|
||||||
}
|
|
||||||
|
|
||||||
def convertParam(number, value) {
|
|
||||||
switch (number){
|
|
||||||
case 201:
|
|
||||||
if (value < 0)
|
|
||||||
256 + value
|
|
||||||
else if (value > 100)
|
|
||||||
value - 256
|
|
||||||
else
|
|
||||||
value
|
|
||||||
break
|
|
||||||
case 202:
|
|
||||||
if (value < 0)
|
|
||||||
256 + value
|
|
||||||
else if (value > 100)
|
|
||||||
value - 256
|
|
||||||
else
|
|
||||||
value
|
|
||||||
break
|
|
||||||
case 203:
|
|
||||||
if (value < 0)
|
|
||||||
65536 + value
|
|
||||||
else if (value > 1000)
|
|
||||||
value - 65536
|
|
||||||
else
|
|
||||||
value
|
|
||||||
break
|
|
||||||
case 204:
|
|
||||||
if (value < 0)
|
|
||||||
256 + value
|
|
||||||
else if (value > 100)
|
|
||||||
value - 256
|
|
||||||
else
|
|
||||||
value
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
value
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private def logging(message) {
|
|
||||||
if (state.enableDebugging == null || state.enableDebugging == "true") log.debug "$message"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def configuration_model()
|
|
||||||
{
|
|
||||||
'''
|
|
||||||
<configuration>
|
|
||||||
<Value type="byte" byteSize="1" index="1" label="Minimum brightness level" min="1" max="98" value="1" setting_type="zwave" fw="3.08">
|
|
||||||
<Help>
|
|
||||||
(parameter is set automatically during the calibration process)
|
|
||||||
The parameter can be changed manually after the calibration.
|
|
||||||
Range: 1~98
|
|
||||||
Default: 1
|
|
||||||
</Help>
|
|
||||||
</Value>
|
|
||||||
<Value type="byte" byteSize="1" index="2" label="Maximum brightness level" min="2" max="99" value="99" setting_type="zwave" fw="3.08">
|
|
||||||
<Help>
|
|
||||||
(parameter is set automatically during the calibration process)
|
|
||||||
The parameter can be changed manually after the calibration.
|
|
||||||
Range: 2~99
|
|
||||||
Default: 99
|
|
||||||
</Help>
|
|
||||||
</Value>
|
|
||||||
<Value type="byte" byteSize="1" index="5" label="Automatic control - dimming step size" min="1" max="99" value="1" setting_type="zwave" fw="3.08">
|
|
||||||
<Help>
|
|
||||||
This parameter defines the percentage value of dimming step during the automatic control.
|
|
||||||
Range: 1~99
|
|
||||||
Default: 1
|
|
||||||
</Help>
|
|
||||||
</Value>
|
|
||||||
<Value type="short" byteSize="2" index="6" label="Automatic control - time of a dimming step" min="0" max="255" value="1" setting_type="zwave" fw="3.08">
|
|
||||||
<Help>
|
|
||||||
This parameter defines the time of single dimming step set in parameter 5 during the automatic control.
|
|
||||||
Range: 0~255
|
|
||||||
Default: 1
|
|
||||||
</Help>
|
|
||||||
</Value>
|
|
||||||
<Value type="byte" byteSize="1" index="7" label="Manual control - dimming step size" min="1" max="99" value="1" setting_type="zwave" fw="3.08">
|
|
||||||
<Help>
|
|
||||||
This parameter defines the percentage value of dimming step during the manual control.
|
|
||||||
Range: 1~99
|
|
||||||
Default: 1
|
|
||||||
</Help>
|
|
||||||
</Value>
|
|
||||||
<Value type="short" byteSize="2" index="8" label="Manual control - time of a dimming step" min="0" max="255" value="5" setting_type="zwave" fw="3.08">
|
|
||||||
<Help>
|
|
||||||
This parameter defines the time of single dimming step set in parameter 7 during the manual control.
|
|
||||||
Range: 0~255
|
|
||||||
Default: 5
|
|
||||||
</Help>
|
|
||||||
</Value>
|
|
||||||
<Value type="list" byteSize="1" index="9" label="State of the device after a power failure" min="0" max="1" value="1" setting_type="zwave" fw="3.08">
|
|
||||||
<Help>
|
|
||||||
The Dimmer 2 will return to the last state before power failure.
|
|
||||||
0 - the Dimmer 2 does not save the state before a power failure, it returns to the "off" position
|
|
||||||
1 - the Dimmer 2 restores its state before power failure
|
|
||||||
Range: 0~1
|
|
||||||
Default: 1
|
|
||||||
</Help>
|
|
||||||
<Item label="Off" value="0" />
|
|
||||||
<Item label="Previous State" value="1" />
|
|
||||||
</Value>
|
|
||||||
<Value type="short" byteSize="2" index="10" label="Timer functionality (auto - off)" min="0" max="32767" value="0" setting_type="zwave" fw="3.08">
|
|
||||||
<Help>
|
|
||||||
This parameter allows to automatically switch off the device after specified time (seconds) from switching on the light source.
|
|
||||||
Range: 1~32767
|
|
||||||
Default: 0
|
|
||||||
</Help>
|
|
||||||
</Value>
|
|
||||||
<Value type="byte" byteSize="1" index="19" label="Forced switch on brightness level" min="0" max="99" value="0" setting_type="zwave" fw="3.08">
|
|
||||||
<Help>
|
|
||||||
If the parameter is active, switching on the Dimmer 2 (S1 single click) will always set this brightness level.
|
|
||||||
Range: 0~99
|
|
||||||
Default: 0
|
|
||||||
</Help>
|
|
||||||
</Value>
|
|
||||||
<Value type="list" byteSize="1" index="20" label="Switch type" min="0" max="2" value="0" setting_type="zwave" fw="3.08">
|
|
||||||
<Help>
|
|
||||||
Choose between momentary, toggle and roller blind switch.
|
|
||||||
Range: 0~2
|
|
||||||
Default: 0
|
|
||||||
</Help>
|
|
||||||
<Item label="Momentary" value="0" />
|
|
||||||
<Item label="Toggle" value="1" />
|
|
||||||
<Item label="Roller Blind" value="2" />
|
|
||||||
</Value>
|
|
||||||
<Value type="list" byteSize="1" index="22" label="Assign toggle switch status to the device status " min="0" max="1" value="0" setting_type="zwave" fw="3.08">
|
|
||||||
<Help>
|
|
||||||
By default each change of toggle switch position results in action of Dimmer 2 (switch on/off) regardless the physical connection of contacts.
|
|
||||||
0 - device changes status on switch status change
|
|
||||||
1 - device status is synchronized with switch status
|
|
||||||
Range: 0~1
|
|
||||||
Default: 0
|
|
||||||
</Help>
|
|
||||||
<Item label="Default" value="0" />
|
|
||||||
<Item label="Synchronized" value="1" />
|
|
||||||
</Value>
|
|
||||||
<Value type="list" byteSize="1" index="23" label="Double click option" min="0" max="1" value="1" setting_type="zwave" fw="3.08">
|
|
||||||
<Help>
|
|
||||||
set the brightness level to MAX
|
|
||||||
Range: 0~1
|
|
||||||
Default: 1
|
|
||||||
</Help>
|
|
||||||
<Item label="Disabled" value="0" />
|
|
||||||
<Item label="Enabled" value="1" />
|
|
||||||
</Value>
|
|
||||||
<Value type="list" byteSize="1" index="26" label="The function of 3-way switch" min="0" max="1" value="0" setting_type="zwave" fw="3.08">
|
|
||||||
<Help>
|
|
||||||
Switch no. 2 controls the Dimmer 2 additionally (in 3-way switch mode). Function disabled for parameter 20 set to 2 (roller blind switch).
|
|
||||||
Range: 0~1
|
|
||||||
Default: 0
|
|
||||||
</Help>
|
|
||||||
<Item label="Disabled" value="0" />
|
|
||||||
<Item label="Enabled" value="1" />
|
|
||||||
</Value>
|
|
||||||
<Value type="list" byteSize="1" index="28" label="Scene activation functionality" min="0" max="1" value="1" setting_type="zwave" fw="3.08">
|
|
||||||
<Help>
|
|
||||||
SCENE ID depends on the switch type configurations.
|
|
||||||
Range: 0~1
|
|
||||||
Default: 0
|
|
||||||
</Help>
|
|
||||||
<Item label="Disabled" value="0" />
|
|
||||||
<Item label="Enabled" value="1" />
|
|
||||||
</Value>
|
|
||||||
<Value type="list" byteSize="1" index="29" label="Switch functionality of S1 and S2" min="0" max="1" value="0" setting_type="zwave" fw="3.08">
|
|
||||||
<Help>
|
|
||||||
This parameter allows for switching the role of keys connected to S1 and S2 without changes in connection.
|
|
||||||
Range: 0~1
|
|
||||||
Default: 0
|
|
||||||
</Help>
|
|
||||||
<Item label="Standard" value="0" />
|
|
||||||
<Item label="Switched" value="1" />
|
|
||||||
</Value>
|
|
||||||
<Value type="list" byteSize="1" index="35" label="Auto-calibration after power on" min="0" max="4" value="1" setting_type="zwave" fw="3.08">
|
|
||||||
<Help>
|
|
||||||
This parameter determines the trigger of auto-calibration procedure, e.g. power on, load error, etc.
|
|
||||||
0 - No auto-calibration of the load after power on
|
|
||||||
1 - Auto-calibration performed after first power on
|
|
||||||
2 - Auto-calibration performed after each power on
|
|
||||||
3 - Auto-calibration performed after first power on or after each LOAD ERROR alarm (no load, load failure, burnt out bulb), if parameter 37 is set to 1 also after alarms: SURGE (Dimmer 2 output overvoltage) and OVERCURRENT (Dimmer 2 output overcurrent)
|
|
||||||
4 - Auto-calibration performed after each power on or after each LOAD ERROR alarm (no load, load failure, burnt out bulb), if parameter 37 is set to 1 also after alarms: SURGE (Dimmer 2 output overvoltage) and OVERCURRENT (Dimmer 2 output overcurrent)
|
|
||||||
Range: 0~4
|
|
||||||
Default: 1
|
|
||||||
</Help>
|
|
||||||
<Item label="0" value="0" />
|
|
||||||
<Item label="1" value="1" />
|
|
||||||
<Item label="2" value="2" />
|
|
||||||
<Item label="3" value="3" />
|
|
||||||
<Item label="4" value="4" />
|
|
||||||
</Value>
|
|
||||||
<Value type="short" byteSize="2" index="39" label="Max Power load" min="0" max="350" value="250" setting_type="zwave" fw="3.08">
|
|
||||||
<Help>
|
|
||||||
This parameter defines the maximum load for a dimmer.
|
|
||||||
Range: 0~350
|
|
||||||
Default: 250
|
|
||||||
</Help>
|
|
||||||
</Value>
|
|
||||||
<Value type="byte" byteSize="1" index="50" label="Active power reports" min="0" max="100" value="10" setting_type="zwave" fw="3.08">
|
|
||||||
<Help>
|
|
||||||
The parameter defines the power level change that will result in a new power report being sent. The value is a percentage of the previous report.
|
|
||||||
Range: 0~100
|
|
||||||
Default: 10
|
|
||||||
</Help>
|
|
||||||
</Value>
|
|
||||||
<Value type="short" byteSize="2" index="52" label="Periodic active power and energy reports" min="0" max="32767" value="3600" setting_type="zwave" fw="3.08">
|
|
||||||
<Help>
|
|
||||||
Parameter 52 defines a time period between consecutive reports. Timer is reset and counted from zero after each report.
|
|
||||||
Range: 0~32767
|
|
||||||
Default: 3600
|
|
||||||
</Help>
|
|
||||||
</Value>
|
|
||||||
<Value type="short" byteSize="2" index="53" label="Energy reports" min="0" max="255" value="10" setting_type="zwave" fw="3.08">
|
|
||||||
<Help>
|
|
||||||
Energy level change which will result in sending a new energy report.
|
|
||||||
Range: 0~255
|
|
||||||
Default: 10
|
|
||||||
</Help>
|
|
||||||
</Value>
|
|
||||||
<Value type="list" byteSize="1" index="54" label="Self-measurement" min="0" max="1" value="0" setting_type="zwave" fw="3.08">
|
|
||||||
<Help>
|
|
||||||
The Dimmer 2 may include active power and energy consumed by itself in reports sent to the main controller.
|
|
||||||
Range: 0~1
|
|
||||||
Default: 0
|
|
||||||
</Help>
|
|
||||||
<Item label="Disabled" value="0" />
|
|
||||||
<Item label="Enabled" value="1" />
|
|
||||||
</Value>
|
|
||||||
<Value type="boolean" index="enableDebugging" label="Enable Debug Logging?" value="true" setting_type="preference" fw="3.08">
|
|
||||||
<Help>
|
|
||||||
</Help>
|
|
||||||
</Value>
|
|
||||||
</configuration>
|
|
||||||
'''
|
|
||||||
}
|
|
||||||
@@ -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
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||||
* in compliance with the License. You may obtain a copy of the License at:
|
* in compliance with the License. You may obtain a copy of the License at:
|
||||||
@@ -59,7 +59,7 @@ def updated() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def configure() {
|
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}"
|
log.debug "configure -- cmds: ${cmds}"
|
||||||
return cmds
|
return cmds
|
||||||
}
|
}
|
||||||
@@ -166,4 +166,4 @@ def checkPresenceCallback() {
|
|||||||
if (timeSinceLastCheckin >= theCheckInterval) {
|
if (timeSinceLastCheckin >= theCheckInterval) {
|
||||||
handlePresenceEvent(false)
|
handlePresenceEvent(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -23,9 +23,9 @@ metadata {
|
|||||||
capability "Health Check"
|
capability "Health Check"
|
||||||
capability "Light"
|
capability "Light"
|
||||||
|
|
||||||
fingerprint mfr:"0063", prod:"4457", deviceJoinName: "GE In-Wall 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:"4944", deviceJoinName: "GE In-Wall Smart Dimmer "
|
||||||
fingerprint mfr:"0063", prod:"5044", deviceJoinName: "GE Plug-In 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"
|
fingerprint mfr:"0063", prod:"4944", model:"3034", deviceJoinName: "GE In-Wall Smart Fan Control"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -114,17 +114,9 @@ def refresh() {
|
|||||||
|
|
||||||
def configure() {
|
def configure() {
|
||||||
log.debug "Configuring Reporting and Bindings."
|
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)
|
// 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
|
// 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])
|
sendEvent(name: "checkInterval", value: 2 * 60 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
|
||||||
cmds + refresh()
|
refresh()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,9 +30,6 @@ metadata {
|
|||||||
fingerprint deviceId: "0x0701", inClusters: "0x5E,0x86,0x72,0x98", outClusters: "0x5A,0x82"
|
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 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:"0002", model:"001D", deviceJoinName: "Aeon Labs Door/Window Sensor (Gen 5)"
|
||||||
fingerprint mfr:"014A", prod:"0001", model:"0002", deviceJoinName: "Ecolink Door/Window Sensor"
|
|
||||||
fingerprint mfr:"0086", prod:"0102", model:"0070", deviceJoinName: "Aeon Labs Door/Window Sensor 6"
|
|
||||||
fingerprint mfr:"011A", prod:"0601", model:"0903", deviceJoinName: "Enerwave Magnetic Door/Window Sensor"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// simulator metadata
|
// simulator metadata
|
||||||
|
|||||||
@@ -22,9 +22,9 @@ metadata {
|
|||||||
capability "Health Check"
|
capability "Health Check"
|
||||||
capability "Light"
|
capability "Light"
|
||||||
|
|
||||||
fingerprint mfr:"0063", prod:"4952", deviceJoinName: "GE Wall Switch"
|
fingerprint mfr:"0063", prod:"4952", deviceJoinName: "Z-Wave Wall Switch"
|
||||||
fingerprint mfr:"0063", prod:"5257", deviceJoinName: "GE Wall Switch"
|
fingerprint mfr:"0063", prod:"5257", deviceJoinName: "Z-Wave Wall Switch"
|
||||||
fingerprint mfr:"0063", prod:"5052", deviceJoinName: "GE Plug-In Switch"
|
fingerprint mfr:"0063", prod:"5052", deviceJoinName: "Z-Wave Plug-In Switch"
|
||||||
fingerprint mfr:"0113", prod:"5257", deviceJoinName: "Z-Wave Wall Switch"
|
fingerprint mfr:"0113", prod:"5257", deviceJoinName: "Z-Wave Wall Switch"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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()
|
||||||
|
])
|
||||||
|
}
|
||||||
@@ -52,15 +52,15 @@ definition(
|
|||||||
//Device Inputs
|
//Device Inputs
|
||||||
preferences {
|
preferences {
|
||||||
section("Allow OpenT2T to control these things...") {
|
section("Allow OpenT2T to control these things...") {
|
||||||
input "contactSensors", "capability.contactSensor", title: "Which Contact 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, hideWhenEmpty: true
|
input "garageDoors", "capability.garageDoorControl", title: "Which Garage Doors?", multiple: true, required: false
|
||||||
input "locks", "capability.lock", title: "Which Locks?", multiple: true, required: false, hideWhenEmpty: true
|
input "locks", "capability.lock", title: "Which Locks?", multiple: true, required: false
|
||||||
input "cameras", "capability.videoCapture", title: "Which Cameras?", multiple: true, required: false, hideWhenEmpty: true
|
input "cameras", "capability.videoCapture", title: "Which Cameras?", multiple: true, required: false
|
||||||
input "motionSensors", "capability.motionSensor", title: "Which Motion Sensors?", multiple: true, required: false, hideWhenEmpty: true
|
input "motionSensors", "capability.motionSensor", title: "Which Motion Sensors?", multiple: true, required: false
|
||||||
input "presenceSensors", "capability.presenceSensor", title: "Which Presence Sensors", multiple: true, required: false, hideWhenEmpty: true
|
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, hideWhenEmpty: true
|
input "switches", "capability.switch", title: "Which Switches and Lights?", multiple: true, required: false
|
||||||
input "thermostats", "capability.thermostat", title: "Which Thermostat?", multiple: true, required: false, hideWhenEmpty: true
|
input "thermostats", "capability.thermostat", title: "Which Thermostat?", multiple: true, required: false
|
||||||
input "waterSensors", "capability.waterSensor", title: "Which Water Leak Sensors?", multiple: true, required: false, hideWhenEmpty: true
|
input "waterSensors", "capability.waterSensor", title: "Which Water Leak Sensors?", multiple: true, required: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,6 +80,16 @@ def getInputs() {
|
|||||||
|
|
||||||
//API external Endpoints
|
//API external Endpoints
|
||||||
mappings {
|
mappings {
|
||||||
|
path("/subscriptionURL/:url") {
|
||||||
|
action: [
|
||||||
|
PUT: "updateEndpointURL"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
path("/connectionId/:connId") {
|
||||||
|
action: [
|
||||||
|
PUT: "updateConnectionId"
|
||||||
|
]
|
||||||
|
}
|
||||||
path("/devices") {
|
path("/devices") {
|
||||||
action: [
|
action: [
|
||||||
GET: "getDevices"
|
GET: "getDevices"
|
||||||
@@ -95,52 +105,33 @@ mappings {
|
|||||||
PUT: "updateDevice"
|
PUT: "updateDevice"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
path("/deviceSubscription") {
|
path("/subscription/:id") {
|
||||||
action: [
|
action: [
|
||||||
POST: "registerDeviceChange",
|
POST: "registerDeviceChange",
|
||||||
DELETE: "unregisterDeviceChange"
|
DELETE: "unregisterDeviceChange"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
path("/locationSubscription") {
|
|
||||||
action: [
|
|
||||||
POST: "registerDeviceGraph",
|
|
||||||
DELETE: "unregisterDeviceGraph"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def installed() {
|
def installed() {
|
||||||
log.debug "Installing with settings: ${settings}"
|
log.debug "Installed with settings: ${settings}"
|
||||||
initialize()
|
initialize()
|
||||||
}
|
}
|
||||||
|
|
||||||
def updated() {
|
def updated() {
|
||||||
log.debug "Updating with settings: ${settings}"
|
log.debug "Updated with settings: ${settings}"
|
||||||
if(state.deviceSubscriptionMap == null){
|
|
||||||
state.deviceSubscriptionMap = [:]
|
|
||||||
log.debug "deviceSubscriptionMap created."
|
|
||||||
}
|
|
||||||
if( state.locationSubscriptionMap == null){
|
|
||||||
state.locationSubscriptionMap = [:]
|
|
||||||
log.debug "locationSubscriptionMap created."
|
|
||||||
}
|
|
||||||
unsubscribe()
|
unsubscribe()
|
||||||
registerAllDeviceSubscriptions()
|
registerSubscriptions()
|
||||||
}
|
}
|
||||||
|
|
||||||
def initialize() {
|
def initialize() {
|
||||||
log.debug "Initializing with settings: ${settings}"
|
state.connectionId = ""
|
||||||
state.deviceSubscriptionMap = [:]
|
state.endpointURL = "https://ifs.windows-int.com/v1/cb/81C7E77B-EABC-488A-B2BF-FEC42F0DABD2/notify"
|
||||||
log.debug "deviceSubscriptionMap created."
|
registerSubscriptions()
|
||||||
registerAllDeviceSubscriptions()
|
|
||||||
state.locationSubscriptionMap = [:]
|
|
||||||
log.debug "locationSubscriptionMap created."
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*** Subscription Functions ***/
|
|
||||||
|
|
||||||
//Subscribe events for all devices
|
//Subscribe events for all devices
|
||||||
def registerAllDeviceSubscriptions() {
|
def registerSubscriptions() {
|
||||||
registerChangeHandler(inputs)
|
registerChangeHandler(inputs)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,195 +140,101 @@ def registerChangeHandler(myList) {
|
|||||||
myList.each { myDevice ->
|
myList.each { myDevice ->
|
||||||
def theAtts = myDevice.supportedAttributes
|
def theAtts = myDevice.supportedAttributes
|
||||||
theAtts.each {att ->
|
theAtts.each {att ->
|
||||||
subscribe(myDevice, att.name, deviceEventHandler)
|
subscribe(myDevice, att.name, eventHandler)
|
||||||
log.info "Registering for ${myDevice.displayName}.${att.name}"
|
log.info "Registering ${myDevice.displayName}.${att.name}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Endpoints function: Subscribe to events from a specific device
|
//Endpoints function: Subscribe to events from a specific device
|
||||||
def registerDeviceChange() {
|
def registerDeviceChange() {
|
||||||
def subscriptionEndpt = params.subscriptionURL
|
def myDevice = findDevice(params.id)
|
||||||
def deviceId = params.deviceId
|
|
||||||
def myDevice = findDevice(deviceId)
|
|
||||||
if( myDevice == null ){
|
|
||||||
httpError(404, "Cannot find device with device ID ${deviceId}.")
|
|
||||||
}
|
|
||||||
|
|
||||||
def theAtts = myDevice.supportedAttributes
|
def theAtts = myDevice.supportedAttributes
|
||||||
try {
|
try {
|
||||||
theAtts.each {att ->
|
theAtts.each {att ->
|
||||||
subscribe(myDevice, att.name, deviceEventHandler)
|
subscribe(myDevice, att.name, eventHandler)
|
||||||
}
|
log.info "Registering ${myDevice.displayName}.${att.name}"
|
||||||
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}"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return ["succeed"]
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
httpError(500, "something went wrong: $e")
|
httpError(500, "something went wrong: $e")
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info "Current subscription map is ${state.deviceSubscriptionMap}"
|
|
||||||
return ["succeed"]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Endpoints function: Unsubscribe to events from a specific device
|
//Endpoints function: Unsubscribe to events from a specific device
|
||||||
def unregisterDeviceChange() {
|
def unregisterDeviceChange() {
|
||||||
def subscriptionEndpt = params.subscriptionURL
|
def myDevice = findDevice(params.id)
|
||||||
def deviceId = params.deviceId
|
|
||||||
def myDevice = findDevice(deviceId)
|
|
||||||
|
|
||||||
if( myDevice == null ){
|
|
||||||
httpError(404, "Cannot find device with device ID ${deviceId}.")
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if(subscriptionEndpt != null && subscriptionEndpt != "undefined"){
|
unsubscribe(myDevice)
|
||||||
if (state.deviceSubscriptionMap[deviceId]?.contains(subscriptionEndpt)){
|
log.info "Unregistering ${myDevice.displayName}"
|
||||||
if(state.deviceSubscriptionMap[deviceId].size() == 1){
|
|
||||||
state.deviceSubscriptionMap.remove(deviceId)
|
|
||||||
} else {
|
|
||||||
state.deviceSubscriptionMap[deviceId].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}"
|
|
||||||
}
|
|
||||||
|
|
||||||
//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}"
|
|
||||||
}
|
|
||||||
log.info "Current location subscription map is ${state.locationSubscriptionMap}"
|
|
||||||
return ["succeed"]
|
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)
|
|
||||||
}
|
|
||||||
log.info "Removed subscription URL: ${subscriptionEndpt} for Location ${location.name}"
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
httpError(400, "missing input parameter: subscriptionURL")
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
httpError(500, "something went wrong: $e")
|
httpError(500, "something went wrong: $e")
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info "Current location subscription map is ${state.locationSubscriptionMap}"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//When events are triggered, send HTTP post to web socket servers
|
//When events are triggered, send HTTP post to web socket servers
|
||||||
def deviceEventHandler(evt) {
|
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_device = evt.device
|
||||||
def evt_deviceType = getDeviceType(evt_device)
|
def evt_deviceType = getDeviceType(evt_device);
|
||||||
def deviceInfo
|
def deviceInfo
|
||||||
|
|
||||||
def params = [ body: [deviceName: evt_device.displayName, deviceId: evt_device.id, locationId: location.id] ]
|
if(evt_deviceType == "thermostat")
|
||||||
|
{
|
||||||
if(evt.data != null){
|
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()]
|
||||||
def evtData = parseJson(evt.data)
|
}
|
||||||
log.info "Received event for ${evt_device.displayName}, data: ${evtData}, description: ${evt.descriptionText}"
|
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)]
|
||||||
}
|
}
|
||||||
|
|
||||||
//send event to all subscriptions urls
|
def params = [
|
||||||
log.debug "Current subscription urls for ${evt_device.displayName} is ${state.deviceSubscriptionMap[evt_device.id]}"
|
uri: "${state.endpointURL}/${state.connectionId}",
|
||||||
state.deviceSubscriptionMap[evt_device.id].each {
|
body: [ deviceInfo ]
|
||||||
params.uri = "${it}"
|
]
|
||||||
|
try {
|
||||||
log.trace "POST URI: ${params.uri}"
|
log.trace "POST URI: ${params.uri}"
|
||||||
log.trace "Payload: ${params.body}"
|
log.trace "Payload: ${params.body}"
|
||||||
try{
|
httpPostJson(params) { resp ->
|
||||||
httpPostJson(params) { resp ->
|
resp.headers.each {
|
||||||
log.trace "response status code: ${resp.status}"
|
log.debug "${it.name} : ${it.value}"
|
||||||
log.trace "response data: ${resp.data}"
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
log.trace "response status code: ${resp.status}"
|
||||||
log.error "something went wrong: $e"
|
log.trace "response data: ${resp.data}"
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
|
log.debug "something went wrong: $e"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def locationEventHandler(evt) {
|
//Endpoints function: update subcription endpoint url [state.endpoint]
|
||||||
log.info "Received event for location ${location.name}/${location.id}, Event: ${evt.name}, description: ${evt.descriptionText}, apiServerUrl: ${apiServerUrl("")}"
|
void updateEndpointURL() {
|
||||||
switch(evt.name){
|
state.endpointURL = params.url
|
||||||
case "DeviceCreated":
|
log.info "Updated EndpointURL to ${state.endpointURL}"
|
||||||
case "DeviceDeleted":
|
|
||||||
def evt_device = evt.device
|
|
||||||
def evt_deviceType = getDeviceType(evt_device)
|
|
||||||
log.info "DeviceName: ${evt_device.displayName}, DeviceID: ${evt_device.id}, deviceType: ${evt_deviceType}"
|
|
||||||
|
|
||||||
def params = [ body: [ eventType:evt.name, deviceId: evt_device.id, locationId: location.id ] ]
|
|
||||||
|
|
||||||
state.locationSubscriptionMap[location.id].each {
|
|
||||||
params.uri = "${it}"
|
|
||||||
log.trace "POST URI: ${params.uri}"
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Endpoints function: update global variable [state.connectionId]
|
||||||
/*** Device Query/Update Functions ***/
|
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
|
//Endpoints function: return all device data in json format
|
||||||
def getDevices() {
|
def getDevices() {
|
||||||
def deviceData = []
|
def deviceData = []
|
||||||
inputs?.each {
|
inputs?.each {
|
||||||
def deviceType = getDeviceType(it)
|
def deviceType = getDeviceType(it)
|
||||||
if(deviceType == "thermostat") {
|
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.getStatus(), deviceType:deviceType, manufacturer:it.getManufacturerName(), model:it.getModelName(), attributes: deviceAttributeList(it), locationMode: getLocationModeInfo()]
|
||||||
deviceData << [name: it.displayName, id: it.id, status:it.status, deviceType:deviceType, manufacturer:it.manufacturerName, model:it.modelName, attributes: deviceAttributeList(it, deviceType)]
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
deviceData << [name: it.displayName, id: it.id, status:it.getStatus(), deviceType:deviceType, manufacturer:it.getManufacturerName(), model:it.getModelName(), attributes: deviceAttributeList(it)]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -350,12 +247,14 @@ def getDevice() {
|
|||||||
def it = findDevice(params.id)
|
def it = findDevice(params.id)
|
||||||
def deviceType = getDeviceType(it)
|
def deviceType = getDeviceType(it)
|
||||||
def device
|
def device
|
||||||
if(deviceType == "thermostat") {
|
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.getStatus(), deviceType:deviceType, manufacturer:it.getManufacturerName(), model:it.getModelName(), attributes: deviceAttributeList(it), locationMode: getLocationModeInfo()]
|
||||||
device = [name: it.displayName, id: it.id, status:it.status, deviceType:deviceType, manufacturer:it.manufacturerName, model:it.modelName, attributes: deviceAttributeList(it, deviceType)]
|
}
|
||||||
|
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}"
|
log.debug "getDevice, return: ${device}"
|
||||||
return device
|
return device
|
||||||
}
|
}
|
||||||
@@ -451,6 +350,9 @@ private getDeviceType(device) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
case "contact sensor":
|
||||||
|
deviceType = "contactSensor"
|
||||||
|
return deviceType
|
||||||
case "garageDoorControl":
|
case "garageDoorControl":
|
||||||
deviceType = "garageDoor"
|
deviceType = "garageDoor"
|
||||||
return deviceType
|
return deviceType
|
||||||
@@ -460,15 +362,17 @@ private getDeviceType(device) {
|
|||||||
case "video camera":
|
case "video camera":
|
||||||
deviceType = "camera"
|
deviceType = "camera"
|
||||||
return deviceType
|
return deviceType
|
||||||
|
case "motion sensor":
|
||||||
|
deviceType = "motionSensor"
|
||||||
|
return deviceType
|
||||||
|
case "presence sensor":
|
||||||
|
deviceType = "presenceSensor"
|
||||||
|
return deviceType
|
||||||
case "thermostat":
|
case "thermostat":
|
||||||
deviceType = "thermostat"
|
deviceType = "thermostat"
|
||||||
return deviceType
|
return deviceType
|
||||||
case "acceleration sensor":
|
|
||||||
case "contact sensor":
|
|
||||||
case "motion sensor":
|
|
||||||
case "presence sensor":
|
|
||||||
case "water sensor":
|
case "water sensor":
|
||||||
deviceType = "genericSensor"
|
deviceType = "waterSensor"
|
||||||
return deviceType
|
return deviceType
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
@@ -483,33 +387,14 @@ private findDevice(deviceId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Return a list of device attributes
|
//Return a list of device attributes
|
||||||
private deviceAttributeList(device, deviceType) {
|
private deviceAttributeList(device) {
|
||||||
def attributeList = [:]
|
device.supportedAttributes.collectEntries { attribute->
|
||||||
def allAttributes = device.supportedAttributes
|
|
||||||
allAttributes.each { attribute ->
|
|
||||||
try {
|
try {
|
||||||
def currentState = device.currentState(attribute.name)
|
[ (attribute.name): device.currentValue(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) {
|
} catch(e) {
|
||||||
attributeList.putAll([ (attribute.name): null ]);
|
[ (attribute.name): null ]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return attributeList
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Map device command and value.
|
//Map device command and value.
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ def bridgeDiscovery(params = [:]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ssdpSubscribe()
|
ssdpSubscribe()
|
||||||
log.trace "bridgeRefreshCount: $bridgeRefreshCount"
|
|
||||||
//bridge discovery request every 15 //25 seconds
|
//bridge discovery request every 15 //25 seconds
|
||||||
if ((bridgeRefreshCount % 5) == 0) {
|
if ((bridgeRefreshCount % 5) == 0) {
|
||||||
discoverBridges()
|
discoverBridges()
|
||||||
@@ -207,7 +207,6 @@ def bulbDiscovery() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private discoverBridges() {
|
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))
|
sendHubCommand(new physicalgraph.device.HubAction("lan discovery urn:schemas-upnp-org:device:basic:1", physicalgraph.device.Protocol.LAN))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user