OSRAM E27 Edison Screw A60 ES 10W LED Lightify RGB Light Bulb

I think the errors might be giving us trouble. I didn’t give you very good directions about the parseDescriptionAsMap() part, so here’s the code adjusted a little.

/**
 *  Copyright 2015 SmartThings
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License. You may obtain a copy of the License at:
 *
 *      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.
 *
 */
/* OSRAM Lightify RGBW Bulb

Capabilities:
  Actuator
  Color Control
  Configuration
  Polling
  Refresh
  Sensor
  Switch
  Switch Level
  
Custom Commands:
  setAdjustedColor
    
*/

metadata {
	definition (name: "OSRAM Lightify Zigbee RGBW Bulb", namespace: "guy_mayhew", author: "Guy Mayhew") {
		capability "Switch Level"
		capability "Actuator"
		capability "Color Control"
		capability "Switch"
		capability "Configuration"
		capability "Polling"
		capability "Refresh"
		capability "Sensor"

		command "setAdjustedColor"

		fingerprint profileId: "C05E", inClusters: "0000,0003,0004,0005,0006,0008,0300,1000", outClusters: "0019"
	}

	// simulator metadata
	simulator {
		// status messages
		status "on": "on/off: 1"
		status "off": "on/off: 0"

		// reply messages
		reply "zcl on-off on": "on/off: 1"
		reply "zcl on-off off": "on/off: 0"
	}

	// UI tile definitions
	tiles {
		standardTile("switch", "device.switch", width: 1, height: 1, canChangeIcon: true) {
			state "on", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#79b821", nextState:"turningOff"
			state "off", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn"
			state "turningOn", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#79b821", nextState:"turningOff"
			state "turningOff", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn"
		}
		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:"setAdjustedColor"
		}
		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") {
			state "level", label: 'Level ${currentValue}%'
		}
		controlTile("saturationSliderControl", "device.saturation", "slider", height: 1, width: 2, inactiveLabel: false) {
			state "saturation", action:"color control.setSaturation"
		}
		valueTile("saturation", "device.saturation", inactiveLabel: false, decoration: "flat") {
			state "saturation", label: 'Sat ${currentValue}    '
		}
		controlTile("hueSliderControl", "device.hue", "slider", height: 1, width: 2, inactiveLabel: false) {
			state "hue", action:"color control.setHue"
		}

		main(["switch"])
		details(["switch", "levelSliderControl", "rgbSelector", "refresh"])
	}
}

