RGBW Device driver

I am migrating over from ST and have 6 - e12 Zigbee bulbs I use in my outside sconces. They are RGBW bulbs made by CMAR on Amazon.

ZigBee E12 Smart Light Bulbs, CMARS LED Candle Bulb Compatible with Alexa, SmartThings Hub, Echo Plus (2nd), Dimmable and RGB Color Changing, 320Lm, Hub Required (3 Pack) https://a.co/d/jcd7vrE

They were working flawlessly off this custom driver in ST below. I cannot find this for Hubitat and have tried all the generic drivers in Hubitat and none are working. Any advice would be great! Thanks!

/**

  • Osram Lightify RGBW A19/BR30 US version (HA) DTH rev 01/19/2018
  • by gkl_sf
  • set default color/level code by ranga
  • To set default initial (power-on) color/level:
    • set your preferred color/level
    • wait for few seconds, then tap the Set Default tile
    • wait 3-5 minutes for the process to complete (do NOT switch off or change any settings during this time)
    • the main (on/off) tile will turn orange with "WAIT" status during this period; if it does not reset after 3-5 minutes, tap the refresh tile
    • after that, you can try switching power off and on to see if the new color/level is set correctly
    • may need to upgrade firmware (via ST OTA) for this to work
  • 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.
  • Parts copyright 2015 SmartThings

**/

