Sending a webhook on device change - is SmartApp the way to go?

I want to do something that should be extremely simple - send a webhook to an url when something in smartthings happens (basically I want on every device status change of any kind).

However, just as with anything ST-related lately this is … awful.

I managed to reach creation of a SmartApp and to confirm ownership of the app via the response, even though the documentation is mostly vague or wrong about that as well.

However, when I go to “Discover” on my Routine tab in Smartthings (btw the documentation is wrong and outdated for that too) I get an error “No network connection” with body “Couldn’t find any available networks”.

Of course, that’s extremely helpful as an error - does anyone know what is that supposed to be?

Also - is this the ONLY and easiest way to send a webhook when a device changes its status? Does it work at all?

For the last several months Samsung Smartthings has been an awful experience…

I don’t even need any information carried out. Just ping a URL when a device changes it’s status/state.

You can check some of the custom “edge services“ edge drivers to see some of the other options community members have come up with.

There is a smartthings restriction that an edge driver can only communicate with the local area network, so you have to send a message to a local server device if you need to get it outside of your own LAN. It’s doable, but a little clunky.

https://community.smartthings.com/tag/edge_services

You may prefer to use an endpoint smartapp instead since you’ve already started down that path, but I don’t know anything about those, so I’ll leave that for others to discuss. :thinking:

2 Likes

This is an option.
I don’t mind writing a bridge to send the webhooks online (or just use them locally), but it will be a pain to create a ton of automations for each status of each device to trigger a webhook via the edge driver. :frowning:

There is already an Edge Bridge from @TAustin which is available here which you could use with his HTTP Requestor Edge driver. But as you point out, creating Routines for each and every state for each and every device is a pain.

2 Likes

Yeah, I saw the bridge.
Basically my use case is this - I am using the Smartthings API to create a web-based button for controlling a device. Simple.

However if I want to display the status of the device “OPEN” or “CLOSED” I’d need to fetch the API status endpoint every 1 second or whatever.

Usually normal services have webhook service that does that for you. There seems to be no easy way for ST to let you know something happened.

You could use OAuth with the SmartThings API. That is how I capture every message with the HubiThings OAuth/Replica application for Hubitat. The messages are real time and honestly very fast. Of course it has complications to set up, but is fairly robust and mature.

2 Likes

Hey, can you elaborate a bit, please? :slight_smile:

I am currently authenticating to Smartthings API via a bearer token.
Do I need to switch to OAuth for something to work, does ST API see any difference in how you auth?

Not easy, no, but the Webhook SmartApp you were working on is a good solution. You are right that they aren’t terribly well documented. Unfortunately a lot of the burden is placed on the demo apps which are written in TypeScript/JavaScript and if you don’t want to do the same they actually make the job harder as you have to work out what they are actually doing. Don’t get me started on Glitch …

There is also the OAuth option but that has even less documentation. I’ve not really tried that out yet.

1 Like

I’ve written SmartApps using Node.JS if you want to see more examples. Unless you are implementing on some kind of serverless platform on AWS or Azure, you also have to make your computer accessible from the internet. Most people don’t want the hassle and security issues of maintaining an internet server, so something like ngrok can be used that makes it quite easy and safe. And no mucking around with oauth - your app uses tokens through the SmartThings SDK.

2 Likes

I am writing myself smarthome dashboard that is hosted on my own cloud server and is using the smart things API to control devices.

I want webhooks sent to IT so I can update the state of the devices - should the dashboard be considered the SMART APP or is the smart app a third party “entity”?

I don’t understand why this smartapp needs any code at all and what should it do (semantically, not as syntax).

The SmartApps are designed around real time message communications via webhooks, but you need to use OAuth2 and keep the connection open every 24 hours. I am not sure if this project still works but was the example I used, and you can see my Groovy code for Hubitat here.

I did a lot of trial and error to get it correct since the documentation doesn’t give you a good feel of how it works. I would not suggest using the Developer Workshop method of a Smartapp, it is half baked and isn’t in production (plus they keep breaking the website).

You can see the create step here for API ONLY:

Create App
def createApp() {
    logInfo "${getDefaultLabel()} creating SmartThings API"
    def response = [statusCode:iHttpError]
    
    String displayName = "$sDefaultAppName ${getOauthId()}"
    def app = [
        appName: "${sDefaultAppName.replaceAll("\\s","").toLowerCase()}-${UUID.randomUUID().toString()}",
        displayName: displayName,
        description: "SmartThings Service to connect with Hubitat",
        iconImage: [ url:"https://raw.githubusercontent.com/bloodtick/Hubitat/main/hubiThingsReplica/icon/replica.png" ],
        appType: "API_ONLY",
        classifications: ["CONNECTED_SERVICE"],
        singleInstance: true,
        apiOnly: [targetUrl:getTargetUrl()],
        oauth: [
            clientName: "HubiThings Replica Oauth",
            scope: lOauthScope,
            redirectUris: [getRedirectUri()]
        ]
    ]
    
    def params = [
        uri: sURI,
        path: "/apps",
        body: JsonOutput.toJson(app),
        headers: [ Authorization: "Bearer ${getAuthToken(true)}" ]        
    ]

    try {
        httpPostJson(params) { resp ->
            if(resp.status==200) {                
                logDebug "createApp() response data: ${JsonOutput.toJson(resp.data)}"
                state.appId = resp.data.app.appId
                state.appName = resp.data.app.appName
                state.oauthClientId = resp.data.oauthClientId
                state.oauthClientSecret = resp.data.oauthClientSecret
                state.oauthCallback = resp.data.app?.apiOnly?.subscription?.targetStatus                
                state.remove('createAppError')
                logTrace resp.data
            }
            response.statusCode = resp.status
        }
    } catch (e) {
        logWarn "createApp() error: $e"
        state.createAppError = e.toString()
    }
    return response
}
1 Like