// Parse incoming device messages to generate events
def parse(String description) {
    log.trace description
    if (description?.startsWith("catchall:")) {
        if(description?.endsWith("0100") ||description?.endsWith("1001") || description?.matches("on/off\\s*:\\s*1"))
        {
            def result = createEvent(name: "switch", value: "on")
            log.debug "Parse returned ${result?.descriptionText}"
            return result
        }
        else if(description?.endsWith("0000") || description?.endsWith("1000") || description?.matches("on/off\\s*:\\s*0"))
        {
            if(!(description?.startsWith("catchall: 0104 0300"))){
                def result = createEvent(name: "switch", value: "off")
                log.debug "Parse returned ${result?.descriptionText}"
                return result
            }
        }
    }
    else if (description?.startsWith("read attr -")) {
        def descMap = parseDescriptionAsMap(description)
        log.trace "descMap : $descMap"

        if (descMap.cluster == "0300") {
            if(descMap.attrId == "0000"){  //Hue Attribute
                def hueValue = Math.round(convertHexToInt(descMap.value) / 255 * 360)
                log.debug "Hue value returned is $hueValue"
                sendEvent(name: "hue", value: hueValue, displayed:false)
            }
            else if(descMap.attrId == "0001"){ //Saturation Attribute
                def saturationValue = Math.round(convertHexToInt(descMap.value) / 255 * 100)
                log.debug "Saturation from refresh is $saturationValue"
                sendEvent(name: "saturation", value: saturationValue, displayed:false)
            }
        }
        else if(descMap.cluster == "0008"){
            def dimmerValue = Math.round(convertHexToInt(descMap.value) * 100 / 255)
            log.debug "dimmer value is $dimmerValue"
            sendEvent(name: "level", value: dimmerValue)
        }
    }
    else {
        def name = description?.startsWith("on/off: ") ? "switch" : null
        def value = name == "switch" ? (description?.endsWith(" 1") ? "on" : "off") : null
        def result = createEvent(name: name, value: value)
        log.debug "Parse returned ${result?.descriptionText}"
        return result
    }
}
def parseDescriptionAsMap(description) {
    (description - "read attr - ").split(",").inject([:]) { map, param ->
        def nameAndValue = param.split(":")
        map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
}
def on() {
	// just assume it works for now
	log.debug "on()"
	sendEvent(name: "switch", value: "on")
	"st cmd 0x${device.deviceNetworkId} ${endpointId} 6 1 {}"
    sendEvent(name: "level", value: 10)
}

def off() {
	// just assume it works for now
	log.debug "off()"
	sendEvent(name: "switch", value: "off")
	"st cmd 0x${device.deviceNetworkId} ${endpointId} 6 0 {}"
}

def setHue(value) {
	def max = 0xfe
	log.trace "setHue($value)"
	sendEvent(name: "hue", value: value)
	def scaledValue = Math.round(value * max / 100.0)
	def cmd = "st cmd 0x${device.deviceNetworkId} ${endpointId} 0x300 0x00 {${hex(scaledValue)} 00 2000}"
	//log.info cmd
	cmd
}

def setAdjustedColor(value) {
	log.debug "setAdjustedColor: ${value}"
	def adjusted = value + [:]
	adjusted.hue = adjustOutgoingHue(value.hue)
	adjusted.level = null // needed because color picker always sends 100
	setColor(adjusted)
}

def setColor(value){
	log.trace "setColor($value)"
	def max = 0xfe

	sendEvent(name: "hue", value: value.hue)
	sendEvent(name: "saturation", value: value.saturation)
	def scaledHueValue = Math.round(value.hue * max / 100.0)
	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} ${endpointId} 6 1 {}"
		cmd << "delay 150"
	}

	cmd << "st cmd 0x${device.deviceNetworkId} ${endpointId} 0x300 0x00 {${hex(scaledHueValue)} 00 1500}"
	cmd << "delay 150"
	cmd << "st cmd 0x${device.deviceNetworkId} ${endpointId} 0x300 0x03 {${hex(scaledSatValue)} 1500}"

	if (value.level != null) {
		cmd << "delay 150"
		cmd.addAll(setLevel(value.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} ${endpointId} 0x300 0x03 {${hex(scaledValue)} 1500 }"
	//log.info cmd
	cmd
}

def configure() {
    state.levelValue = 100
    log.debug "Configuring Reporting and Bindings."
    def configCmds = [

            //Switch Reporting
            "zcl global send-me-a-report 6 0 0x10 0 3600 {01}", "delay 500",
            "send 0x${device.deviceNetworkId} ${endpointId} 1", "delay 1000",

            //Level Control Reporting
            "zcl global send-me-a-report 8 0 0x20 5 3600 {0010}", "delay 200",
            "send 0x${device.deviceNetworkId} ${endpointId} 1", "delay 1500",

            "zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 6 {${device.zigbeeId}} {}", "delay 1000",
            "zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 8 {${device.zigbeeId}} {}", "delay 500",
            "zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 0x0300 {${device.zigbeeId}} {}", "delay 500"
    ]
    return configCmds + refresh() // send refresh cmds as part of config
}

def refresh() {
 	[	
            "st rattr 0x${device.deviceNetworkId} ${endpointId} 6 0", "delay 500",
            "st rattr 0x${device.deviceNetworkId} ${endpointId} 8 0", "delay 500",
            "st rattr 0x${device.deviceNetworkId} ${endpointId} 0x0300 0", "delay 500",
            "st rattr 0x${device.deviceNetworkId} ${endpointId} 0x0300 1", "delay 500",
            "st wattr 0x${device.deviceNetworkId} 1 8 0x10 0x21 {0015}"
        ]
}

def poll(){
	log.debug "Poll is calling refresh"
	refresh()
}

def zigbeeSetLevel(level) {
    "st cmd 0x${device.deviceNetworkId} ${endpointId} 8 4 {${level} 1500}"
}

def setLevel(value) {
	state.levelValue = (value==null) ? 100 : value
    log.trace "setLevel($value)"
    def cmds = []

    if (value == 0) {
        sendEvent(name: "switch", value: "off")
        cmds << zigbeeOff()
    }
    else if (device.latestValue("switch") == "off") {
        sendEvent(name: "switch", value: "on")
    }

    sendEvent(name: "level", value: state.levelValue)
    def level = hex(state.levelValue * 255 / 100)
    cmds << zigbeeSetLevel(level)

    //log.debug cmds
    cmds
}

private getEndpointId() {
	new BigInteger(device.endpointId, 16).toString()
}

private hex(value, width=2) {
	def s = new BigInteger(Math.round(value).toString()).toString(16)
	while (s.size() < width) {
		s = "0" + s
	}
	s
}

private adjustOutgoingHue(percent) {
	def adjusted = percent
	if (percent > 31) {
		if (percent < 63.0) {
			adjusted = percent + (7 * (percent -30 ) / 32)
		}
		else if (percent < 73.0) {
			adjusted = 69 + (5 * (percent - 62) / 10)
		}
		else {
			adjusted = percent + (2 * (100 - percent) / 28)
		}
	}
	log.info "percent: $percent, adjusted: $adjusted"
	adjusted
}

Hi Scott,

I seem to be getting this on the refresh when using that code:

37936b7c-8928-490a-a30e-9bd99e86dbfb ‎14‎:‎24‎:‎40: error groovy.lang.MissingMethodException: No signature of method: script1445026156258679568561.parseDescriptionAsMap() is applicable for argument types: (java.lang.String) values: [read attr - raw: 5F460303000A0100002000, dni: 5F46, endpoint: 03, cluster: 0300, size: 0A, attrId: 0001, result: success, encoding: 20, value: 00] @ line 113

37936b7c-8928-490a-a30e-9bd99e86dbfb ‎14‎:‎24‎:‎39: error groovy.lang.MissingMethodException: No signature of method: script1445026156258679568561.parseDescriptionAsMap() is applicable for argument types: (java.lang.String) values: [read attr - raw: 5F460303000A0000002000, dni: 5F46, endpoint: 03, cluster: 0300, size: 0A, attrId: 0000, result: success, encoding: 20, value: 00] @ line 113

37936b7c-8928-490a-a30e-9bd99e86dbfb ‎14‎:‎24‎:‎39: error groovy.lang.MissingMethodException: No signature of method: script1445026156258679568561.parseDescriptionAsMap() is applicable for argument types: (java.lang.String) values: [read attr - raw: 5F460303000A0100002000, dni: 5F46, endpoint: 03, cluster: 0300, size: 0A, attrId: 0001, result: success, encoding: 20, value: 00] @ line 113

37936b7c-8928-490a-a30e-9bd99e86dbfb ‎14‎:‎24‎:‎39: error groovy.lang.MissingMethodException: No signature of method: script1445026156258679568561.parseDescriptionAsMap() is applicable for argument types: (java.lang.String) values: [read attr - raw: 5F460300080A000000201A, dni: 5F46, endpoint: 03, cluster: 0008, size: 0A, attrId: 0000, result: success, encoding: 20, value: 1a] @ line 113

37936b7c-8928-490a-a30e-9bd99e86dbfb ‎14‎:‎24‎:‎39: error groovy.lang.MissingMethodException: No signature of method: script1445026156258679568561.parseDescriptionAsMap() is applicable for argument types: (java.lang.String) values: [read attr - raw: 5F460303000A0000002000, dni: 5F46, endpoint: 03, cluster: 0300, size: 0A, attrId: 0000, result: success, encoding: 20, value: 00] @ line 113

37936b7c-8928-490a-a30e-9bd99e86dbfb ‎14‎:‎24‎:‎39: error groovy.lang.MissingMethodException: No signature of method: script1445026156258679568561.parseDescriptionAsMap() is applicable for argument types: (java.lang.String) values: [read attr - raw: 5F460303000A0100002000, dni: 5F46, endpoint: 03, cluster: 0300, size: 0A, attrId: 0001, result: success, encoding: 20, value: 00] @ line 113

37936b7c-8928-490a-a30e-9bd99e86dbfb ‎14‎:‎24‎:‎38: error groovy.lang.MissingMethodException: No signature of method: script1445026156258679568561.parseDescriptionAsMap() is applicable for argument types: (java.lang.String) values: [read attr - raw: 5F460300080A000000201A, dni: 5F46, endpoint: 03, cluster: 0008, size: 0A, attrId: 0000, result: success, encoding: 20, value: 1a] @ line 113

37936b7c-8928-490a-a30e-9bd99e86dbfb ‎14‎:‎24‎:‎38: error groovy.lang.MissingMethodException: No signature of method: script1445026156258679568561.parseDescriptionAsMap() is applicable for argument types: (java.lang.String) values: [read attr - raw: 5F460303000A0000002000, dni: 5F46, endpoint: 03, cluster: 0300, size: 0A, attrId: 0000, result: success, encoding: 20, value: 00] @ line 113

37936b7c-8928-490a-a30e-9bd99e86dbfb ‎14‎:‎24‎:‎38: error groovy.lang.MissingMethodException: No signature of method: script1445026156258679568561.parseDescriptionAsMap() is applicable for argument types: (java.lang.String) values: [read attr - raw: 5F460300080A000000201A, dni: 5F46, endpoint: 03, cluster: 0008, size: 0A, attrId: 0000, result: success, encoding: 20, value: 1a] @ line 113

37936b7c-8928-490a-a30e-9bd99e86dbfb ‎14‎:‎24‎:‎37: debug Parse returned Living Room Lamp 2 switch is off

Line 113 code is:

def descMap = parseDescriptionAsMap(description)

Many thanks,
Guy

Sorry, I had messed up a couple of things, including the command to write the on/off transition. Try this code. Should fix the errors and correctly send the on/off transition time attribute to see if it’s supported.

If it isn’t supported, you should get a line towards the end that looks like this where the 86 means it’s unsupported:

9:50:20 AM: debug catchall: 0104 0008 0B 01 0100 00 D5B3 00 00 0000 04 01 861000

/**
 *  Copyright 2015 SmartThings
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License. You may obtain a copy of the License at:
 *
 *      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.
 *
 */
/* OSRAM Lightify RGBW Bulb

Capabilities:
  Actuator
  Color Control
  Configuration
  Polling
  Refresh
  Sensor
  Switch
  Switch Level
  
Custom Commands:
  setAdjustedColor
    
*/

metadata {
	definition (name: "OSRAM Lightify Zigbee RGBW Bulb", namespace: "guy_mayhew", author: "Guy Mayhew") {
		capability "Switch Level"
		capability "Actuator"
		capability "Color Control"
		capability "Switch"
		capability "Configuration"
		capability "Polling"
		capability "Refresh"
		capability "Sensor"

		command "setAdjustedColor"

		fingerprint profileId: "C05E", inClusters: "0000,0003,0004,0005,0006,0008,0300,1000", outClusters: "0019"
	}

	// simulator metadata
	simulator {
		// status messages
		status "on": "on/off: 1"
		status "off": "on/off: 0"

		// reply messages
		reply "zcl on-off on": "on/off: 1"
		reply "zcl on-off off": "on/off: 0"
	}

	// UI tile definitions
	tiles {
		standardTile("switch", "device.switch", width: 1, height: 1, canChangeIcon: true) {
			state "on", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#79b821", nextState:"turningOff"
			state "off", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn"
			state "turningOn", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#79b821", nextState:"turningOff"
			state "turningOff", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn"
		}
		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:"setAdjustedColor"
		}
		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") {
			state "level", label: 'Level ${currentValue}%'
		}
		controlTile("saturationSliderControl", "device.saturation", "slider", height: 1, width: 2, inactiveLabel: false) {
			state "saturation", action:"color control.setSaturation"
		}
		valueTile("saturation", "device.saturation", inactiveLabel: false, decoration: "flat") {
			state "saturation", label: 'Sat ${currentValue}    '
		}
		controlTile("hueSliderControl", "device.hue", "slider", height: 1, width: 2, inactiveLabel: false) {
			state "hue", action:"color control.setHue"
		}

		main(["switch"])
		details(["switch", "levelSliderControl", "rgbSelector", "refresh"])
	}
}