metadata {
definition (name: "Osram RGBW", namespace: "gkl-sf", author: "gkl_sf") {

    capability "Actuator"
    capability "Color Control"
    capability "Color Temperature"
    capability "Configuration"
    capability "Polling"
    capability "Refresh"
    capability "Switch"
    capability "Switch Level"
    
    command "temp2700"
    command "temp3000"
    command "temp6500"
    
    command "setHue360", ["number"]

    command "pulseOn"
    command "pulseOff"
    
    command "blinkOn"
    command "blinkOff"
    
    command "loopOn"
    command "loopOff"
    
    command "setDefaultColor"
    
attribute "colorName", "string"
    attribute "colorMode", "string"    
    
    fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0300,0B04,FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY A19 RGBW", deviceJoinName: "Osram Lightify A19 RGBW"
    fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0300,0B04,FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY BR RGBW", deviceJoinName: "Osram Lightify LED BR30 RGBW"
}

preferences {            
   input(
         "switchTransition",
         "number",
         title: "Dim duration for On/Off",
         range: "0..10",
         description: "0-10 seconds",
         defaultValue: 2,
         required: false,
         displayDuringSetup: true
        )
   input(
         "levelTransition",
         "number",
         title: "Dim duration for level change",
         range: "0..10",
         description: "0-10 seconds",
         defaultValue: 4,
         required: false,
         displayDuringSetup: true
        )
   input(
         "colorTransition",
         "number",
         title: "Time to transition color",
         range: "0..10",
         description: "0-10 seconds",
         defaultValue: 2,
         required: false,
         displayDuringSetup: true
        )             
   input(
         "pulseDuration",
         "number",
         title: "Pulse dim up/down duration",
         range: "1..10",
         description: "1-10 seconds",
         defaultValue: 4,
         required: false,
         displayDuringSetup: true
        )
   input(
         "loopRate",
         "number",
         title: "Color loop rate in steps per second",
         range: "1..20",
         description: "range 1-25",
         defaultValue: 5,
         required: false,
         displayDuringSetup: true
        )           
}       

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:"off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
            attributeState "off", label:'${name}', action:"on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
            attributeState "turningOn", label:'Turning...', action:"on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
            attributeState "turningOff", label:'Turning...', action:"off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
            attributeState "wait", label:'Wait...', icon:"st.lights.philips.hue-single", backgroundColor:"#ffa81e"                
        }           
        tileAttribute ("device.level", key: "SLIDER_CONTROL") {
            attributeState "level", action:"setLevel"
        }
        tileAttribute ("device.color", key: "COLOR_CONTROL") {
            attributeState "color", action:"setColor"
        }
    }
                   
    controlTile("hueSliderControl", "device.hue", "slider", width: 4, height: 2, range: "(0..360)") {
        state "hue", action: "setHue360"
    }
    valueTile("hue", "device.hue", width: 2, height: 2) {
        state "hue", label:'Hue\n${currentValue}°',
        backgroundColors:[
                [value: 0, color: "ff0000"],
                [value: 8, color: "ff3800"],
                [value: 21, color: "ff6700"],
                [value: 27, color: "ffbf00"],
                [value: 41, color: "ffff00"],
                [value: 55, color: "dfff00"],
                [value: 95, color: "00ff00"],
                [value: 125, color: "00ff6f"],
                [value: 137, color: "00ffff"],
                [value: 165, color: "007fff"],
                [value: 206, color: "0000ff"],
                [value: 246, color: "8f00ff"],
                [value: 276, color: "ff00ff"],
                [value: 316, color: "ff007f"],
                [value: 341, color: "ffc0cb"],
                [value: 351, color: "dc143c"],
                [value: 357, color: "ff0000"],
                [value: 360, color: "ff0000"]                    
	    ]               
    }

    valueTile("saturation", "device.saturation", width: 3, height: 1) {
        state "saturation", label:'Saturation ${currentValue}%'
    }        
    valueTile("colorName", "device.colorName", width: 3, height: 1) {
        state "colorName", label:'${currentValue}'  
    }         
    
    
    controlTile("colorTempSliderControl", "device.colorTemperature", "slider", width: 4, height: 2, range:"(2700..6500)") {
        state "colorTemp", action:"color temperature.setColorTemperature"
    }
    valueTile("colorTemp", "device.colorTemperature", width: 2, height: 2) {
        state "colorTemp", label: 'Temp\n${currentValue} K'           
    }

    standardTile("temp2700", "device.temp2700", decoration: "flat", width: 2, height: 2) {
        state "default", label:'Incandescent', action: "temp2700", icon: "st.Lighting.light13"
    }        
    standardTile("temp3000", "device.temp3000", decoration: "flat", width: 2, height: 2) {
        state "default", label:'Halogen', action: "temp3000", icon: "st.lights.philips.hue-single"
    }
    standardTile("temp6500", "device.temp6500", decoration: "flat", width: 2, height: 2) {
        state "default", label:'Daylight', action: "temp6500", icon: "st.Weather.weather14"
    }         
            
    standardTile("pulse", "device.pulse", decoration: "flat", width: 2, height: 2) {
        state "off", label:'Pulse', action: "pulseOn", icon: "st.Lighting.light11", backgroundColor:"#ffffff"
        state "on", label:'Pulse', action: "pulseOff", icon: "st.Lighting.light11", backgroundColor:"#dcdcdc"
    }
    standardTile("blink", "device.blink", decoration: "flat", width: 2, height: 2) {
        state "off", label:'Blink', action: "blinkOn", icon: "st.Lighting.light11", backgroundColor:"#ffffff"
        state "on", label:'Blink', action: "blinkOff", icon: "st.Lighting.light11", backgroundColor:"#dcdcdc"
    }        
    standardTile("colorLoop", "device.colorLoop", decoration: "flat", width: 2, height: 2) {
        state "off", label:'Color Loop', action: "loopOn", icon: "st.Kids.kids2", backgroundColor:"#ffffff"
        state "on", label:'Color Loop', action: "loopOff", icon: "st.Kids.kids2", backgroundColor:"#dcdcdc"
    }
   
    standardTile("refresh", "device.refresh", decoration: "flat", width: 2, height: 2) {
        state "refresh", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
    }
    
    standardTile("configure", "device.configure", decoration: "flat", height: 2, width: 2) {
		state "configure", label:'', action:"configure", icon:"st.secondary.configure"
	}

    standardTile("defaultColor", "device.defaultColor", decoration: "flat", width: 2, height: 2) {
        state "default", label:'Set Default', action: "setDefaultColor", icon: "st.Lighting.light13"
    }        
    
    main(["switch"])
    details(["switch", "hueSliderControl", "hue", "saturation", "colorName", "colorTempSliderControl", "colorTemp", "temp2700", "temp3000", "temp6500", "pulse", "blink", "colorLoop", "refresh", "configure", "defaultColor"])
}

}

private getON_OFF_CLUSTER() { 6 }
private getLEVEL_CONTROL_CLUSTER() { 8 }
private getCOLOR_CONTROL_CLUSTER() { 0x0300 }

private getHUE_COMMAND() { 0 }
private getSATURATION_COMMAND() { 3 }
private getHUE_SATURATION_COMMAND() { 6 }

private getATTRIBUTE_HUE() { 0 }
private getATTRIBUTE_SATURATION() { 1 }
private getATTRIBUTE_COLOR_TEMPERATURE() { 7 }
private getATTRIBUTE_COLOR_MODE() { 8 }

private getDEFAULT_LEVEL_TRANSITION() {"2800"} //4 secs (little endian)
private getDEFAULT_COLOR_TRANSITION() {"1400"} //2 secs (little endian)
private getDEFAULT_PULSE_DURATION() {"2800"} //4 secs (little endian)
private getDEFAULT_LOOP_RATE() {"05"} //5 steps per sec

