I give up - why don't I get a response back from my LAN connected device?


(Dan) #1

I’ve spent most of a frustrating day trying to connect to a smart TV. The smart app can discover the TV and I am creating a child device and setting the discovered properties on the device. I am then constructing what looks to me like a proper request, but my parse() method is never called.

Code snippets:

standardTile("refresh", "device.status", inactiveLabel:false, decoration:"flat") {
            state "default", icon:"st.secondary.refresh", action:"Refresh.refresh"
        }

def refresh() 
{
    log.debug "Executing 'refresh'"
    return sessionIdCommand()
}

def sessionIdCommand()
{
    def commandText = "<?xml version=\"1.0\" encoding=\"utf-8\"?><auth><type>AuthReq</type><value>$pairingKey</value></auth>"       
    def httpRequest = [
      	method:		"POST",
        path: 		"/hdcp/api/auth",
        body:		"$commandText",
        headers:	[
        		HOST:			"$televisionIp:8080",
                        "Content-Type":	"application/atom+xml",
                    ]
	]
    
    try 
    {
    	def hubAction = new physicalgraph.device.HubAction(httpRequest)
        log.debug "hub action: $hubAction"
        hubAction
    }
    catch (Exception e) 
       {
		log.debug "Hit Exception $e on $hubAction"
	}
    }

I have (repeatedly) confirmed that my device network id is the correct hex values of ip:port. In searching through the forums here, that seemed to be the thing that tripped most people up.

What I am not certain of, is whether this hubAction is actually executed. I get no exception in the log, and the debug statement prints out a reasonable looking result. I see the log statement whenever I press the refresh tile on the device screen in the phone app.

Similar code works fine in the SmartApp with a sendHubCommand (hubAction) call. But the ST docs say that in a device type, I have to return a hubAction from my command method and it will get executed by the platform. I have similar code in another device type that works fine. The only exception is the body text and POST. The working example is a GET. So is there an issue with POST from a device type?

Thanks!


(Patrick Stuart [@pstuart]) #2

I assume you have tried this outside of ST? Does that work? What do the headers look like for a query and response?

I assume you are trying to do this inside a devicetype and not a smartapp?

What capabilities did you set for the devicetype?

Is it possible that this needs to be done over https / ssl?


(Dan) #3

Ok, that didn’t take long. As I was typing the original question the ST “similar topics” gave me a couple of extra things to search for. Turns out the device is not associated with the hub. On to the next question - how do I set the hub in the addChildDevice? addChildDevice(“zzz”, “aaa”, dni, location.hubs[0].id, …) does not seem to work. In that case I don’t even see the device in my list of devices, whereas previously it showed up, just with the hub field empty…

Oh the joys of trying to reverse engineer a platform without documentation…


(Patrick Stuart [@pstuart]) #4

So are you trying to create a child device from a smartapp?

If so, you can set the hub from that.

If you are playing around with the IDE, you need to create a device and map it to your published code with the proper DNI for the device. Then map the IDE to that real device.


(Dan) #5

Thanks for the reply. Postman is normally the first thing I do before even trying things in ST. As mentioned below (I think we posted within seconds), if I manually change the hub association in the device page, then I do indeed get a response back to my parse() function.


(Patrick Stuart [@pstuart]) #6

So, are you doing a child device from a SmartApp?

If so, you will be better served doing the hubaction in the SmartApp and subscribing to location to get responses and then passing the result into the device or get the device actions, etc.

That way it really doesn’t matter how the child device is set up, since the SmartApp handles all the local IP connections.


(Dan) #7

Do you have a working example of that? The code I tried does not work, as I mentioned above.

addChildDevice("zzz", "aaa", dni, location.hubs[0].id, ...) 

When I had the location.hubs[0].id value as null, at least it showed up in my Devices list. Now it doesn’t show up there…


(Patrick Stuart [@pstuart]) #8

Already been there…

But it is better to do the logic in the smartapp and not in the device. Check the sonos sample out or dropcam samples.


(Dan) #9

It looks like I spoke too soon. I got a response from the device one time, after I manually updated the device to set the hub. I then changed the code in the smart app to set the hub when I create the device, and the device shows the correct hub in the My Devices list. The network id is also correct, but simply no response to my parse() function in the device.

As a bit of background - I wrote a smart app that discovers a device via UPnP, That works. I get the correct IP address. I then do a HTTP request in the smart app to display a pairing key on the device, which I enter into the smart app. The smart app then creates a custom child device and sets the ip and pairing key on the device. That all works. But when I try to send a command via a hubAction to the device from the device code, I don’t seem to get a response.

Enough for today…


(Dan) #10

It looks like the final issue was that my network ID had the ip part as uppercase hex, and the port part as lowercase hex. When I made it both uppercase, things started working again. That’s pretty bizarre…


(Serge Sozonoff) #11

@pstuart

If so, you will be better served doing the hubaction in the SmartApp and subscribing to location to get responses and then passing the result into the device or get the device actions, etc.

Still unclear how we re-conciliate the hubaction with the response when it comes as a location event assuming I may have to send several hubaction commands to gather all the data I require. Anyone have any thoughts on this?

