Hello Kikura,
I just put together all the changes based on the updated handler from carl.debily and came up with this:
/**
* TRĂ
DFRI Dimmer v0.4
*
* Copyright 2017 Kristian Andrews (v0.1)
* Modified 2018-01 by Carl de Billy (v0.2)
* and published on Github:
* https://github.com/carldebilly/SmartThings-Stuff/edit/master/devicetypes/andrews-k/tradfri-dimmer-v0-2.src/tradfri-dimmer-v0-2.groovy
*
* 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.
*
* To do:
* 1) make the handler "useful" (able to control another _real_ device)
* 2) support battery reporting
*/
metadata {
definition (name: "Tradfri Dimmer v0.4", namespace: "andrews.k", author: "Kristian Andrews") {
capability "Sensor"
capability "Configuration"
capability "Switch"
capability "Switch Level"
fingerprint endpointId: "01", profileId: "0104", deviceId: "0810", deviceVersion: "02", inClusters: "0000, 0001, 0003, 0009, 0B05, 1000", outClusters: "0003, 0004, 0006, 0008, 0019, 1000"
}
simulator {
// TODO: define status and reply messages here
}
// UI tile definitions
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"
}
}
standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
}
main(["switch"])
details(["switch", "refresh"])
}
}
def configure() {
log.debug "Configiring Reporting and Bindings."
def configCmds = []
//state = {}
configCmds += "zdo bind 0x${device.deviceNetworkId} 0x01 0x01 0x0001 {${device.zigbeeId}} {}" // battery
configCmds += "zdo bind 0x${device.deviceNetworkId} 0x01 0x01 0x0008 {${device.zigbeeId}} {}" // intensity changes
configCmds += "delay 2000"
return configCmds + refresh()
}
def refresh() {
log.debug "refreshing..."
}
// parse events into attributes
def parse(String description) {
def now = now()
computeVelocity(now)
if (description?.startsWith("catchall:")) {
def descMap = zigbee.parseDescriptionAsMap(description)
def cluster = descMap.clusterInt
def command = descMap.commandInt
def data = descMap.data
if(cluster == 0x0008) {
switch(command) {
case 0x00:
def level = Integer.parseInt(data[0], 16)
def speed = Integer.parseInt(data[1]+data[2], 16)
log.debug "cmd-$command: Go to level. Level=$level, speed=$speed."
state.velocity = 0
state.level = level
break
case 0x01:
def direction = data[0] == "00" ? "up" : "down"
def rate = Integer.parseInt(data[1], 16)
if(rate == 0xFF) rate = 5
log.debug "cmd-$command: Moving. Direction=$direction, rate=$rate."
//state.velocity = direction == "up" ? rate : -1 * rate
state.velocity = direction == "up" ? 15 : -15
break
case 0x04:
def level = Integer.parseInt(data[0], 16)
def speed = Integer.parseInt(data[1]+data[2], 16)
log.debug "cmd-$command: Go to level with on/off. Level=$level, speed=$speed."
state.velocity = 0
state.level = level
if(level == 0)
state.switch = 0
else
state.switch = 1
break
case 0x05:
def direction = data[0] == "00" ? "up" : "down"
def rate = Integer.parseInt(data[1], 16)
if(rate == 0xFF) rate = 5
log.debug "cmd-$command: Moving with on/off. Direction=$direction, rate=$rate."
//state.velocity = direction == "up" ? rate : -1 * rate
state.velocity = direction == "up" ? 15 : -15
break
case 0x07:
log.debug "cmd-$command: Stopped Moving."
state.velocity = 0
break
}
}
else {
log.debug "CATCH-ALL Map: $descMap"
}
}
sendEvents()
}
def computeVelocity(now) {
def velocity = state.velocity ?: 0
if(!velocity) return // nothing to do
def currentLevel = state.level ?: 0
def currentTs = state.timestamp = state.timestamp ?: now
def duration = (now - currentTs) / 1000000
if(!duration) return // nothing to do
def increment = (Integer)(duration * velocity);
log.debug "now: $now, cur: $currentTs, dur: $duration, Inc: $increment, ts: $state.timestamp"
state.level = currentLevel + increment
if(state.level > 0xFF) {
state.level = 0xFF
}
if(state.level < 0x00) {
state.level = 0x00
}
log.debug "Level: $state.level"
sendEvents()
}
def sendEvents() {
Integer level = (state.level ?: 0) * 100 / 0xFF
def sw = state.switch ? "on" : "off"
sendEvent(name: "level", value: level)
sendEvent(name: "switch", value: sw)
}
// handle commands
def on() {
log.debug "Executing 'on'"
state.switch = 1
state.velocity = 0
if(!state.level) {
state.level = 0xFF
}
sendEvents()
}
def off() {
log.debug "Executing 'off'"
state.switch = 0
state.velocity = 0
sendEvents()
}
def setLevel(int lev) {
log.debug "Executing 'setLevel' $lev"
Integer newLevel = lev * 0xFF / 100
state.level = newLevel
state.velocity = 0
sendEvents()
}
For me this does report events but I think there is a big in the detection of the turn timing as it jumps fully on or off whenever it is turned. I canât really follow how it is working but I added some debug lines and you can see the turn start time never updates⊠Anyway this should at least let you see something reported.
You can try my more clunky code if you like, this one has the critical device registration added so it should report events properly:
/**
* Tradfri Dimmer v0.2
*
* Copyright 2017 Kristian Andrews
*
* 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.
*
* In this handler the following actions are recognised
* turning clockwise slowly increased the level, the amount is determined by the time turning
* turning clockwise fast increased the level by 50
* turning anticlockwise slowly decreases the level, the amount is determined by the time turning
* turning anticlockwise fast decreases the level by 50
*
* There are several reported actions that I can't decipher
*
* When the level reaches 0 the switch is turned off, any level above 0 the switch is on
*
* Can be used to control lights by the use of a SmartApp such as Synchronised Dimming
*
* To do:
* 1) Debug why the light flashes when I turn off, maybe I don't need to report events for the ones that are set or the commands should be reversed?
* e.g. when Off is set from the app GUI do I need to send an Off event as well?
* 2) Update so that a fast turn clockwise/anticlockwise can turn the lights on and off but maintain the set level
*/
metadata {
definition (name: "Tradfri Dimmer v0.2", namespace: "andrews.k", author: "Kristian Andrews") {
capability "Sensor"
capability "Switch"
capability "Switch Level"
capability "Configuration"
//fingerprint endpointId: "01", profileId: "0104", deviceId: "0810", deviceVersion: "02", inClusters: "0000, 0001, 0003, 0009, 0B05, 1000", outClusters: "0003, 0004, 0006, 0008, 0019, 1000"
}
simulator {
// TODO: define status and reply messages here
}
// UI tile definitions
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"
}
}
standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
}
main(["switch"])
details(["switch", "refresh"])
}
}
// parse events into attributes
def parse(String description) {
log.debug "Parsing '${description}'"
// TODO: handle 'switch' attribute
// TODO: handle 'level' attribute
def name = null
def value = null
def name2 = null
def value2 = null
// Check if the stored variables are initialised
if (state.level == null) {
state.level = 100
}
if (state.OnOff == null) {
state.OnOff = 1
}
if (state.start == null) {
state.start = now()
}
if (state.clockwise == null) {
state.clockwise = 0
}
if (description?.startsWith("catchall:")) {
//def descMap = zigbee.parseDescriptionAsMap(description)
log.debug "Catch all: $description"
log.debug zigbee.parseDescriptionAsMap(description)
// Responds to Zigbee events sent by the dimmer
// Probably not the most elegant solution
if (description.endsWith("00 0000 05 00 00C3")) {
// Start Turn clockwise
log.debug "cw"
// Store when the turning started and the direction
state.start = now()
state.clockwise = 1
} else if (description.endsWith("00 0000 01 00 01C3")) {
// Start turn anticlockwise
log.debug "acw"
// Store when the turning started and the direction
state.start = now()
state.clockwise = -1
} else if (description.endsWith("00 0000 07 00 ")) {
// Stop turning
log.debug "Stop"
// Calculate duration of turn
def turnTime = now() - state.start
//log.debug "Turn ms: $turnTime"
// If the turn is too long ignore it, it probably came from a missed event
if (turnTime > 2500) {
turnTime = 0
}
// How much change to the level
// 2000 ms = full up or down
int change = turnTime / 20
//log.debug change
if (state.clockwise == 1) {
// If clockwise, increase the level and turn on
// Update the stored state
state.level = state.level + change
// If above 100 reset to 100
if (state.level > 100){
state.level = 100
}
// Create the ST event details
name = "level"
value = state.level
// Turn on switch if off
if (state.OnOff == 0){
// Create the ST event details
name2 = "switch"
value2 = "on"
// Update the stored state
state.OnOff = 1
}
// Set state to not turning
state.clockwise = 0
} else if (state.clockwise == -1){
// If anticlockwise, decrease the level
// Reduce the stored level
state.level = state.level - change
// If below 0 reset to 0
if (state.level < 0){
// Make sure it doesn't go below 0
state.level = 0
}
// Create the ST event details
name = "level"
value = state.level
// If level = 0 then turn off switch
if (state.level == 0) {
// Update stored state
state.OnOff = 0
// Create ST event details
name2 = "switch"
value2 = "off"
}
// Set state to not turning
state.clockwise = 0
}
} else if (description.endsWith("00 0000 04 00 000100")) {
// Fast turn anti-clockwise
log.debug "fast acw"
// turn down level by 50
state.level = state.level - 50
// If below 0 reset to 0
if (state.level < 0){
state.level = 0
}
// Create ST event details
name = "level"
value = state.level
// if reached 0 turn off switch
if (state.level == 0) {
if (state.OnOff == 1) {
state.OnOff = 0
name2 = "switch"
value2 = "off"
}
}
} else if (description.endsWith("00 0000 04 00 FF0100")) {
// Fast turn clockwise
log.debug "fast cw"
// turn up level by 50
state.level = state.level + 50
// make sure it doesn't go above 100
if (state.level > 100) {
state.level = 100
}
name = "level"
value = state.level
if (state.OnOff == 0) {
state.OnOff = 1
name2 = "switch"
value2 = "on"
}
} else if (description.endsWith("00 0000 07 00 ")) {
// Unknown reply - appears after most turns
log.debug "Unknown catchall 1"
} else {
log.debug "Other Catch all: '$description'"
// The output I have seen so far from the dimmer
// catchall: 0104 0008 01 01 0100 00 D035 01 00 0000 04 00 000100 - fast turn anti-clockwise
// catchall: 0104 0008 01 01 0140 00 D035 01 00 0000 04 00 000100 - different type of fast turn anti-clockwise?
// catchall: 0104 0008 01 01 0100 00 D035 01 00 0000 04 00 FF0100 - fast turn clockwise
// catchall: 0104 0008 01 01 0140 00 D035 01 00 0000 04 00 FF0100 - different type of fast turn clockwise?
//def descMap = zigbee.parseDescriptionAsMap(description)
//log.debug zigbee.parseDescriptionAsMap(description)
}
} else {
log.debug "Not catch all: $description"
}
// createEvent returns a Map that defines an Event
def result = createEvent(name: name, value: value)
def result2 = createEvent(name: name2, value: value2)
// If there is any result log it
if (name != null) {
log.debug "Result 1: ${result?.descriptionText}"
}
if (name2 != null) {
log.debug "Result 2: ${result2?.descriptionText}"
}
// returning the Event definition map creates an Event
// in the SmartThings platform, and propagates it to
// SmartApps subscribed to the device events.
return [result, result2]
}
// handle commands
def on() {
log.debug "Executing 'on'"
// Handles 'on' commands from the SmartThings app or others
// if switched off, turn on and set level to 50
state.level = 50
state.OnOff = 1
// Send switch on and level events
sendEvent(name: "switch", value: "on")
sendEvent(name: "level", value: 50)
}
def off() {
log.debug "Executing 'off'"
// Handles 'off' commands from the SmartThings app or others
// Update the stored state
state.level = 0 // Set level to 0
state.OnOff = 0 // Set to off
// Send events to report set level and switch off
sendEvent(name: "switch", value: "off")
sendEvent(name: "level", value: 0)
}
def setLevel(int lev) {
log.debug "Executing 'setLevel' $lev"
// Handles 'setLevel' commands from the SmartThings app or others
// If the level has changed
if (lev != state.level) {
if (lev == 0) {
// If 0 switch off
// Update the stored state
state.level = lev
state.OnOff = 0
// Send events for the updates
sendEvent(name: "switch", value: "off")
sendEvent(name: "level", value: 0)
} else {
// Set the stored level
state.level = lev
// If above 100 set to 100
if (state.level > 100) {
state.level = 100
}
// If the switch is off turn it on
if (state.OnOff == 0) {
state.OnOff = 1 // Update stored state to on
sendEvent(name: "switch", value: "on")
}
// Send event to set the level (needed?)
sendEvent(name: "level", value: state.level)
}
}
}
def configure() {
log.debug "Configure called"
["zdo bind 0x${device.deviceNetworkId} 0x01 0x01 8 {${device.zigbeeId}} {}"]
}
With this I can change the level gradually even if the code isnât correct
I hope this helps,
Kris.