def parse(String description) {

def result = zigbee.getEvent(description)
def cmds = []

if (result) {
    cmds << createEvent(result)
    
    if (device.currentValue("pulse") == "on" && result.name == "level") {
        if (!state.pulseDuration) state.pulseDuration = DEFAULT_PULSE_DURATION
        if (result.value == 5) cmds << new physicalgraph.device.HubAction("st cmd 0x${device.deviceNetworkId} ${endpointId} 8 4 {fb ${state.pulseDuration}}")
        else if (result.value == 99) cmds << new physicalgraph.device.HubAction("st cmd 0x${device.deviceNetworkId} ${endpointId} 8 4 {0d ${state.pulseDuration}}")            
    }
    else if (result.name == "colorTemperature") {
        if (device.currentValue("colorMode") == "W") {
            def tempName = getTempName(result.value)
            cmds << createEvent(name: "colorName", value: tempName, displayed: false)
        }    
    }
}        
else {
    def zigbeeMap = zigbee.parseDescriptionAsMap(description)
    if (zigbeeMap?.clusterInt == COLOR_CONTROL_CLUSTER && device.currentValue("switch") == "on") {        
        if (zigbeeMap.attrInt == ATTRIBUTE_HUE) {
            def hueValue = Math.round(zigbee.convertHexToInt(zigbeeMap.value) / 254 * 360)
            def colorName = getColorName(hueValue)
            cmds << createEvent(name: "hue", value: hueValue, displayed: false)
            cmds << createEvent(name: "colorName", value: colorName, displayed: false)
        }            
        else if (zigbeeMap.attrInt == ATTRIBUTE_SATURATION) {
            def saturationValue = Math.round(zigbee.convertHexToInt(zigbeeMap.value) / 254 * 100)
            cmds << createEvent(name: "saturation", value: saturationValue, displayed: false)
        }
        else if (zigbeeMap.attrInt == ATTRIBUTE_COLOR_MODE) {
            if (zigbeeMap.value == "00") {
                cmds << createEvent(name: "colorMode", value: "RGB", displayed: false)
            }
            else if (zigbeeMap.value == "02") {
                cmds << createEvent(name: "colorMode", value: "W", displayed: false)
            }
        }               
    }
    else if (zigbeeMap?.clusterInt == 0x8021) {
        log.debug "*** received Configure Reporting response: ${zigbeeMap.data}"
    }
    else { log.debug "*** unparsed response: ${zigbeeMap}" }
}

return cmds

}

def updated() {

if (state.updatedTime) {
    if ((state.updatedTime + 5000) > now()) return null
}
state.updatedTime = now()

log.debug "--- Updated with: ${settings}"

String switchTransition
if (settings.switchTransition) {
    switchTransition = hex((settings.switchTransition * 10),4) //OnOffTransitionTime in 1/10th sec (big endian)
}
else {
    switchTransition = "0014" //2 seconds (big endian)
}    

if (settings.levelTransition) {
    state.levelTransition = swapEndianHex(hex((settings.levelTransition * 10),4))
}
else {
    state.levelTransition = "2800" //4 seconds
}    

if (settings.colorTransition) {
    state.colorTransition = swapEndianHex(hex((settings.colorTransition * 10),4))
}
else {
    state.colorTransition = "1400" //2 seconds
}

if (settings.pulseDuration) {
    state.pulseDuration = swapEndianHex(hex((settings.pulseDuration * 10),4))
}
else {
    state.pulseDuration = "2800" //4 seconds
}    

if (settings.loopRate) {
    state.loopRate = hex((settings.loopRate),2)
}
else {
    state.loopRate = "05"
}

return new physicalgraph.device.HubAction("st wattr 0x${device.deviceNetworkId} ${endpointId} 8 0x0010 0x21 {${switchTransition}}")  // on/off dim duration  

}

def refresh() {
[
"st rattr 0x${device.deviceNetworkId} ${endpointId} 6 0", "delay 500", //on-off
"st rattr 0x${device.deviceNetworkId} ${endpointId} 8 0", "delay 500", //level
"st rattr 0x${device.deviceNetworkId} ${endpointId} 0x0300 0", "delay 500", //hue
"st rattr 0x${device.deviceNetworkId} ${endpointId} 0x0300 1", "delay 500", //sat
"st rattr 0x${device.deviceNetworkId} ${endpointId} 0x0300 7", "delay 500", //color temp
"st rattr 0x${device.deviceNetworkId} ${endpointId} 0x0300 8" //color mode
]
}

