LiFX without OAuth (UK)

drhunter, I managed to get the LIFX bulb connected and working in the UK using a custom device script which uses an API key from LIFX’s cloud site. It negates the need for OAuth.

I also got our Wemo non-Insight switches, Osram Lightify bulbs and Nest Protects using other scripts available via the SmartThings Developer portal.

Bit of a long-winded affair but it got all the above working without having the wait for OAuth integration for UK users.

2 Likes

How did you manage this? I really want to integrate my LIFX.

@DRoss, I generated an API token from here:
https://cloud.lifx.com/settings
I think you just need to log into the site with the Lifx account you used to signup with in the regular app.

I then had to make a modification or two to Zzarbi’s LIFX custom device, https://github.com/zzarbi/smartthings/blob/master/device/lifx/lifx-bulb-2.groovy , on line 61 to hardcode the API key rather than get it from the preference. Second modification was to remove the debug preference check on line 55. Both these changes were required (though I could have implemented them better) as the accompanying smart app from Zzarbi would have had to use OAuth to to set them up initially.

Let me know if you need any more info.

1 Like

Thanks. Very helpful. I’ll try this out tomorrow evening.

Sorry, i am useless. Would you mind pasting your amended code so I can copy from it? Obviously just remove your token and replace it with [INSERT CODE HERE] or something… thanks so much.

No problem, here’s the code I’m using with my key removed (replace [API_KEY_FROM_LIFX] with your LIFX API key):

/**
 *  Lifx Http
 *
 *  Copyright 2014 Nicolas Cerveaux
 *
 *  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.
 *
 */

metadata {
    definition (name: "LIFX Bulb", namespace: "lifx", author: "Nicolas Cerveaux") {
        capability "Polling"
        capability "Switch"
        capability "Switch Level"
        capability "Color Control"
        capability "Refresh"
        
        command "setAdjustedColor"
        command "setColor"
    }

    simulator {
    }

    tiles {
        standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
            state "on", label:'${name}', action:"switch.off", icon:"st.Lighting.light14", backgroundColor:"#79b821", nextState:"turningOff"
            state "off", label:'${name}', action:"switch.on", icon:"st.Lighting.light14", backgroundColor:"#ffffff", nextState:"turningOn"
            state "turningOn", label:'${name}', icon:"st.Lighting.light14", backgroundColor:"#79b821"
            state "turningOff", label:'${name}', icon:"st.Lighting.light14", backgroundColor:"#ffffff"
        }
        controlTile("levelSliderControl", "device.level", "slider", height: 1, width: 2, inactiveLabel: false) {
            state "level", action:"switch level.setLevel"
        }
        controlTile("rgbSelector", "device.color", "color", height: 3, width: 3, inactiveLabel: false) {
            state "color", action:"setAdjustedColor"
        }
        standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") {
            state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
        }
        
        main(["switch"])
        details(["switch","levelSliderControl","rgbSelector","refresh"])
    }
}

private debug(data){
    //if(parent.appSettings.debug == "true"){
        log.debug(data)
    //}
}

private getAccessToken() {
    return "[API_KEY_FROM_LIFX]";
}

private sendCommand(path, method="GET", body=null) {
    def accessToken = getAccessToken()
    def pollParams = [
        uri: "https://api.lifx.com",
        path: "/v1beta1/"+path+".json",
        headers: ["Content-Type": "application/x-www-form-urlencoded", "Authorization": "Bearer ${accessToken}"],
        body: body
    ]
    debug(method+" Http Params ("+pollParams+")")
    
    try{
        if(method=="GET"){
            httpGet(pollParams) { resp ->            
                parseResponse(resp)
            }
        }else if(method=="PUT") {
            httpPut(pollParams) { resp ->            
                parseResponse(resp)
            }
        }
    } catch(Exception e){
        debug("___exception: " + e)
    }
}

private parseResponse(resp) {
    debug("Response: "+resp.data)
    if(resp.status == 200) {
        if (resp.data) {
            if(resp.data.power){
                def brightness = Math.ceil(resp.data.brightness*100)
                def hue = Math.ceil(resp.data.color.hue / 3.6)
                def saturation = Math.ceil(resp.data.color.saturation*100)
                
                //update switch
                if(device.currentValue("switch")!=resp.data.power){
                    debug("Update switch to "+resp.data.power)
                    sendEvent(name: "switch", value: resp.data.power)
                }
                
                // update level
                if(brightness != device.currentValue("level")){
                    debug('Update level to '+brightness)
                    sendEvent(name: 'level', value: brightness)
                }
                
                // update hue
                if(hue != device.currentValue("hue")){
                    debug('Update hue to '+hue)
                    sendEvent(name: 'hue', value: hue)
                }
                
                // update saturation
                if(saturation != device.currentValue("saturation")){
                    debug('Update saturation to '+saturation)
                    sendEvent(name: 'saturation', value: saturation)
                }
            }
        }
    }else if(resp.status == 201){
        debug("Something was created/updated")
    }
}

