Osram/Sylvania Lightify (it works)

DTH == Device Type Handler. I think most people here refer to them that way rather than as “DH”. I dunno. Maybe people prefer three letter acronyms over two letter acronyms.

With respect to your question, I’m using the “official” DTH’s for the physical devices, and custom ones for the virtual devices that I use to group those.

this is the ZLL RGBW Bulb which my Osram chose now …


the others still use the Zigbee Hue Bulb, I have just re-assigned one and it works perfectly - I got the RGB lights though - no whites but maybe worth a try mate …

go to the bulb in the IDE / my devices / select the bulb / edit and change the type to your desired profile …

the Kelvin picker is actually quite nice …

Oki, great :slight_smile:
So can you help me on my way to use such a virtual bulb to groups them?
I assume I go into IDE, create a new Device Handler “From code” using the codes we have refered to.
What exactly do I do then to create the virtual bulb, and then how to I “attach” the physical bulbs to that virtual bulb?

I’m missing the connection on that so hope you can help me out.

what do you want a virtual bulb for? not sure what you want to achieve but surely could help you getting there …

go to the bulb in the IDE / my devices / select the bulb / edit and change the type to your desired profile …

^^^^ with this you can edit the bulb via the app as usual

As I said, I’m using Rule Machine (deprecated, unfortunately, but it still works; if you don’t already have it you’re potentially out of luck). The rules that I have created trigger when the virtual device is changed (turned on/off or dimmed), then the “true” and “false” conditions of the rule control the physical bulbs that I want to group together. So, for example, I have a virtual dimmer switch called “Front Yard Lights”, and when it turns on, the five physical lights on the front side of the house turn on. Same when it turns off.

I will likely be switching over to the “new hotness” rule engine called CoRE over time. Right now it does not have the capability to create the kind of rule that I want, though. The developer of that App, which while complex beyond the ken of many looks as if it will be extremely powerful, promises that the feature I need will be supported soon.

You can also do this sort of thing in a much clunkier way with, for example, Smart Lighting.

All my bulbs are in groups. I dont have any that are just a “single”.

So on my smarttiles dashboard, which I assume we will control most or our lights and suff from, and want a control for lights in “kitchen”. on/off and dimmer.

I thought to do this I needed to create this virtuel bulb, attach the others to that virtual bulb, and then I could add the virtual bulb to the dashboard.

But I have also looked at this today, which perhaps can help do the same thing. I haven’t had time to test.

So what is the best way to achieve a control button on a smarttiles dashboard which controls multiple bulbs?

@ady624 just tried it tonight. Using my minimote i used a piston to turn on, set color, set %. The named colors do not match anything remotely what the gardenspots produce :). Like any of the soft/cool/warm whites are like green red or something. So I’m not sure what lightify is needing for color info. Like the color wheel picker in the thing view, i pick something in the middle of green with the name of lime…and its very white. Its like the color wheel doesnt match anything logical. So in the piston I tried setting ‘lime’ i think it came out red. I tried various whites or a few other colors, and it wasnt anything logical for color. I dont think this is core’s fault cause smartlighting does the same. I think its the DH’s color codes. The lightify led strips has the new DH with the square color picker and its colors more closely match the colors. But still yet in smart lighting it doesnt know how to do rgbw 2700k, nor if i say green does it know green. If i had to guess these color pickers are like only smart enough to know Hue and not lightify.

Where can I look at the DTH code for the garden spots? ST has a problem with colors where they use the hue as a percent (0-100) while the world knows hue as an angle (0-360). The setColor and setHue commands within the DTH have to multiply the hue by 3.6 because all apps are supposed to send a percentage for the hue. If you know a bit of programming, try “patching” the DTH. Or I can help. See if that gets the colors right…

Gotcha - well what you want to do is create a virtual switch use whatever app to link the bulbs to the switch and add the switch to your dashboard

On IDE / devices / create a new device type simulates switch and give it a bogus network id eg FFFFFFFFF0

You then have the seitch available in the app so you can assign it to the bulbs you want and trigger them from the dashboard