def configure() {
zigbee.onOffConfig() +
zigbee.levelConfig() +
zigbee.colorTemperatureConfig() +
[
//hue
"zcl global send-me-a-report 0x0300 0 0x20 1 3600 {01}", "delay 500",
"send 0x${device.deviceNetworkId} ${endpointId} 1", "delay 1000",

    //saturation
    "zcl global send-me-a-report 0x0300 1 0x20 1 3600 {01}", "delay 500",
    "send 0x${device.deviceNetworkId} ${endpointId} 1", "delay 1500",

    //color mode
    "zcl global send-me-a-report 0x0300 8 0x30 1 3600 {}", "delay 500",
    "send 0x${device.deviceNetworkId} ${endpointId} 1", "delay 1500",        
    
    "zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 0x0300 {${device.zigbeeId}} {}", "delay 500"
] +
zigbee.writeAttribute(LEVEL_CONTROL_CLUSTER, 0x0010, 0x21, "0014") //OnOffTransitionTime in 1/10th sec, set to 2 sec, note big endian

}

def on() {
zigbee.on()
}

def off() {
pulseOff()
zigbee.off()
}

def setColorTemperature(value) {
def cmds = []
cmds << sendEvent(name: "colorMode", value: "W", displayed: false)
cmds << zigbee.setColorTemperature(value)
cmds
}

def setLevel(value, duration = settings.levelTransition) { //duration in seconds
if (value == 0) off()
else zigbee.setLevel(value,duration)
}

def setColor(value) {
if (!state.colorTransition) state.colorTransition = DEFAULT_COLOR_TRANSITION
def cmds = []
if (device.currentValue("switch") == "off") {
cmds << zigbee.on()
cmds << "delay 500"
}
def scaledHueValue = zigbee.convertToHexString(Math.round(value.hue * 0xfe / 100), 2)
def scaledSatValue = zigbee.convertToHexString(Math.round(value.saturation * 0xfe / 100), 2)
cmds << sendEvent(name: "saturation", value: value.saturation, displayed: false)
cmds << sendEvent(name: "colorMode", value: "RGB", displayed: false)
cmds << zigbee.command(COLOR_CONTROL_CLUSTER, HUE_SATURATION_COMMAND, scaledHueValue, scaledSatValue, state.colorTransition)
cmds
}

def setHue(value) { //accepts hue values 0-100, doesn't change saturation
def cmds = []
if (!state.colorTransition) state.colorTransition = DEFAULT_COLOR_TRANSITION
def scaledHueValue = zigbee.convertToHexString(Math.round(value * 0xfe / 100), 2)
cmds << sendEvent(name: "colorMode", value: "RGB", displayed: false)
cmds << zigbee.command(COLOR_CONTROL_CLUSTER, HUE_COMMAND, scaledHueValue, "00", state.colorTransition)
cmds
}

def setHue360(value) { //accepts hue values 0-360 and sets saturation to 100%
if (!state.colorTransition) state.colorTransition = DEFAULT_COLOR_TRANSITION
def scaledHueValue = zigbee.convertToHexString(Math.round(value * 0xfe / 360), 2)
def cmds = []
cmds << sendEvent(name: "saturation", value: 100, displayed: false)
cmds << sendEvent(name: "colorMode", value: "RGB", displayed: false)
cmds << zigbee.command(COLOR_CONTROL_CLUSTER, HUE_SATURATION_COMMAND, scaledHueValue, "fe", state.colorTransition)
cmds
}

def setSaturation(value) {
if (!state.colorTransition) state.colorTransition = DEFAULT_COLOR_TRANSITION
def scaledSatValue = zigbee.convertToHexString(Math.round(value * 0xfe / 100), 2)
def cmds = []
cmds << sendEvent(name: "saturation", value: value, displayed: false)
cmds << sendEvent(name: "colorMode", value: "RGB", displayed: false)
cmds << zigbee.command(COLOR_CONTROL_CLUSTER, SATURATION_COMMAND, scaledSatValue, state.colorTransition)
cmds
}

def loopOn() {
if (!state.loopRate) state.loopRate = DEFAULT_LOOP_RATE
def cmds = []
cmds << zigbee.command(COLOR_CONTROL_CLUSTER, SATURATION_COMMAND, "fe", "1400") //set saturation to 100% over 2 sec
cmds << sendEvent(name: "colorLoop", value: "on", descriptionText: "Color Loop started", displayed: true, isChange: true)
cmds << sendEvent(name: "colorMode", value: "RGB", displayed: false)
cmds << zigbee.command(COLOR_CONTROL_CLUSTER, 0x01, "01", state.loopRate) //move hue command is 0x01, up is "01", rate is steps per sec
cmds
}