// Parse incoming device messages to generate events
def parse(String description) {
    log.debug description
    if (description?.startsWith("catchall:")) {
        if(description?.endsWith("0100") ||description?.endsWith("1001") || description?.matches("on/off\\s*:\\s*1"))
        {
            def result = createEvent(name: "switch", value: "on")
            log.debug "Parse returned ${result?.descriptionText}"
            return result
        }
        else if(description?.endsWith("0000") || description?.endsWith("1000") || description?.matches("on/off\\s*:\\s*0"))
        {
            if(!(description?.startsWith("catchall: 0104 0300"))){
                def result = createEvent(name: "switch", value: "off")
                log.debug "Parse returned ${result?.descriptionText}"
                return result
            }
        }
    }
    else if (description?.startsWith("read attr -")) {
        def descMap = parseDescriptionAsMap(description)
        log.trace "descMap : $descMap"

        if (descMap.cluster == "0300") {
            if(descMap.attrId == "0000"){  //Hue Attribute
                def hueValue = Math.round(convertHexToInt(descMap.value) / 255 * 360)
                log.debug "Hue value returned is $hueValue"
                sendEvent(name: "hue", value: hueValue, displayed:false)
            }
            else if(descMap.attrId == "0001"){ //Saturation Attribute
                def saturationValue = Math.round(convertHexToInt(descMap.value) / 255 * 100)
                log.debug "Saturation from refresh is $saturationValue"
                sendEvent(name: "saturation", value: saturationValue, displayed:false)
            }
        }
        else if(descMap.cluster == "0008"){
            def dimmerValue = Math.round(convertHexToInt(descMap.value) * 100 / 255)
            log.debug "dimmer value is $dimmerValue"
            sendEvent(name: "level", value: dimmerValue)
        }
    }
    else {
        def name = description?.startsWith("on/off: ") ? "switch" : null
        def value = name == "switch" ? (description?.endsWith(" 1") ? "on" : "off") : null
        def result = createEvent(name: name, value: value)
        log.debug "Parse returned ${result?.descriptionText}"
        return result
    }


}