Sorry to ask a stupid question, but what code is everyone using for the gardenspots? I just received mine and plan on installing them this weekend. I am currently testing them out and the lightify app sucks and want to put it into ST and the code for that is even worse. Does anyone know of any custom code for the gardenspots? I want the colors to change in a scene every night from dusk to about midnight. thank you for the help.

@ady624 Im using the default device handler for what they pair as in the app. By default ST does see them as lightify garden spots. Is that code up on their github? I never looked. I have almost all the rgb/w stuff from lightify if you want help with color on them. Have the rgbw led strips, the rgbw can light replecement thing, and the rgb garden spots. I dont have basic bulb but id assume they work as the same. I know enough coding to be dangerous (MIS degree and work as a linux sysadmin so i can hack existing stuff pretty well). Id just need a little help getting going. I dont know much on the backend here but enough to be dangerous. can follow instructions well tho :slight_smile:

@tycalmc Im using the default stuff within ST app right now. They pair up as osram rgb gardenspots without much trouble. Then right now Im using the default smart lighting app to do basic turn on/off stuff. If you set the color by hand, and only do ‘turn on’ not ‘set color’ in the automation it will turn back on as the last color it turned off as. I havent gotten fancy with like a disco mode or anything. setting color is iffy cause what the app says is red may not be red…

One of the repos that I’m connected to seems to be a clone of the SmartThings repo. This is the code for the GardenSpots DTH:

/*
Osram Lightify Gardenspot Mini RGB

Osram bulbs have a firmware issue causing it to forget its dimming level when turned off (via commands). Handling
that issue by using state variables

*/

metadata {
definition (name: “OSRAM LIGHTIFY Gardenspot mini RGB”, namespace: “smartthings”, author: “SmartThings”) {

    capability "Color Temperature"
    capability "Actuator"
    capability "Switch"
    capability "Switch Level"
    capability "Configuration"
    capability "Polling"
    capability "Refresh"
    capability "Sensor"
    capability "Color Control"
    attribute "colorName", "string"
    command "setAdjustedColor"
    fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0300,0B04,FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "Gardenspot RGB"
  fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0300,0B04,FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY Gardenspot RGB"
}
// 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: 2, height: 2, canChangeIcon: true) {
        state "on", label: '${name}', action: "switch.off", icon: "st.switches.light.on", backgroundColor: "#79b821"
        state "off", label: '${name}', action: "switch.on", icon: "st.switches.light.off", backgroundColor: "#ffffff"
    }
    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"
    }
    valueTile("colorName", "device.colorName", inactiveLabel: false, decoration: "flat") {
        state "colorName", label: '${currentValue}'
    }
    controlTile("levelSliderControl", "device.level", "slider", height: 1, width: 2, inactiveLabel: false, range:"(0..100)") {
        state "level", action:"switch level.setLevel"
    }
    valueTile("level", "device.level", inactiveLabel: false, decoration: "flat") {
        state "level", label: 'Level ${currentValue}%'
    }
    main(["switch"])
    details(["switch", "refresh", "colorName", "levelSliderControl", "level", "rgbSelector"])
}

}