def loopOff() {
def cmds = []
cmds << sendEvent(name: "colorLoop", value: "off", descriptionText: "Color Loop stopped", displayed: true, isChange: true)
cmds << zigbee.command(COLOR_CONTROL_CLUSTER, 0x01, "00") //move hue command is 0x01, stop is "00"
cmds
}

def pulseOn() {
def cmds = []
cmds << sendEvent(name: "pulse", value: "on", descriptionText: "Pulse mode set to On", displayed: true, isChange: true)
cmds << zigbee.setLevel(95,0) //in case the level is already 99, since level needs to change to initiate the pulse cycling
cmds << "delay 100"
cmds << zigbee.setLevel(99,0)
cmds
}

def pulseOff() {
sendEvent(name: "pulse", value: "off", descriptionText: "Pulse mode set to Off", displayed: true, isChange: true)
}

def blinkOn() {
def cmds = []
cmds << sendEvent(name: "blink", value: "on", descriptionText: "Blink mode set to On", displayed: true, isChange: true)
cmds << zigbee.command(3, 0x00, "100e") //payload is time in secs to continue blinking (set to 3600 secs)
cmds
}

def blinkOff() {
def cmds = []
cmds << sendEvent(name: "blink", value: "off", descriptionText: "Blink mode set to Off", displayed: true, isChange: true)
cmds << zigbee.command(3, 0x00, "0000")
cmds
}

def temp2700() {
setColorTemperature(2700)
}

def temp3000() {
setColorTemperature(3000)
}

def temp6500() {
setColorTemperature(6500)
}

def setDefaultColor() {
log.info "Setting default color"
def cmds =
[
sendEvent(name: "switch", value: "wait", descriptionText: "Setting default color/level", displayed: true, isChange: true),
"st cmd 0x${device.deviceNetworkId} ${endpointId} 0xFC0F 0x01 {}",
"delay 180000"
]
return cmds + refresh()
}

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

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

private String swapEndianHex(String hex) {
reverseArray(hex.decodeHex()).encodeHex()
}

private byte[] reverseArray(byte[] array) {
byte tmp;
tmp = array[1];
array[1] = array[0];
array[0] = tmp;
return array
}

private getTempName(value) {
String tempName
if (value < 3000) tempName = "Incandescent"
else if (value < 3300) tempName = "Halogen"
else if (value < 5000) tempName = "Cool White"
else if (value <= 6500) tempName = "Daylight"
else tempName = "White Light Mode"
return tempName
}

//color name for saturation 100%
private getColorName(hueValue) {
String colorName
if (hueValue >= 0 && hueValue <= 7) colorName = "Red"
else if (hueValue >= 8 && hueValue <= 20) colorName = "Red-Orange"
else if (hueValue >= 21 && hueValue <= 26) colorName = "Orange"
else if (hueValue >= 27 && hueValue <= 40) colorName = "Orange-Yellow"
else if (hueValue >= 41 && hueValue <= 54) colorName = "Yellow"
else if (hueValue >= 55 && hueValue <= 94) colorName = "Yellow-Green"
else if (hueValue >= 95 && hueValue <= 124) colorName = "Green"
else if (hueValue >= 125 && hueValue <= 136) colorName = "Green-Cyan"
else if (hueValue >= 137 && hueValue <= 164) colorName = "Cyan"
else if (hueValue >= 165 && hueValue <= 205) colorName = "Cyan-Blue"
else if (hueValue >= 206 && hueValue <= 243) colorName = "Blue"
else if (hueValue >= 244 && hueValue <= 272) colorName = "Blue-Magenta"
else if (hueValue >= 273 && hueValue <= 320) colorName = "Magenta"
else if (hueValue >= 321 && hueValue <= 349) colorName = "Magenta-Pink"
else if (hueValue == 350) colorName = "Pink"
else if (hueValue >= 351 && hueValue <= 356) colorName = "Pink-Red"
else if (hueValue >= 357 && hueValue <= 360) colorName = "Red"
else colorName = "Color Mode"
return colorName
}

My recollection is that I got those bulbs working with the built-in Advanced Zigbee RGBW bulb driver. Just be sure to press “Configure” on the device page when you change to a driver.

You could also port the Samsung driver that works to Hubitat. It could be as simple as replacing all instances of physicalgraph with hubitat.

Thank you for responding. I tried the Advanced Zigbee RGB and hit reconfigure with no luck. I may try and fix the code but have no experience with code.

I knew I had tested these two years ago. Here is that post, along with both the relevant links:

1 Like

Thanks! I think I got them working from your post info!

2 Likes

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.