And here is the code for this
/*
* ZLight2 does not support attribute reporting so the only way to get attribute value updates
* is the refresh button and polling.
* ZLL spec does not support configure reporting on the Lights attributes
*
*/
metadata {
// Automatically generated. Make future change here.
definition (name: “ZLight2”, namespace: “sozonoff”, author: “Sozonoff”) {
capability "Switch Level"
capability "Color Control"
capability "Switch"
capability "Polling"
capability "Refresh"
capability “Sensor”
fingerprint endpointId: "0B", profileId: "0104", deviceId: "0102", inClusters: "0000 0003 0004 0005 0006 0008 0300"
fingerprint endpointId: "0D", profileId: "C05E", inClusters: "1000", outClusters: "1000"
fingerprint endpointId: "0C", profileId: "0104", deviceId: "0200"
}
simulator {
}
// UI tile definitions
tiles {
standardTile("switch", "device.switch", width: 1, height: 1, canChangeIcon: true) {
state "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff"
state "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821"
}
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") {
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
}
controlTile("rgbSelector", "device.color", "color", height: 3, width: 3, inactiveLabel: false) {
state "color", action:"color control.setColor"
}
controlTile("levelSliderControl", "device.level", "slider", height: 1, width: 2, inactiveLabel: false) {
state "level", action:"switch level.setLevel"
}
valueTile("level", "device.level", inactiveLabel: false, decoration: "flat", width: 1, height: 1) {
state "level", label: 'Level ${currentValue}%'
}
valueTile("saturation", "device.saturation", inactiveLabel: false, decoration: "flat", width: 1, height: 1) {
state "saturation", label: 'Sat ${currentValue} '
}
main(["switch"])
details(["switch", "levelSliderControl", "rgbSelector", "level", "saturation", "refresh"])
}
}
def parseDescriptionAsMap(description) {
(description - "read attr - ").split(",").inject([:]) { map, param ->
def nameAndValue = param.split(":")
map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
}
}
// Parse incoming device messages to generate events
def parse(String description) {
log.trace("desc: ${description}")
// for some reason the on/off read comes in a catchall....
if (description?.startsWith("catchall:")) {
def msg = zigbee.parse(description)
if(msg.clusterId == 0x0006 && msg.command == 0x01) {
log.trace("got on/off switch status ${msg.data}")
def v = msg.data[4] == 1 ? "on" : "off"
sendEvent(name: "switch", value: v)
}
}
// handle read attr responses
if (description?.startsWith("read attr")) {
def respMap = parseDescriptionAsMap(description)
switch(respMap.cluster) {
case "0006" :
break
case "0008" :
if (respMap.attrId == "0000") {
def i = Math.round(convertHexToInt(respMap.value) / 256 * 100 )
log.trace("hue value: ${i}")
sendEvent( name: "level", value: i )
}
break
}
}
}
def on() {
log.debug "on()"
sendEvent(name: "switch", value: "on")
def cmds = []
// restore to the last level recorded
if (device.latestValue("level"))
cmds << setLevel(device.latestValue("level"))
cmds << "st cmd 0x${device.deviceNetworkId} 0X0B 6 1 {}"
return cmds
}
def off() {
// just assume it works for now
log.debug "off()"
sendEvent(name: "switch", value: "off")
"st cmd 0x${device.deviceNetworkId} 0X0B 6 0 {}"
}
def setHue(value) {
// Hue is provided by the ST colour wheel as a percentage, needs to be converted to a range between 0-254
log.trace "setHue($value)"
sendEvent(name: "hue", value: value)
def scaledValue = convertHueFromPercentage(value)
def cmd = "st cmd 0x${device.deviceNetworkId} 0X0B 0x300 0x00 {${hex(scaledValue)} 00 0000}"
cmd
}
private convertHueFromPercentage(value) {
def max = 0xfe
return Math.round(value * max / 100.0)
}
def setColor(value) {
log.trace "setColor($value)"
def max = 0xfe
sendEvent(name: "hue", value: value.hue)
sendEvent(name: "saturation", value: value.saturation)
def scaledHueValue = convertHueFromPercentage(value.hue)
def scaledSatValue = Math.round(value.saturation * max / 100.0)
def cmd = []
if (value.switch != "off" && device.latestValue("switch") == "off") {
cmd << "st cmd 0x${device.deviceNetworkId} 0X0B 6 1 {}"
cmd << "delay 150"
}
cmd << "st cmd 0x${device.deviceNetworkId} 0X0B 0x300 0x00 {${hex(scaledHueValue)} 00 0000}"
cmd << "delay 150"
cmd << "st cmd 0x${device.deviceNetworkId} 0X0B 0x300 0x03 {${hex(scaledSatValue)} 0000}"
// restore to the last level recorded
if (device.latestValue("level")) {
cmd << "delay 150"
cmd << setLevel(device.latestValue("level"))
}
if (value.switch == "off") {
cmd << "delay 150"
cmd << off()
}
log.info cmd
cmd
}
def setSaturation(value) {
def max = 0xfe
log.trace "setSaturation($value)"
sendEvent(name: "saturation", value: value)
def scaledValue = Math.round(value * max / 100.0)
def cmd = "st cmd 0x${device.deviceNetworkId} 0X0B 0x300 0x03 {${hex(scaledValue)} 0000}"
cmd
}
def refresh() {
log.trace("refreshing")
def cmd = []
cmd << ["st rattr 0x${device.deviceNetworkId} 0x0B 0x0006 0x00", "delay 1000"]
cmd << "st rattr 0x${device.deviceNetworkId} 0x0B 0x0008 0x00"
return cmd
}
def poll(){
log.debug "Poll is calling refresh"
refresh()
}
def setLevel(value) {
log.trace "setLevel($value)"
def cmds = []
if (value == 0) {
sendEvent(name: "switch", value: "off")
cmds << off()
} else if (value > 0 && device.latestValue("switch") == "off") {
cmds << on()
}
sendEvent(name: "level", value: value)
def level = new BigInteger(Math.round(value * 255 / 100).toString()).toString(16)
cmds << "st cmd 0x${device.deviceNetworkId} 0X0B 8 4 {${level} 0000}"
//log.debug cmds
cmds
}
private Integer convertHexToInt(hex) {
Integer.parseInt(hex,16)
}
private hex(value, width=2) {
def s = new BigInteger(Math.round(value).toString()).toString(16)
while (s.size() < width) {
s = "0" + s
}
s
}