OpenGarage Forums Comments, Suggestions, Requests Request: SmartThings Integration

This topic contains 40 replies, has 10 voices, and was last updated by  codahq 2 months ago.

Viewing 15 posts - 16 through 30 (of 41 total)
  • Author
    Posts
  • #960

    Alonzo
    Participant

    Thanks @codahq. I just saw this. I have just implemented this and will report back. Thanks again!

    #984

    lawrence_jeff
    Participant

    @codahq – Does smartthings support a webhook model? If so and you have the info handy could you point me to it

    I am having a problem with some of my polling where they pickup transient door states via the http endpoint – Opengarage itself is smart enough to filter these out and not update the actual door status but the door value in the API does temporarily update.

    Having a generic webhook type handler would avoid this constant polling and maybe help in your scenario as well

    #992

    codahq
    Participant

    @lawrence_jeff Yes, it does. It is pretty simple to register a URL for events if the URL is hosted on the SmartThings platform. Apparently it gets a little harder when you try to have a local device make calls at the SmartThings hubs directly.

    If you want to add something to the firmware that would be awesome. Here is some documentation here.

    http://docs.smartthings.com/en/latest/smartapp-web-services-developers-guide/tutorial-part1.html

    Essentially you would need to make a HTTP GET for each action to:
    https://${baseUrl}/api/smartapps/installations/${app.id}/${action}?access_token=${state.accessToken}

    The base URL will change depending on where you got hosted when you log into SmartThings first so it might be one of about 10 values. For example, mine is “graph-na02-useast1.api.smartthings.com” but my brother’s who lives only 30 miles away is different. The ${app.id} is the unique ID given to the SmartApp when it is setup in SmartThing’s API. This is different for every instance of the app. The action is whatever we decide we want to implement. I would suggest at a bare minimum we have a “open” and “close” action. ${state.accessToken} is an OAUTH token that you have to generate after installing the SmartApp.

    Please note, I am calling this part of the SmartThings integration a SmartApp instead of a Device Handler because from what I understand you can’t do this type of thing in a Device Handler. I could be wrong on that but that’s the gist after looking at a view examples. Full disclaimer, I am not an expert at SmartThings on any level. My first experience writing code for the platform is for the OpenGarage. I know this will work though… I setup a quick example to make sure I could hit the API and change the statuses on a virtual device with the following SmartApp code. This was mostly taken from another example I found on the SmartThings forum and modified to work for our purposes.

    /**
     *  OpenGarage SmartApp
     *
     *  Copyright 2018 Ben Rimmasch
     *
     */
    
    definition(
        name: "HTTP Open/Close Endpoint",
        namespace: "me.bendy.opengarage",
        author: "Ben Rimmasch",
        description: "Creates an HTTP motion endpoint for your OpenGarage",
        category: "Convenience",
        iconUrl: "https://need.an.icon.com/badly.png",
        iconX2Url: "https://need.an.icon.com/[email protected]"
    )
    
    preferences {
        page(name: "selectDevices", install: false, uninstall: true, nextPage: "viewURL") {
            section("Allow endpoint to control this thing...") {
                input "og", "capability.doorControl", title: "Which door controller is the OpenGarage?", required: true
                label title: "Assign a name", required: false
                mode title: "Set for specific mode(s)", required: false
            }
        }
        page(name: "viewURL", title: "viewURL", install: true)
    }
    
    def installed() {
        log.debug "Installed with settings: ${settings}"
    }
    
    def updated() {
        log.debug "Updated with settings: ${settings}"
        unsubscribe()
    }
    
    mappings {
        path("/open") {
            action: [
                GET: "openAction"
            ]
        }
        path("/close") {
            action: [
                GET: "closeAction"
            ]
        }
    }
    
    void openAction() {
        log.debug "Updated with settings: ${settings}"
        og?.open()
    }
    
    void closeAction() {
        log.debug "Updated with settings: ${settings}"
        og?.close()
    }
    
    def generateURL() {
        createAccessToken()
        ["https://graph.api.smartthings.com/api/smartapps/installations/${app.id}/open", "?access_token=${state.accessToken}"]
    }
    
    def viewURL() {
        dynamicPage(name: "viewURL", title: "HTTP Motion Endpoint", install:!resetOauth, nextPage: resetOauth ? "viewURL" : null) {
            section() {
                generateURL() 
                paragraph "Open: https://graph.api.smartthings.com/api/smartapps/installations/${app.id}/open?access_token=${state.accessToken}"
                paragraph "Close: https://graph.api.smartthings.com/api/smartapps/installations/${app.id}/close?access_token=${state.accessToken}"
            }
        }
    }
    #993

    codahq
    Participant

    The more I think about it the more I wonder if we shouldn’t just have the openGarage tell SmartThings when a refresh needs to happen and then the SmartThings can go ask the OpenGarage what values it has. I think this will work the best with the least amount of change on both sides.

    So, maybe:

    https://${baseUrl}/api/smartapps/installations/${app.id}/needs_update?access_token=${state.accessToken}

    Or something like that? What do you think?

    #999

    codahq
    Participant

    I did some searching around and I found a similar project that uses this approach. I think this will be okay. I have implemented a SmartApp already for this approach if you can get the firmware to call the URL.

    Here is the code for the SmartApp.

    https://github.com/codahq/opengarage.io-handler/blob/master/OpenGarage.io-smartapp.groovy

    In postman (or something similar) I was able to hit the URL that this SmartApp provided and SmartThings was responding appropriately and refreshing data from the device. @lawrence_jeff Can you make a firmware change then? Please let me know if you have any questions.

    Here is the URL the OG would need to GET:
    https://${base_api_url}/api/smartapps/installations/${app_instance_id}/do_update?access_code=${access_token}

    So, in the UI of the OG they would need to provide the base api URL, the app instance ID and the access token or alternatively just this entire URL with their info already in place.

    #1000

    lawrence_jeff
    Participant

    Yes – working on getting my dev environment back up based on 1.0.8.

    #1003

    lawrence_jeff
    Participant

    @codahq – One challenge off the bat is the HTTPS requirement of smart things, this isn’t available in the current OpenGarage codebase, as all the web calls it currently makes are http. Looks like the change to add SSL would eat a good bit of the ESP’s limited memory

    Is it an option to bridge via one of the existing supported solutions – for instance use the change notification to IFTTT to have it then call your update endpoint? Or I saw mention of a MQTT -> Smartbridge adapter that could potentially pickup the change notification from MQTT and then make the HTTPS call on the OG’s behalf

    The IFTTT option could at least operate without another device but the MQTT would need something on your network like Node-red to make the HTTPS call

    #1007

    codahq
    Participant

    @lawrence_jeff

    I guess I didn’t realize that the current integration calls weren’t HTTPS. I would prefer not to go through IFTTT but that is an option. There is probably a way for an IFTTT recipe to perform a GET to any given URL. Maybe the Maker channel does that already. I think MQTT or any proxy server is a lot of requirement to add because to me it feels like this should be more simple.

    How hard is it to do a HTTP POST with a JSON payload? I did some messing around and I was able to get the more complicated method of receiving local requests without going through the ST website. Everybody around the ST forums made it sound very complicated but it wasn’t bad. I was able to get a device handler to receive local requests. The trick was that the device network ID of the OG had to match the MAC address without the octet separator and it had to be in all caps. Then it was as easy as doing a POST in Postman to http://[internal_st_hub_ip]:39500/. The request had no authentication. It had just one header with Content-Type as “application/json” and then a body in JSON.
    The JSON it is expecting in the POST to do a refresh is { “refresh”: true }.

    I wrote this new local functionality into my device handler already. I had to change a few things in the current device handler to get this to work. For example, when an outbound request is made from the device the response is only routed to the correct device handler if the device’s device network ID is the IPinHex:PortinHex. I found out that routing also works if you use the MAC address as I described above. However, to receive inbound requests the device’s device network ID MUST be the MAC and not the IP:PORT so I adapted the code to use the IP:PORT until it could get the MAC from a “/jc” call and then fill it in so it could receive requests.

    Here is the link to the updated device handler.
    https://github.com/codahq/opengarage.io-handler/blob/master/OpenGarage.io-handler.groovy

    #1008

    lawrence_jeff
    Participant

    Doing an HTTP post with that bit of JSON is no issue, but I don’t follow this part
    The trick was that the device network ID of the OG had to match the MAC address without the octet separator

    What exactly do you mean when you say netwtork ID? Is it the user agent sent by OG in the web request, or from a network resolution standpoint your hub has to resolve the IP back to the name in form MAC Address…

    #1009

    codahq
    Participant

    @lawrence_jeff Ah, sorry for the confusion. It isn’t anything you have to handle in the request on the firmware side. If you are setting up the device in SmartThings it was something very specific that you have to do on the hub side. It was so specific in fact that I programmed around it making it easier for user by filling in values that work programmatically using the IP and port and then later putting the MAC address in there when it’s available from a response from the OG. The values had to be manipulated in a special way and wasn’t intuitive at all.

    For the firmware’s part it really is as simple as just the one header, the JSON payload and the destination address of http://[internal_st_hub_ip]:39500/ I think. The rest I’ll include as a write-up or something of how you have to configure the ST hub side. It’s actually very simple for the user now.

    By the way, the listening port on the ST hub is apparently variable so I made a printout on the ST side for the user to be able to see what it is. On the firmware side don’t append 39500 as a hardcode and let the user provide a string containing the the entire URL I think just to be safe.

    Please let me know if I haven’t cleared things up.

    • This reply was modified 1 year, 1 month ago by  codahq.
    #1011

    lawrence_jeff
    Participant

    Yes that makes sense, before I try to fiddle with the UI, can you send me the specific URL for your config (I guess I just need the IP) and I will create you a firmware hardcoded to make that call on update. If it seems to work we can figure out how to incorporate that into the UI.

    Thanks
    Jeff

    #1013

    codahq
    Participant

    Awesome, let’s do http://10.10.10.225:39500/ for now. Thanks!

    #1016

    lawrence_jeff
    Participant

    Here is a version hardcoded to make that request to the IP and port you specified

    https://github.com/lawrence-jeff/OpenGarage-Firmware/tree/master/Compiled

    #1022

    codahq
    Participant

    It seems to work perfectly.

    #1023

    lawrence_jeff
    Participant

    Great to hear – after thinking about this a bit – would this be more flexible if instead of sending refresh:true it sent the new status (door open, door close, etc)
    That way you would have the option of either taking the updated status as is (and not rechecking) or you could always recheck exactly like you are today if you want

    That would make things a bit more consistent with the other code and allow your app some additional flexibility.

    Another thought – is the port specific to OG or is that generic – wondering if you had 2 OGs (in a 2 door setup) or you later wanted to use this for other custom devices if you need to be able to differentiate which device is asking the hub to do an update? IFTTT does this with a specific post endpoint but it could also be in the payload I guess

    {[DeviceName]:refresh} for instance

    • This reply was modified 1 year, 1 month ago by  lawrence_jeff.
Viewing 15 posts - 16 through 30 (of 41 total)

You must be logged in to reply to this topic.

OpenGarage Forums Comments, Suggestions, Requests Request: SmartThings Integration