Control Philips Hue with a SmartApp


(Sti Lab) #1

I am trying to create API endpoints using the groovy like so:

    /**
     *  App Endpoint API Access Example
     *
     *  Author: SmartThings
     ******************************
     *			Changes
     ******************************
     * Change 1: 2014-02-25
     *			 Initial version
     *
     * Change 2: 2014-03-02
     *			 Added Capabilities
     *
     */
    
    // Automatically generated. Make future change here.
    definition(
            name: "HueSmartApp",
            namespace: "HueMadness",
            author: "STI Lab",
            description: "Philips Hue Control App that appears in SmartThings Labs on the mobile app",
            category: "SmartThings Labs",
            iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png",
            iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png",
            iconX3Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png",
            oauth: true)
    
    preferences {
        section("Allow Endpoint to Control These Things...") {
            input "switches", "capability.switch", title: "Which Switches?", multiple: true
            input "color", "capability.colorControl", title: "Color Control", required: false, multiple: true
            input "level", "capability.switchLevel", title: "Level Control", required: false, multiple: true
        }
        section("Control Smart Locks") {
            //input "locks", "capability.lock", title: "Which Locks?", multiple: true
    	}
    }
    
    mappings {
        path("/switches") {
            action: [
                    GET: "listSwitches"
            ]
        }
        path("/switches/:id") {
            action: [
                    GET: "showSwitch"
            ]
        }
        path("/switches/:id/:command") {
            action: [
                    PUT: "updateSwitch"
            ]
        }
        //all capabilities
        path("/capabilities") {
            action:
            [
                    GET: "capabilitiesList"
            ]
        }
        path("/level/:id/:command") {
            action: [
                    PUT: "updateLevel"
            ]
        }
    }
    
    def installed() {}
    
    def updated() {}
    
    //switches
    def listSwitches() {
        switches.collect{device(it,"switch")}
    }
    
    def showSwitch() {
        show(switches, "switch")
    }
    
    void updateSwitch() {
        update(switches)
    }
    
   void updateLevel() {
	updateLvl(level)
   }
    def deviceHandler(evt) {}
    
    private void update(devices) {
        log.debug "update, request: params: ${params}, devices: $devices.id"
    
        //def command = request.JSON?.command
        def command = params.command
        //let's create a toggle option here
        if (command)
        {
            def device = devices.find { it.id == params.id }
            if (!device) {
                httpError(404, "Device not found")
            } else {
                if(command == "toggle")
                {
                    if(device.currentValue('switch') == "on")
                        device.off();
                    else
                        device.on();
                }
                else
                {
                    device."$command"()
                }
            }
        }
    }
    
    private show(devices, type) {
        def device = devices.find { it.id == params.id }
        if (!device) {
            httpError(404, "Device not found")
        }
        else {
            def attributeName = type == "motionSensor" ? "motion" : type
            def s = device.currentState(attributeName)
            [id: device.id, label: device.displayName, value: s?.value, unitTime: s?.date?.time, type: type]
        }
    }
    
    def capabilitiesList() {
        def cap = []
        allDevices().each {
            it.capabilities.each { cap.add(it.toString().toLowerCase()) }
        }
        [capabilities : cap.unique { a, b -> a <=> b }]
    
    }
    
    // create an unique array of all devices the user has given us access to
    def allDevices() {
        def valuesMap = switches + color + level
    
        valuesMap.unique { a, b -> a.id <=> b.id };
    }
    
    private device(it, type) {
        it ? [id: it.id, label: it.label, type: type] : null
    }
    
    private void updateLvl(devices) {
//	log.debug "update, request: params: ${params}, level: $level.id"
      log.debug "update, request: params: ${params}, devices: $devices.id, levl: ${params.param4}"

    //def command = request.JSON?.command
    def command = params.command
	def lvel = params.lvl
	log.debug "level: ${params.lvl}"
    //let's create a toggle option here
    if (command)
    {
        def device = level.find { it.id == params.id }
        if (!device) {
            httpError(404, "Device not found")
        } else {
            if(command == "toggle")
            {
                if(device.currentValue('switch') == "on"){
                  device.setLevel(50)
                  device.off()
                  }
                else{
                  	device.setLevel(100)
            		device.off()
                	device.on();
            	}
            }
            else
            {
	            device.setLevel(35)
            	device.off()
                device."$command"()
            }
        }
    }
 }
    private levelUp() {
        def level = Math.min(currentSwitchLevel + 10, 100)
        log.debug "level = $level"
        level?.setLevel(level)
    }
    
    private levelDown() {
        def level = Math.max(currentSwitchLevel - 10, 10)
        log.debug "level = $level"
        level?.setLevel(level)
    }
    
    private getCurrentSwitchLevel() {
        def level = 0
        level.each {
            level = Math.max(it.currentValue("level")?.toInteger() ?: 0, level)
        }
        level.toInteger()
    }

So, when I try to run a cURL like so:

“curl --header “Authorization: Bearer XXX-XXX-XXX-XXX-XXX” -X PUT https://graph.api.smartthings.com/api/smartapps/installations/XXX-XXX-XXX-XXX-XXX/level/myDeviceID/up

returns either a SmartAppException or Access Denied. I am not sure the exact process for adjusting brightness and color for a Philips Hue and I need to ensure that I can create an API endpoint to integrate in an Android app.

Any pointers would be greatly helpful. Thank you.


(Joshua Lyon (SharpTools.io Dashboard)) #2

What kind of Android app are you trying to integrate into?

I would try testing out the endpoints in a REST test tool with OAuth support like Postman that way you can focus on building and testing the API rather than debugging the OAuth workflow.

Edit: Also, it looks like your /level/:id/:command path maps to updateLevel but your command is defined as updateLvl so you’ve got a mismatch there.


(Sti Lab) #3

Thank you. I am trying to figure out the expiry time for the bearer as that was causing the Access Denied issue.

As for /level/:id/:command it does map to updateLevel and internally calls updateLvl so there should be no issues there.