def parseDescriptionAsMap(description) {
    (description - "read attr - ").split(",").inject([:]) { map, param ->
        def nameAndValue = param.split(":")
        map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
    }
}
def on() {
	// just assume it works for now
	log.debug "on()"
	sendEvent(name: "switch", value: "on")
	"st cmd 0x${device.deviceNetworkId} ${endpointId} 6 1 {}"
    sendEvent(name: "level", value: 10)
}

def off() {
	// just assume it works for now
	log.debug "off()"
	sendEvent(name: "switch", value: "off")
	"st cmd 0x${device.deviceNetworkId} ${endpointId} 6 0 {}"
}

def setHue(value) {
	def max = 0xfe
	log.trace "setHue($value)"
	sendEvent(name: "hue", value: value)
	def scaledValue = Math.round(value * max / 100.0)
	def cmd = "st cmd 0x${device.deviceNetworkId} ${endpointId} 0x300 0x00 {${hex(scaledValue)} 00 2000}"
	//log.info cmd
	cmd
}

def setAdjustedColor(value) {
	log.debug "setAdjustedColor: ${value}"
	def adjusted = value + [:]
	adjusted.hue = adjustOutgoingHue(value.hue)
	adjusted.level = null // needed because color picker always sends 100
	setColor(adjusted)
}