Thanks,
Serge


(Patrick Stuart [@pstuart]) #12

Not really sure what your issue is without knowing about the SmartApp and devices you are trying to control.

Assuming the devices you are trying to control have some sort of unique identifier, then that would be where I would start.

The original poster was trying to connect to a smartTV, what are you trying to do?


(Serge Sozonoff) #13

Assuming the devices you are trying to control have some sort of unique identifier, then that would be where I would start.

From what I see the request is Async. So you send the request and then at some point you get a hit on your Location handler with the response.

Now assume I send 5 different requests one after the other to my device.
How are the responses going to arrive in the Location handler ? I assume in the same order as they were sent, but who knows. What happens if one times out, do the others still come through ? So the question is how does one tie the response in the Location Handler to the request which generated it.

Thanks,
Serge


(Patrick Stuart [@pstuart]) #14

But what messages are you sending and why does it matter when the responses come back in?

Is this a Rest API call or an attempt at a raw socket communication?

If you send an API call to a device to change the channel, what response is so hard to process when it comes in, even if it is async? Success? Fail?

If you are sending an API call to a device to get a status, then when you get the status back, you just parse it. There should be some transaction or id for indicating its a status?

Basically, it sounds like you want old style terminal socket commands, send a command, get a response, send another command. ST hubaction just doesn’t work like this.


(Serge Sozonoff) #15

So I am working on interfacing with a bunch of camera’s connected through a Synology. The original code is that created by a user known as swanny and if you have a look at it you will understand the problem very quickly.

In order to interact with the Synology API you need to.

  1. Get the list of endpoints for the various API methods
  2. Login and retrieve the sessions ID to be used in all subsequent REST calls
  3. Call various endpoints to retrieve different bits of status information

So we are easily making several REST calls in order to achieve the desired result. In most cases elements from the previous response data need to be re-used for subsequent requests and so forth.

If we were simply sending a message for something to happen then potentially we could not care less about the response. But remember that often we also need to update device status as well and for this we need to parse responses and know which response we are parsing since it not a single call which returns all the status information required.

Thanks,
Serge


(Patrick Stuart [@pstuart]) #16

So the response from Snyology API doesn’t provide camera ID? I took a quick look at the camera API and each camera has a CAMERA_ID that you could link to a child devicetype

When you get a response from that Camera_ID just update the linked child device?

You can also in one request get a response from multiple Camera_IDs as well, saving you the trouble of multiple requests.

Maybe the original code is not written properly to the API spec? I don’t know, if you provide a link to the code, you could help me out.

But I still don’t see what the issue is, unless it’s specifically the way that SmartApp was written.


(Serge Sozonoff) #17

Hi Patrick,

Seems like we are moving away from the point. Yes we get Camera Id’s and all sorts of things but thats irrelevant to fact that several Synology API methods need to be called in order to fully retrieve the state of the camera. Regarding Panning, Patrol Mode, etc… Not to mention that a login needs to be performed first to retrieve a session ID.

So its impossible to parse a single JSON structure on the response side and since the various HTTP responses have different JSON structures. Hence we need to know which HTTP request generated the response in order to process the data.

The very basic problem is that you have know way of knowing which response is from which http request since you have nothing to tie the two together with.

Thanks,
Serge


(Patrick Stuart [@pstuart]) #18

This has been done many times over. I don’t understand what the issue is.

Parse the responses as they come in.

Login, get a response for login. Parse login response (hint probably contains a login status)

Get camera info by I’d, Parse response that contains that I’d to that device type

Check the hue connect and songs connect for examples of how to handle the api calls


(Serge Sozonoff) #19

Hi Patrick,

Thanks, so you seem to be implying that despite the Asynchronous nature of the hubaction call we are guaranteed to get the responses in the same order the requests were made ?

Additionally whats happens in case of a timeout during the HTTP request ? Does the calling code get notified in any way ?
If we send the HTTP request and then sit and wait for the location event listener to get called with the response before we continue to perform the next request do we hardcode a timer for 20s and assume after this that their was a timeout ?

Thanks for your help so far,
Serge


(Patrick Stuart [@pstuart]) #20

No there is no guarantee.

I haven’t found any local action where it times out but I’m sure you are able to pull your network cable and find out.

I think you are missing the point of Async communication, is it is not going to always be sequential.

So parse the response and route appropriately.

The local hubaction is less than ideal of a solution and probably doesn’t fit your needs if you can’t make it work. It was an afterthought, a hack and has many documented limitations.

I still don’t see any place that the REST API for the Synology cameras requires one action before another, other than login.

So, as all the examples show, just set a global variable of notLoggedIn and then when the API sends the response, logged in, set it to true. Now allow any other requests.

Every action can be queued up in a state variable if you want to get synchronous activities, BTW.

Send one local hubaction, wait for a response for that action, pop it off the array of the queue, if it times out, report accordingly (maybe flush the queue). Then send the next hubaction from the state array.

Build yourself a queue and only send one command at a time if you can’t figure out how to do it ASYNC.