//parse events into attributes
def parse(value) {
    debug("Parsing '${value}' for ${device.deviceNetworkId}")
}

private sendAdjustedColor(data, powerOn) {
    def hue = Math.ceil(data.hue*3.6)
    def saturation = data.saturation/100
    def brightness = data.level/100
    
    sendCommand("lights/"+device.deviceNetworkId+"/color", "PUT", 'color=hue%3A'+hue+'%20saturation%3A'+saturation+'%20brightness%3A'+brightness+'&duration=1&power_on='+powerOn)
}

def setAdjustedColor(value) {
    def data = [:]
    data.hue = value.hue
    data.saturation = value.saturation
    data.level = device.currentValue("level")
    
    sendAdjustedColor(data, 'false')
    sendEvent(name: 'hue', value: value.hue)
    sendEvent(name: 'saturation', value: value.saturation)
}

def setLevel(double value) {
    def data = [:]
    data.hue = device.currentValue("hue")
    data.saturation = device.currentValue("saturation")
    data.level = value

    sendAdjustedColor(data, 'true')
    sendEvent(name: 'level', value: value)
    sendEvent(name: 'switch', value: "on")
}

def setColor(value) {
    log.debug "setColor: ${value}"
    def data = [:]
    data.hue = value.hue
    data.saturation = value.saturation
    data.level = (value.level)?value.level:device.currentValue("level")
    
    sendAdjustedColor(data, 'true')
    sendEvent(name: 'hue', value: value.hue)
    sendEvent(name: 'saturation', value: value.saturation)
    sendEvent(name: 'switch', value: "on")
}

def on() {
    sendCommand("lights/"+device.deviceNetworkId+"/power", "PUT", "state=on&duration=1")
    sendEvent(name: "switch", value: "on")
}

def off() {
    sendCommand("lights/"+device.deviceNetworkId+"/power", "PUT", "state=off&duration=1")
    sendEvent(name: "switch", value: "off")
}

def refresh() {
    sendCommand("lights/"+device.deviceNetworkId)
}

def poll() {
    refresh()
}

Thanks.i can’t seem to work this. I publish the device type online and then go to the app but it can’t find my lifX bulb when I search for devices, and I cannot find LifX under light bulbs when I search by device type. Am I doing something wrong? Thanks

I also cannot make this work - I am assuming one or more of the following additional modifications is needed as well - could you take a look and shed some light please:

  1. Zzarvi’s code uses the beta 1 api - do we need to modify this to use the final LIFX API
  2. Surely some modifications are also required for the accompanying smartapp?

Finally - this is all via LIFX cloud - but LIFX has a LAN API. Has anyone had any luck with that? surely that would be the most reliable method to use - cloud to cloud just seems so convoluted by comparison.

SmartThings is cloud based - the ability to control things locally (LAN) is limited in the current development environment. However there are intentions that local control will become easier and more prevalent. Otherwise it is as you say convoluted, slower and requires your Internet connection be available to work.

I have many items that are working over LAN with smartthings in my house e.g. iTach. What is limited about the current LAN abilities? seems like an enormous oversight!

I’m just echoing some things I’ve read as I haven’t develved into the TCP side yet - it may be to do with keeping listener sockets open. However for LIFX the LAN protocol uses UDP and that is not supported at all, especially as a listener, (limited hack/reverse engineer for sendUDP),

From the LIFX LAN API docs:


" The LIFX Protocol utilizes UDP/IP for all messages covered by this documentation. "

Ok then - another thing to wait for.

Anyone got ian1’s method working?

Sorry it’s my fault, I forgot about an extra step I had carried out. I’ll type up a more detailed set of instructions later in the weekend and start a new thread as recommended in the post above.

It basically involved creating a second ST account without a hub but set in the US and using the ST developer console to get the device network ID from a successfully paired OAuth LIFX device.

EDIT: Did a bit of digging and it is easier to get the Device network ID via the LIFX API. Turns out it is the the “id” field of the LIFX device if you look them up from here: http://api.developer.lifx.com/docs/list-lights You’ll need to use the selector “all” to list all bulbs and enter your API key by clicking the key button before this works.

1 Like

Yes this works!! very clever!

I’m now wondering if we can apply the same thinking towards:

  • LIFX Groups
  • Amazon Echo
  • SmartRules

I tried to do the same for Groups - but I couldn’t get it to work - have you had any luck with Groups?

Not tried groups I’m afraid, I only have a single LIFX so don’t need it yet. Though I’m planning on having a play with the API to see what it can do regarding this.

Created a new topic for you guys so we can keep the other thread clean :smile:

3 Likes

This thread has additional ‘how to’ comment here…