def setColor(value){
	log.trace "setColor($value)"
	def max = 0xfe

	sendEvent(name: "hue", value: value.hue)
	sendEvent(name: "saturation", value: value.saturation)
	def scaledHueValue = Math.round(value.hue * max / 100.0)
	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} ${endpointId} 6 1 {}"
		cmd << "delay 150"
	}

	cmd << "st cmd 0x${device.deviceNetworkId} ${endpointId} 0x300 0x00 {${hex(scaledHueValue)} 00 1500}"
	cmd << "delay 150"
	cmd << "st cmd 0x${device.deviceNetworkId} ${endpointId} 0x300 0x03 {${hex(scaledSatValue)} 1500}"

	if (value.level != null) {
		cmd << "delay 150"
		cmd.addAll(setLevel(value.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} ${endpointId} 0x300 0x03 {${hex(scaledValue)} 1500 }"
	//log.info cmd
	cmd
}

def configure() {
    state.levelValue = 100
    log.debug "Configuring Reporting and Bindings."
    def configCmds = [

            //Switch Reporting
            "zcl global send-me-a-report 6 0 0x10 0 3600 {01}", "delay 500",
            "send 0x${device.deviceNetworkId} ${endpointId} 1", "delay 1000",

            //Level Control Reporting
            "zcl global send-me-a-report 8 0 0x20 5 3600 {0010}", "delay 200",
            "send 0x${device.deviceNetworkId} ${endpointId} 1", "delay 1500",

            "zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 6 {${device.zigbeeId}} {}", "delay 1000",
            "zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 8 {${device.zigbeeId}} {}", "delay 500",
            "zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 0x0300 {${device.zigbeeId}} {}", "delay 500"
    ]
    return configCmds + refresh() // send refresh cmds as part of config
}

def refresh() {
 	[	
            "st rattr 0x${device.deviceNetworkId} ${endpointId} 6 0", "delay 500",
            "st rattr 0x${device.deviceNetworkId} ${endpointId} 8 0", "delay 500",
            "st rattr 0x${device.deviceNetworkId} ${endpointId} 0x0300 0", "delay 500",
            "st rattr 0x${device.deviceNetworkId} ${endpointId} 0x0300 1", "delay 500",
            "st wattr 0x${device.deviceNetworkId} ${endpointId} 8 0x10 0x21 {0015}"
        ]
}

def poll(){
	log.debug "Poll is calling refresh"
	refresh()
}

def zigbeeSetLevel(level) {
    "st cmd 0x${device.deviceNetworkId} ${endpointId} 8 4 {${level} 1500}"
}

def setLevel(value) {
	state.levelValue = (value==null) ? 100 : value
    log.trace "setLevel($value)"
    def cmds = []

    if (value == 0) {
        sendEvent(name: "switch", value: "off")
        cmds << zigbeeOff()
    }
    else if (device.latestValue("switch") == "off") {
        sendEvent(name: "switch", value: "on")
    }

    sendEvent(name: "level", value: state.levelValue)
    def level = hex(state.levelValue * 255 / 100)
    cmds << zigbeeSetLevel(level)

    //log.debug cmds
    cmds
}

private getEndpointId() {
	new BigInteger(device.endpointId, 16).toString()
}

private hex(value, width=2) {
	def s = new BigInteger(Math.round(value).toString()).toString(16)
	while (s.size() < width) {
		s = "0" + s
	}
	s
}

private Integer convertHexToInt(hex) {
    Integer.parseInt(hex,16)
}

private adjustOutgoingHue(percent) {
	def adjusted = percent
	if (percent > 31) {
		if (percent < 63.0) {
			adjusted = percent + (7 * (percent -30 ) / 32)
		}
		else if (percent < 73.0) {
			adjusted = 69 + (5 * (percent - 62) / 10)
		}
		else {
			adjusted = percent + (2 * (100 - percent) / 28)
		}
	}
	log.info "percent: $percent, adjusted: $adjusted"
	adjusted
}

Hey Scott,

Good job, all sorted :smiley:

Log as below:

37936b7c-8928-490a-a30e-9bd99e86dbfb ‎15‎:‎02‎:‎00: debug Saturation from refresh is 0

37936b7c-8928-490a-a30e-9bd99e86dbfb ‎15‎:‎02‎:‎00: trace descMap : [raw:5F460303000A0100002000, dni:5F46, endpoint:03, cluster:0300, size:0A, attrId:0001, result:success, encoding:20, value:00]

37936b7c-8928-490a-a30e-9bd99e86dbfb ‎15‎:‎02‎:‎00: debug read attr - raw: 5F460303000A0100002000, dni: 5F46, endpoint: 03, cluster: 0300, size: 0A, attrId: 0001, result: success, encoding: 20, value: 00

37936b7c-8928-490a-a30e-9bd99e86dbfb ‎15‎:‎01‎:‎59: debug Hue value returned is 0

37936b7c-8928-490a-a30e-9bd99e86dbfb ‎15‎:‎01‎:‎59: trace descMap : [raw:5F460303000A0000002000, dni:5F46, endpoint:03, cluster:0300, size:0A, attrId:0000, result:success, encoding:20, value:00]

37936b7c-8928-490a-a30e-9bd99e86dbfb ‎15‎:‎01‎:‎59: debug read attr - raw: 5F460303000A0000002000, dni: 5F46, endpoint: 03, cluster: 0300, size: 0A, attrId: 0000, result: success, encoding: 20, value: 00

37936b7c-8928-490a-a30e-9bd99e86dbfb ‎15‎:‎01‎:‎59: debug dimmer value is 71

37936b7c-8928-490a-a30e-9bd99e86dbfb ‎15‎:‎01‎:‎59: trace descMap : [raw:5F460300080A00000020B5, dni:5F46, endpoint:03, cluster:0008, size:0A, attrId:0000, result:success, encoding:20, value:b5]

37936b7c-8928-490a-a30e-9bd99e86dbfb ‎15‎:‎01‎:‎59: debug read attr - raw: 5F460300080A00000020B5, dni: 5F46, endpoint: 03, cluster: 0008, size: 0A, attrId: 0000, result: success, encoding: 20, value: b5

37936b7c-8928-490a-a30e-9bd99e86dbfb ‎15‎:‎01‎:‎58: debug Parse returned Living Room Lamp 2 switch is on

37936b7c-8928-490a-a30e-9bd99e86dbfb ‎15‎:‎01‎:‎58: debug catchall: 0104 0006 03 01 0040 00 5F46 00 00 0000 01 01 0000001001

Looks like a slightly different output to the tunable white.

Cheers
Guy

It’s weird though because I still don’t see the response message from the attempt to turn on the smooth on/off transition. Did you copy all of my code or just the parts that were giving us issues?

Success!! We now have dim to off (video below!), but the light still turns on abruptly (not sure if its a simple fix):

:smiley: :smiley:

1 Like

Hi Scott,

I copied all your code in, to avoid any issues - have posted it at the top of the thread.

Hi Deano,

Please see below photos of our lounge/diner lit by 3 OSRAM bulbs (full luminosity - warm white).

Thank you! This is just what I wanted! I think they will be bright enough.

I just got one of these yesterday. Thanks to you I didn’t have too much trouble setting it up! However, is the code in the OP the final one because I’ve noticed a few particularities with my bulb. For instance

  • when I press the “on” button, it doesn’t seem to switch on the bulb? I have to use the level slider or the rgb selector to switch it on.

  • selecting colour initially doesn’t seem to work, but starts working after a slight delay. However, once it gets going it seems to be fine after that.

  • Sometimes, the bulb changes colours that doesn’t necessarily correspond to the changes in the app e.g. selecting blue, the bulb goes red?

Since I installed the bulb in the hallway and fearing the missus complaining, I’ve switched over to the custom device type that @Sticks18 posted on github:

Lightify RGBW with Color Temp draft.groovy

@Sticks18’s version mostly works and I like the different white temperature presets. However, selecting colours doesn’t seem to work for me. I checked the IDE logs and whenever I try to change the colour, it doesn’t appear in the logs either?

HI @ma10, I think (and its just a theory for now) the lack of colour is due to the colour intensity slider being missing and by default its set to a very low value - i have not yet checked and dont have time today unfortunately but now the colour picker is a 2x2 grid in the current @Sticks18 code which i think it just removes the colour intensity slider (at least on android). The standard colour wheel is a 3x3 which obviously takes up a lot of the page. I think this will be an easy fix at a UI level. As for the other issues you are having - i have not experienced any of those with my 2 bulbs. Do you only have the one at this time?

Quick update as it seemed to simple a fix to not test and yes it was the height & default value on the RGB wheel causing the washed out colours.

i updated the height of the colour picker in the device code to the below (line 74):

	controlTile("rgbSelector", "device.color", "color", height: 3, width: 2, inactiveLabel: false) {

This then brings back the slider to adjust intensity…buuuuut it uses a lot of space. You can also use height 2, width 1 for a very narrow (but still usable) colour picker that brings back the intensity slider. Hope this helps :smile:

@Sticks18 - what would be the easiest way to set colour saturation at 100% as the default? this seems to be independent to the white saturation slider. I dont know about other users but for me im happy with 100% saturation when using colour and just dim it down if needed via the dimmer slide. This would allow the RGB wheel to remain 2x2.

Couple of things going on here.

@Guy_Mayhew looks like you added a sendEvent() command for level at the end of the on() method. Only the last command in a method gets sent, so if you want to keep that, you should move it above the “st cmd” line. SendEvent() lines will work anywhere. Also that sendEvent() command will only tell ST that the level is 10. It won’t actually change the level.

I’m not sure why you’re seeing color issues with the code in the first post here. Nothing looks out of the ordinary to me. @adgwill is probably right about the missing saturation slider in the UI of my code.

As long as you’re really sure that 100% saturation for color is ok, then you could add a line in the setColor() portion right above def scaledSatValue = evenHex(Math.round(value.satuartion * max / 100,0)) that sets the value.saturation to 100 like so: value.satuartion = 100

It’s unfortunate that the color picker works that way on the Android UI. The 2x2 version seems to work ok on iOS with full color control. We could try to convert it into the new multiAttribute tile format. Not sure if you’ve seen any examples of that.

I have not see the MultiAttribute tile but it sounds exciting @Sticks18 ! It is indeed a shame that the 2x2 doesn’t work the same…but when would cross functionality between Apple / Android apps ever make life easy :wink:

do you have any links to reference docs for ui tweaks / multiformat tile etc?

Here’s the doc: http://docs.smartthings.com/en/latest/device-type-developers-guide/tiles-metadata.html

It basically makes the top two rows a combined main tile that can have multiple controls. The color selection becomes a little icon in the bottom right of that tile, which brings up the color picker. It can have a slider in an arc as part of it for either level or color temperature, and we can add text in the bottom left of the tile based on color mode, dim level, etc…

Here’s what I’ve been working on for my Hue bulb.

1 Like

@Sticks18 Wow that looks really nice! I like the fact it shows what colour the bulb is set too from the Things screen too. very helpful all round!

Should configure do anything at this time or is there anything planned in there for the Lightify bulbs?

This is meant for Hue bulbs that are directly connected. Most of it should work for the Osram, so I can probably port it over into the Osram specific device type for you.

Configure() in ST is mainly for protocol based settings when a device initially joins the network, which is why you don’t see it much in device screens. I put it in there because I was testing stuff, but it’ll be useless for you. I can replace it in the Osram version.

im not sure how much will need changing tbh - it seemed to work right as it is with my osram bulbs (unless you know there are some hue specific gremlins in there?) … it did keep switching to a blue colour on the UI without actually updating the bulb after a few minutes so maybe that is one :wink: … replacing the configure icon with default 2700k white would pretty much nail it as far as UI capabilities go :bulb:

The Osram can do things the Hue can’t, so I would want to make sure those are included.

You mean without you making any changes, it would think the bulb was blue? Changing the color and name of the main tile?

Ah okay - nice to know the Lightify bulbs are capable vs hue (especially given how cheap they are!).

Yes the UI updated to show Boston Blue after a few seconds even though no changes were made by me and the bulb was still the colour i had set it a few moments before.