// Parse incoming device messages to generate events
def parse(String description) {
//log.info “description is $description”
if (description?.startsWith(“catchall:”)) {
if(description?.endsWith(“0100”) ||description?.endsWith(“1001”) || description?.matches(“on/off\s*:\s1"))
{
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 on() {
log.debug “on()”
sendEvent(name: “switch”, value: “on”)
setLevel(state?.levelValue)
}

def zigbeeOff() {
“st cmd 0x${device.deviceNetworkId} ${endpointId} 6 0 {}”
}

def off() {
log.debug “off()”
sendEvent(name: “switch”, value: “off”)
zigbeeOff()
}

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”
]

}

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 parseDescriptionAsMap(description) {
(description - “read attr - “).split(”,”).inject([:]) { map, param →
def nameAndValue = param.split(“:”)
map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
}
}

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

def zigbeeSetLevel(level) {
“st cmd 0x${device.deviceNetworkId} ${endpointId} 8 4 {${level} 0000}”
}

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

}

//input Hue Integer values; returns color name for saturation 100%
private getColorName(hueValue){
if(hueValue>360 || hueValue<0)
return

hueValue = Math.round(hueValue / 100 * 360)
log.debug "hue value is $hueValue"
def colorName = "Color Mode"
if(hueValue>=0 && hueValue <= 4){
    colorName = "Red"
}
else if (hueValue>=5 && hueValue <=21 ){
    colorName = "Brick Red"
}
else if (hueValue>=22 && hueValue <=30 ){
    colorName = "Safety Orange"
}
else if (hueValue>=31 && hueValue <=40 ){
    colorName = "Dark Orange"
}
else if (hueValue>=41 && hueValue <=49 ){
    colorName = "Amber"
}
else if (hueValue>=50 && hueValue <=56 ){
    colorName = "Gold"
}
else if (hueValue>=57 && hueValue <=65 ){
    colorName = "Yellow"
}
else if (hueValue>=66 && hueValue <=83 ){
    colorName = "Electric Lime"
}
else if (hueValue>=84 && hueValue <=93 ){
    colorName = "Lawn Green"
}
else if (hueValue>=94 && hueValue <=112 ){
    colorName = "Bright Green"
}
else if (hueValue>=113 && hueValue <=135 ){
    colorName = "Lime"
}
else if (hueValue>=136 && hueValue <=166 ){
    colorName = "Spring Green"
}
else if (hueValue>=167 && hueValue <=171 ){
    colorName = "Turquoise"
}
else if (hueValue>=172 && hueValue <=187 ){
    colorName = "Aqua"
}
else if (hueValue>=188 && hueValue <=203 ){
    colorName = "Sky Blue"
}
else if (hueValue>=204 && hueValue <=217 ){
    colorName = "Dodger Blue"
}
else if (hueValue>=218 && hueValue <=223 ){
    colorName = "Navy Blue"
}
else if (hueValue>=224 && hueValue <=251 ){
    colorName = "Blue"
}
else if (hueValue>=252 && hueValue <=256 ){
    colorName = "Han Purple"
}
else if (hueValue>=257 && hueValue <=274 ){
    colorName = "Electric Indigo"
}
else if (hueValue>=275 && hueValue <=289 ){
    colorName = "Electric Purple"
}
else if (hueValue>=290 && hueValue <=300 ){
    colorName = "Orchid Purple"
}
else if (hueValue>=301 && hueValue <=315 ){
    colorName = "Magenta"
}
else if (hueValue>=316 && hueValue <=326 ){
    colorName = "Hot Pink"
}
else if (hueValue>=327 && hueValue <=335 ){
    colorName = "Deep Pink"
}
else if (hueValue>=336 && hueValue <=339 ){
    colorName = "Raspberry"
}
else if (hueValue>=340 && hueValue <=352 ){
    colorName = "Crimson"
}
else if (hueValue>=353 && hueValue <=360 ){
    colorName = "Red"
}
colorName

}

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 evenHex(value){
def s = new BigInteger(Math.round(value).toString()).toString(16)
while (s.size() % 2 != 0) {
s = “0” + s
}
s
}

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

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

//Need to reverse array of size 2
private byte reverseArray(byte array) {
byte tmp;
tmp = array[1];
array[1] = array[0];
array[0] = tmp;
return array
}

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

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

if (value.hex) { sendEvent(name: "color", value: value.hex, displayed:false)}
def colorName = getColorName(value.hue)
sendEvent(name: "colorName", value: colorName)
log.debug "color name is : $colorName"
sendEvent(name: "hue", value: value.hue, displayed:false)
sendEvent(name: "saturation", value: value.saturation, displayed:false)
def scaledHueValue = evenHex(Math.round(value.hue * max / 100.0))
def scaledSatValue = evenHex(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 {${scaledHueValue} 00 0000}"
cmd << "delay 150"
cmd << "st cmd 0x${device.deviceNetworkId} ${endpointId} 0x300 0x03 {${scaledSatValue} 0000}"

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

if (value.switch == "off") {
    cmd << "delay 150"
    cmd << off()
}
cmd

}

1 Like

Try adding this line after this code block:

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

If (value.hue) value.hue = value.hue * 3.6

Hopefully the setColor is not used internally too, in which case you would have to split it.

Hi Joel,

I have 12 x Osram Lightify Par16 TW and they work perfectly over zigbee directly to the ST Hub v2…
I use the device handler from Scott (Sticks18) and it work very well…
(with the Osram default White bulb device handler (DH), the dimming is stepped whereas the the DH of Scott (bulb v2) it is very smooth and progressive…
Excellent job done by Scott!

Bulb v2 DH:

Virtual bulb v2 DH:
https://github.com/sticks18/Lightify-Bulb/blob/master/Virtual_Lightify_Bulb_v

I have grouped the Par 16 in 2 groups of 6 bulbs (6 lounge + 6 Kitchen, open space divided in 2 zones), so that I can control 6 bulbs at a time or all 12 at the same time.

to do this I did:

  • Connect each bulb with ST hub 2.
  • Change the device handler to Sticks18 Bulb v2
  • Created 3 virtual Bulb device (Sticks18 Virtual Bulb v2) which will be used to control:
    . Virtual Lounge (6 bulbs)
    . Virtual Kitchen (6 bulbs)
    . Virtual All (Kitchen + Lounge, 12 bulbs).
  • Use the smartapp “ZigBee RGBW Bulb Grouper” of Joshua Moore x 3
    .Lounge: Master = Virtual Lounge; slaves = 6 x lounge bulbs
    .Kitchen: Master = Virtual Kitchen; Slaves = 6 x Kitchen bulbs
    .All: Master = Virtual All, Slaves = all 12 bulbs.

The code of Joshua is for RGBW bulbs grouper.


This is a clever piece of code as it calls the device handler of the bulb and pass parameters to it rather than doing the dimming etc in the smartapp… Hence why it is important to have the right device handler for the physical bulb ! (well done Joshua!)

In order to be able to list the TW bulb instead of only the RGBW in the smartapp, I simply added the color.temperature capability to Stick18 bulb v2 handler and virtual handler by adding:
capability "Color Temperature"
in the definition part of the device handler, after capability “Switch Level”.

That way, Joshua’s smart app lists the bulbs with Color.Temperature capability, hence all the bulbs with Stick18 bulb v2 DH (the Par 16 TW and virtual bulbs).

This works flawlessly, I can switch on/off, dim and change the white temperature of 6 (lounge or kitchen) or 12 bulbs at a time !

Thanks Scott, Thanks Joshua !

5 Likes

Question for those with the Flex strips, have any of you tried using another RGBW strip with the controller? I am wondering if it will work with others or if any tinkering would be needed. In theory it should work given the 4 pin connector, I believe.

1 Like

Mind the voltage too. As far as I know, for example, the hue light strip works @ 24V

1 Like

thank you for sharing this! I’m just trying to connect one of the bulbs to my v2 now.

ah turned it off and on and it showed up for connection and that device type works great

thank you again

WAF will be through the roof as I was thinking id’ have to get normal lifx lights for my ensuite (didn’t want to get more gu10 lifx as they aren’t stable - and not for sale anymore!)

May I ask if you’re happy with their brightness especially in your lounge room? my lifx downlights are 650 lumens and these are 350 lm. Tho I generally don’t use mine at full brightness.

The Lightify Par16 TW are not the brightest bulbs indeed, but grouped by 6, they are good enough for my room (10m x 11m / in 2 zones (6 bulbs for 5 x 11m each)… All 12 bulbs project to a white ceiling to defuse the white. At 350 lm each, they are just enough for my room at 100%. I would have wished for a bit more lm, but this is good enough for me, all depend of your room I guess…

One thing I haven’t work out yet is how to keep the color temp when switched off electrically (power off at the electrical switch, not via ST). When powered ON electrically they go back to 2700K 100%… This is not a big issue as I am planning to change my electrical switch with a 4 buttons zigbee scene switch in the long term (ie not electrically switch off unless power cut)…

ah yeh not as smart as the lifx lights i see but not surprising. thanks for the info!

Hi
Unless I misunderstand you, you can set the default temp within the Osram app.
You can set color, temp and level and then you have a small icon of a switch I think, which will set the current selected values as the default values. Meaning when you switch electricity on and off.