Permalink
Switch branches/tags
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
358 lines (318 sloc) 13.6 KB
/**
* Zooz Zen22 Dimmer Switch v2
*
* Revision History:
* 2017-08-29 - Initial release
* 2017-09-06 - Change color scheme to match new ST standard
* 2017-09-08 - Remove extra basic report that firmware returns causing duplicate events to display
* 2017-09-09 - Fix problem with slider going to zero when turning off, refactor to address double event (change control from light to switch)
* 2018-02-11 - Update for new parameter for switches purchased after 2/1/18
* 2018-03-31 - Changed 99% setting (platform maximum) to show 100% in the slider
* 2018-08-31 - Added Manufacturer ID and Vendor ID for new SmartThings connect app. Device settings not available. Will likely need update when new standards are published.
*
* Supported Command Classes
* Association v2
* Association Group Information
* Basic
* Configuration
* Device Reset Local
* Manufacturer Specific v2
* Powerlevel
* Switch_all
* Switch_multilevel
* Version v2
* ZWavePlus Info v2
*
* Parm Size Description Value
* 1 1 Invert Switch 0 (Default)-Upper paddle turns light on, 1-Lower paddle turns light on
* 2 1 LED Indicator 0 (Default)-LED is on when light is OFF, 1-LED is on when light is ON
* 3 1 LED Disable 0 (Default)-LED is on based on parameter 2, 1-LED is off always
* 4 1 Fast Ramp 0 (Default)-100% brightness within 2 seconds and off in 4 seconds, 1-instant 100% and off in 1 second
*/
metadata {
definition (name: "Zooz Zen22 Dimmer v2", namespace: "doncaruana", author: "Don Caruana", ocfDeviceType: "oic.d.light", mnmn: "SmartThings", vid: "generic-dimmer") {
capability "Switch Level"
capability "Actuator"
capability "Health Check"
capability "Switch"
capability "Polling"
capability "Refresh"
capability "Sensor"
capability "Light"
//zw:L type:1101 mfr:027A prod:B112 model:1F1C ver:20.15 zwv:4.05 lib:06 cc:5E,85,59,70,5A,72,73,27,26,86,20 role:05 ff:9C02 ui:9C00
fingerprint mfr:"027A", prod:"B112", model:"1F1C", deviceJoinName: "Zooz Zen22 Dimmer v2"
fingerprint deviceId:"0x1101", inClusters: "0x5E,0x59,0x85,0x70,0x5A,0x72,0x73,0x27,0x26,0x86,0x20"
fingerprint cc: "0x5E,0x59,0x85,0x70,0x5A,0x72,0x73,0x27,0x26,0x86,0x20", mfr:"027A", prod:"B112", model:"1F1C", deviceJoinName: "Zooz Zen22 Dimmer v2"
}
simulator {
status "on": "command: 2003, payload: FF"
status "off": "command: 2003, payload: 00"
status "09%": "command: 2003, payload: 09"
status "10%": "command: 2003, payload: 0A"
status "33%": "command: 2003, payload: 21"
status "66%": "command: 2003, payload: 42"
status "99%": "command: 2003, payload: 63"
// reply messages
reply "2001FF,delay 5000,2602": "command: 2603, payload: FF"
reply "200100,delay 5000,2602": "command: 2603, payload: 00"
reply "200119,delay 5000,2602": "command: 2603, payload: 19"
reply "200132,delay 5000,2602": "command: 2603, payload: 32"
reply "20014B,delay 5000,2602": "command: 2603, payload: 4B"
reply "200163,delay 5000,2602": "command: 2603, payload: 63"
}
preferences {
input "ledIndicator", "bool", title: "LED on when light on", description: "LED will be on when light OFF if not set", required: false, defaultValue: false
input "invertSwitch", "bool", title: "Invert Switch", description: "Flip switch upside down", required: false, defaultValue: false
input "ledDisable", "bool", title: "LED Disabled", description: "Turn off LED completely", required: false, defaultValue: false
input "fastRamp", "bool", title: "Fast Ramp on/off", description: "Only for switches purchased after 2/1/18", required: false, defaultValue: false
}
tiles(scale: 2) {
multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
attributeState "on", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#00a0dc", nextState:"turningOff"
attributeState "off", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn"
attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#00a0dc", nextState:"turningOff"
attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn"
}
tileAttribute ("device.level", key: "SLIDER_CONTROL") {
attributeState "level", action:"switch level.setLevel"
}
}
standardTile("refresh", "device.switch", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
}
valueTile("level", "device.level", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "level", label:'${currentValue} %', unit:"%", backgroundColor:"#ffffff"
}
main(["switch"])
details(["switch", "level", "refresh"])
}
}
def installed() {
def cmds = []
// Device-Watch simply pings if no device events received for 32min(checkInterval)
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
cmds << mfrGet()
cmds << zwave.versionV1.versionGet().format()
cmds << parmGet(1)
cmds << parmGet(2)
cmds << parmGet(3)
cmds << parmGet(4)
def level = 99
cmds << zwave.basicV1.basicSet(value: level).format()
cmds << zwave.switchMultilevelV1.switchMultilevelGet().format()
return response(delayBetween(cmds,200))
}
def updated(){
def commands = []
if (getDataValue("MSR") == null) {
def level = 99
commands << mfrGet()
commands << zwave.versionV1.versionGet().format()
commands << zwave.basicV1.basicSet(value: level).format()
commands << zwave.switchMultilevelV1.switchMultilevelGet().format()
}
//parmset takes the parameter number, it's size, and the value - in that order
commands << parmSet(4, 1, [fastRamp == true ? 1 : 0])
commands << parmSet(3, 1, [ledDisable == true ? 1 : 0])
commands << parmSet(2, 1, [ledIndicator == true ? 1 : 0])
commands << parmSet(1, 1, [invertSwitch == true ? 1 : 0])
commands << parmGet(1)
commands << parmGet(2)
commands << parmGet(3)
commands << parmGet(4)
// Device-Watch simply pings if no device events received for 32min(checkInterval)
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
return response(delayBetween(commands, 500))
}
private getCommandClassVersions() {
[
0x59: 1, // AssociationGrpInfo
0x85: 2, // Association
0x5A: 1, // DeviceResetLocally
0x72: 2, // ManufacturerSpecific
0x73: 1, // Powerlevel
0x86: 1, // Version
0x5E: 2, // ZwaveplusInfo
0x27: 1, // All Switch
0x26: 1, // Multilevel Switch
0x70: 1, // Configuration
0x20: 1, // Basic
]
}
def parse(String description) {
def result = null
// UI Trick to make 99%, which is actually the platform limit, show 100% on the control
if (description.indexOf('command: 2603, payload: 63 63 00') > -1) {
description = description.replaceAll('payload: 63 63 00','payload: 64 64 00')
}
if (description != "updated") {
log.debug "parse() >> zwave.parse($description)"
def cmd = zwave.parse(description, commandClassVersions)
if (cmd) {
result = zwaveEvent(cmd)
}
}
if (result?.name == 'hail' && hubFirmwareLessThan("000.011.00602")) {
result = [result, response(zwave.basicV1.basicGet())]
log.debug "Was hailed: requesting state update"
} else {
log.debug "Parse returned ${result?.descriptionText}"
}
return result
}
def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) {
dimmerEvents(cmd)
}
def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd) {
dimmerEvents(cmd)
}
def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv1.SwitchMultilevelReport cmd) {
dimmerEvents(cmd)
}
def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv1.SwitchMultilevelSet cmd) {
dimmerEvents(cmd)
}
private dimmerEvents(physicalgraph.zwave.Command cmd) {
def value = (cmd.value ? "on" : "off")
def result = [createEvent(name: "switch", value: value)]
if (cmd.value && cmd.value <= 100) {
result << createEvent(name: "level", value: cmd.value, unit: "%")
}
return result
}
def zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport cmd) {
def name = ""
def value = ""
def reportValue = cmd.configurationValue[0]
log.debug "---CONFIGURATION REPORT V1--- ${device.displayName} parameter ${cmd.parameterNumber} with a byte size of ${cmd.size} is set to ${cmd.configurationValue}"
switch (cmd.parameterNumber) {
case 1:
name = "topoff"
value = reportValue == 1 ? "true" : "false"
break
case 2:
name = "ledfollow"
value = reportValue == 1 ? "true" : "false"
break
case 3:
name = "ledoff"
value = reportValue == 1 ? "true" : "false"
break
case 4:
name = "rampfast"
value = reportValue == 1 ? "true" : "false"
break
default:
break
}
createEvent([name: name, value: value])
}
def zwaveEvent(physicalgraph.zwave.commands.hailv1.Hail cmd) {
createEvent([name: "hail", value: "hail", descriptionText: "Switch button was pressed", displayed: false])
}
def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) {
def manufacturerCode = String.format("%04X", cmd.manufacturerId)
def productTypeCode = String.format("%04X", cmd.productTypeId)
def productCode = String.format("%04X", cmd.productId)
def msr = manufacturerCode + "-" + productTypeCode + "-" + productCode
updateDataValue("MSR", msr)
updateDataValue("Manufacturer", "Zooz")
updateDataValue("Manufacturer ID", manufacturerCode)
updateDataValue("Product Type", productTypeCode)
updateDataValue("Product Code", productCode)
createEvent([descriptionText: "$device.displayName MSR: $msr", isStateChange: false])
}
def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv1.SwitchMultilevelStopLevelChange cmd) {
[createEvent(name:"switch", value:"on"), response(zwave.switchMultilevelV1.switchMultilevelGet().format())]
}
def zwaveEvent(physicalgraph.zwave.commands.crc16encapv1.Crc16Encap cmd) {
def versions = commandClassVersions
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) {
// Handles all Z-Wave commands we aren't interested in
[:]
}
def on() {
delayBetween([
zwave.basicV1.basicSet(value: 0xFF).format(),
zwave.switchMultilevelV1.switchMultilevelGet().format()
],2000)
}
def off() {
delayBetween([
zwave.basicV1.basicSet(value: 0x00).format(),
zwave.switchMultilevelV1.switchMultilevelGet().format()
],2000)
}
def setLevel(value) {
log.debug "setLevel >> value: $value"
def valueaux = value as Integer
def level = Math.max(Math.min(valueaux, 99), 0)
if (level > 0) {
sendEvent(name: "switch", value: "on")
} else {
sendEvent(name: "switch", value: "off")
}
sendEvent(name: "level", value: level, unit: "%")
delayBetween ([zwave.basicV1.basicSet(value: level).format(), zwave.switchMultilevelV1.switchMultilevelGet().format()], 2000)
}
def setLevel(value, duration) {
log.debug "setLevel >> value: $value, duration: $duration"
def valueaux = value as Integer
def level = Math.max(Math.min(valueaux, 99), 0)
def dimmingDuration = duration < 128 ? duration : 128 + Math.round(duration / 60)
def getStatusDelay = duration < 128 ? (duration*1000)+2000 : (Math.round(duration / 60)*60*1000)+2000
delayBetween ([zwave.switchMultilevelV2.switchMultilevelSet(value: level, dimmingDuration: dimmingDuration).format(),
zwave.switchMultilevelV1.switchMultilevelGet().format()], getStatusDelay)
}
def poll() {
zwave.switchMultilevelV1.switchMultilevelGet().format()
}
/**
* PING is used by Device-Watch in attempt to reach the Device
* */
def ping() {
refresh()
}
def refresh() {
log.debug "refresh() is called"
def commands = []
if (getDataValue("MSR") == null) {
commands << mfrGet()
commands << zwave.versionV1.versionGet().format()
}
commands << zwave.switchMultilevelV1.switchMultilevelGet().format()
delayBetween(commands,100)
}
def parmSet(parmnum, parmsize, parmval) {
return zwave.configurationV1.configurationSet(configurationValue: parmval, parameterNumber: parmnum, size: parmsize).format()
}
def parmGet(parmnum) {
return zwave.configurationV1.configurationGet(parameterNumber: parmnum).format()
}
def mfrGet() {
return zwave.manufacturerSpecificV2.manufacturerSpecificGet().format()
}
def zwaveEvent(physicalgraph.zwave.commands.versionv1.VersionReport cmd) {
updateDataValue("applicationVersion", "${cmd.applicationVersion}")
updateDataValue("applicationSubVersion", "${cmd.applicationSubVersion}")
updateDataValue("zWaveLibraryType", "${cmd.zWaveLibraryType}")
updateDataValue("zWaveProtocolVersion", "${cmd.zWaveProtocolVersion}")
updateDataValue("zWaveProtocolSubVersion", "${cmd.zWaveProtocolSubVersion}")
}
def zwaveEvent(physicalgraph.zwave.commands.versionv1.VersionCommandClassReport cmd) {
log.debug "vccr"
def rcc = ""
log.debug "version: ${cmd.commandClassVersion}"
log.debug "class: ${cmd.requestedCommandClass}"
rcc = Integer.toHexString(cmd.requestedCommandClass.toInteger()).toString()
log.debug "${rcc}"
if (cmd.commandClassVersion > 0) {log.debug "0x${rcc}_V${cmd.commandClassVersion}"}
}