Local API

Is RoomMe a local device or cloud service?

The traditional method for integrating a HTTP type of device like this is to write an OAuth2 Application. These apps expose an HTTP endpoint which can easily be used to integrate both LAN and CLOUD connections, and are secure.

If the device is LAN connected, there is another fairly simple option to have a Device Driver receive unsolicited HTTP posts which avoids the OAuth2 complexities.

It is strictly local with no oauth support (at this point with V1). The unsolicited posts device driver sounds perfect. Could you please point me to an example? Thanks for your help.

Not exactly on point, but the UDP listener in Lifx might add some depth to the options.

Nothing is more 'unsolicited' than UDP :smiley:

Very interesting. Please forgive me if I am wrong but from what you said ad looking at the code I surmised that Hubitat is listening on 39501 and routes http calls to a device with the ID of the http sender IP. When a match is found it calls the parse method on the device.

Is this correct? If so could I ask where you found this documented? I’ve searched poorly it seems.

You have the basic concept correct. As for where it is documented....:thinking:

SmartThings listened on port 39500 in the same way. I don’t think they documented it either.

1 Like

Gotcha. Thanks for the help.

Would be great if they added http as a first class citizen like the other protocols so a device driver could simply implement http_post(json) and go from there.

Absolutely.. and for the 77 things ahead of that :smiley:

2 more examples of drivers with MAC addresses or IP:PORT in the device network ID so they can receive messages from LAN devices.

I believe I have helper methods in one of both of those take set the DNI as well from an IP/PORT combo in decimal (since it needs to be in caps hex I think).

Cool, thanks.

I hear ya, the joys of managing a big feature backlog.

I'm guessing you want to write an app that can receive http calls. Here's an example you can use. It may not work as I stripped out a lot of code but the key points is looking at the mappings section as that routes it to the handler. You also need to enable oauth when you add the app.

It should give a you a good idea as to how you can receive a call and action on it.

definition (
    name: "Sample Endpoint App", 
    namespace: "GvnCampbell", 
    author: "Gavin Campbell", 
    description: "",
    iconUrl: "",
    iconX2Url: "",
    singleInstance: true
) 

preferences { page(name: "pageConfig") }
def pageConfig() {
    dynamicPage(name: "", title: "", install: true, uninstall: true, refreshInterval:0) {       
        section ("Devices",hideable:true,hidden:true) {
            input(name:"requestSource",type:"enum",title:"Allow Requests From",options:[0:"Cloud and Local",1:"Cloud Only",2:"Local Only"],defaultValue:0,required:true)
            
        }
        
        if (!state.accessToken) {
            createAccessToken() // create our own OAUTH access token to use in webhook url
        }

		section("URL's",hideable:true,hidden:true) {
            paragraph("<b>Running a command locally:</b>\n" + 
                      "${getLocalApiServerUrl()}/${app.id}/device/command/[Device ID]/[Command]?access_token=${state.accessToken}\n" + 
                      "<b>Setting an attribute locally:</b>\n" + 
                      "${getLocalApiServerUrl()}/${app.id}/device/attribute/[Device ID]/[Attribute]/[Value]?access_token=${state.accessToken}")
            paragraph("<b>Running a command remotely:</b>\n" + 
                      "${getApiServerUrl()}/${hubUID}/apps/${app.id}/device/command/[Device ID]/[Command]?access_token=${state.accessToken}\n" + 
                      "<b>Setting an attribute remotely:</b>\n" + 
                      "${getApiServerUrl()}/${hubUID}/apps/${app.id}/device/attribute/[Device ID]/[Attribute]/[Value]?access_token=${state.accessToken}")
        }

    }
}

// *** [ Initialization Methods ] *********************************************
def installed() {
    initialize()
}
def updated() {
    initialize()
}
def initialize() {
    
}

// *** [ Webhook Methods ] ****************************************************
mappings {
    path("/device/command/:d/:c") {
        action: [
            GET: "handlerWebhook"
        ]
    }
    path("/device/attribute/:d/:a/:v") {
        action: [
            GET: "handlerWebhook"
        ]
    }
}
def handlerWebhook() {
    log.debug "request: ${request}"
    log.debug "params: ${params}"
    if (requestSource=="0" || (requestSource=="1" && request?.requestSource=="cloud") || (requestSource=="2" && request?.requestSource=="local")) {
    } else {
        log.debug "Requests from '${request?.requestSource}' are blocked."
    }
}
1 Like

Thanks. It seems RoomMe isn’t doing what it’ expected to do in terms of the http post. I am going over this with their head of R&D.

Is there any way to log or view a log of the traffic that Hubinet is getting at 39501 port?

In the Live Logs, if there is not a corresponding device with a matching deviceNetworkId (DNI), Hubitat will log a message in the Live Logs. I don't think it includes the payload, though.

What are you using as your DNI? It needs to be HEX encoded. It does NOT need the sending port as a suffix, just the IP Address. Look in my Parent Driver and you'll see the code that sets the DNI based on the entered IP address of the sending device.

Here's the helper function to convert a string IP address to HEX

private String convertIPtoHex(ipAddress) { 
    String hex = ipAddress.tokenize( '.' ).collect {  String.format( '%02x', it.toInteger() ) }.join()
    return hex.toUpperCase()

}

Which is used by the updated() function which is called after a user types in the IP address to the user setting and clicks SAVE

def updated() {
    log.info "Executing 'updated()'"
    log.info "Hub IP Address = ${device.hub.getDataValue("localIP")}, Hub Port = ${device.hub.getDataValue("localSrvPortTCP")}"
    log.info "Arduino IP Address = ${ip}, Hub Port = ${port}"
    
    def iphex = convertIPtoHex(ip)
    log.info "Setting DNI = ${iphex}"
    device.setDeviceNetworkId("${iphex}")
    
}
1 Like

Thanks. I put a sniffer on it and determined RoomMe is not respecting the destination port setting and defaulting to 80 instead. I assume there is no way to get traffic on 80?

I let the RoomMe guys know about this problem.

You'd have to create an OAuth2 enabled Hubitat App, instead of a driver. This would require the RoomMe folks to allow you to fully configure the URL that is being called, so you could include the OAuth2 key.

Ya, that makes sense. Easier for them to fix the port issue.

Turns out it was a bug in the RoomMe app and I got past that with their help so I’ve got a proof working. Now on to building out the real RoomMe support.

1 Like

@wayne2 Just curious about RoomMe. Is there a way to get data directly from the sensors and bypass the app on the phone? Are you trying to have the phone app talk to the Hubitat Hub?
Using just the sensors in Hubitat would be cool.
One more question: Would the RoomMe sensors see other bluetooth devices you walk around with like a smartwatch or fitness band?
I love the idea of these solely as presence sensors, just don't like the phone idea as we put those down and don't walk around with them in the house.

RoomMe requires an app on a phone or smart watch. That app sends a message containing data in which device triggered which sensor and if it was enter or exit. This will be received by my Hubitat driver and the driver will maintain the room presence information for each person and each room. I’ve gotten completely sidetracked with a remodel but should be freeing up time soon.

I just ran into RoomMe and it seems super interesting.
Has anyone had any success here or know of any progress for a driver/app with HE?