httpGet cast issue

hi all, i'm a non-programmer trying to get something working 'cause no-one else seems to have my issue.
i've spent HOURS trying to work this problem out, i've gotten what seems to be very close but i don't understand how to progress.
I am trying to successfully do a httpGet to an Daikin AC wifi controller.
The existing HE driver for the non-secure (HTTP) wifi adapter version of this AC unit uses a HubAction GET for its dirty work. However MY wifi adapter is the newer 'secure' version that uses HTTPS. After much time and pain, I can get the damn thing to connect, but it refuses to allow connection with HubAction (keeps giving connection resets). If i use httpGet method, it seems to connect OK, BUT i keep getting a data type error of sorts as I try to receive the data.
"java.lang.ClassCastException: java.lang.String cannot be cast to java.util.Map on line 175 (apiGet)".
I've read numerous posts and code examples, but for something that I am guessing is simple for a seasoned programmer to understand, I don't know the fundamentals enough to truly understand whats going on and how to sort it. the problem line is the httpGet, i don't really understand the 'closure' thing at all, the { resp -> def respData = resp.data.response } bit was stolen from someones post hoping it would work but ... nope.

Here is the offending snippet:
private apiGet(def apiCommand) {
def daikinURI = "https://" + getHostAddress() + apiCommand
def daikinUUID = "X-Daikin-uuid" + ": " + settings.AdapterUUID
def params = [ uri: daikinURI, ignoreSSLIssues: true, headers: daikinUUID ]
log.debug "Executing httpGet on " + daikinURI
log.debug params
//HTTPparams.put ( "Content-Type", "application/x-www-form-urlencoded" )
//HTTPparams.put ( "Timeout", 10 )
httpGet(params,{ resp -> def respData = resp.data.response })
log.debug respData
return respData
}

any help appreciated! its 2am and this is doing my head in...just so's i can smarten a supposedly already smart AC unit. :sleeping: :unamused:

What is the format of the reply? Do you have an API reference or another example or demo from the web that shows how to interact with the Daikin?

Try the closer part like this. I'll guess that you may need to tweak either contentType or requestContentType based on what the device expects and how it replies.

def respData
httpGet(params)
{ resp ->
    if (resp.data)
    {
       log.debug "resp.data = ${resp.data}"
       respData = resp.data
    }
}

What does the debug print of resp.data look like, assuming you get a response?

thanks @tomw i assume logDebug should be log.debug?

Yes, it is a helper method elsewhere in my driver. I'll edit my post to make that more clear.

hmmm...same error:
java.lang.ClassCastException: java.lang.String cannot be cast to java.util.Map on line 176 (apiGet).
the log msg you added does not 'appear', it doesn't get that far.

snippet now:

private apiGet(def apiCommand) {
def daikinURI = "https://" + getHostAddress() + apiCommand
def daikinUUID = "X-Daikin-uuid" + ": " + settings.AdapterUUID
def respData
def params = [ uri: daikinURI, ignoreSSLIssues: true, headers: daikinUUID ]
log.debug "Executing httpGet on " + daikinURI
log.debug params
//HTTPparams.put ( "Content-Type", "application/x-www-form-urlencoded" )
//HTTPparams.put ( "Timeout", 10 )
httpGet(params)
{ resp ->
if (resp.data)
{
log.Debug("resp.data = ${resp.data}")
respData = resp.data
}
}
return respData
}

Please post your code with line numbers so that it's easier to see where it is breaking down.

uh...dumb question...how do i copy the code with the numbers? the side bar with the numbers is not 'selectable'.
line 176 is literally just the httpGet(params) line

OK...one more try:

def respData
httpGet(params)
{ resp ->
    respData = resp
}

log.debug "respData = ${respData}"
log.debug "respData.getContentType() = ${respData.getContentType()}"
log.debug "respData.getData() = ${respData.getData()}"

BTW thanks for your efforts so far...same error though, it doesn't get past the httpGet and down to display the debug messages.

the guide docs Common Methods Object - Hubitat Documentation shows that using a Map to provide the necessary parameters is valid, this closure thing to me is completely unexplained and references an object of type "HttpResponseDecorator"

Ah, finally saw the issue, I think. Your headers parameter needs to be declared as a map (line 171). Right now it is just a string.

Here's an illustration from one of my drivers (with a couple of irrelevant headers entries removed):

def params =
        [
            uri: getBaseURI() + "users/_this/token",
            headers:
            [
                'AppVersion': '2.10.1',
                'BuildVersion': '131'
              ]
        ]

thanks..does this achieve what you suggest?? it still gives the same error, maybe i need to change how i'm doing it?

image

Try this:

def daikinUUID = ["X-Daikin-uuid" : settings.AdapterUUID]

1 Like

OMG. comma, colon. fussy damn compiler. whats it want next? correct spelling??

you're a freaking GENIUS. thank you thank you thank you.

1 Like

here's a snippet of using async.. which I'd suggest:

def params = [
    uri: "https://xxxxxxx",
    headers: [
        'Accept': '*/*', // */ comment
        'dataType': 'json',
        'Accept-Encoding': 'plain',
        'Accept-Language': 'en-US,en,q=0.8',
        'Connection': 'keep-alive',
        'Cookie': device.data.cookiess
    ],
timeout: 10
]

if (debugOutput) log.debug "sending getStatus request"
asynchttpGet("getStatusHandler", params)

}

def getStatusHandler(resp, data) {
if(resp.getStatus() == 200 || resp.getStatus() == 207) {
def setStatusResult = parseJson(resp.data)

sync http waits for the response.. if the Site takes 2 seconds to respond, the hub's going to be hit by that. Async on the other hand, splits the send and the response, allowing the hub to work while the Site is responding.

Obviously, if the httpGet is run once an hour, the hub isn't going to falter significantly. But for the most part, these are usually polled and at a high cycle time. Async is the better choice.

However, back to your actual question, yes, the Headers need to be a Map as shown in my snippet...

1 Like

hi csteele...thanks for the explanation, i read the method guide but it wasn't clear to me what the difference is between sync and async httpGETs are in this context.
i even gave async a try during my many hours of frustration, but i couldn't get it working either, and i seemed to be getting further with the sync version.

i agree with your suggestion...i'm almost too scared to try. :slight_smile:

yes, Sync is "easier"

Converting is easy too.

The "trick" is to recognize that the response is 'stale' until the next arrives. In my case, I have to login in two sequential http get's.. once to get some cookies, then a real login with the cookies sent along. Quite clearly, I can't fall into the 2nd http get til the first returns the cookies. With sync of course, falling is precisely what works and is easy.

1 Like

hi @csteele i';ve given it a go...but error "Method definition not expected here. Please define the method at an appropriate place or perhaps try using a block/Closure instead. at line: 182 column: 5. File: Script1.groovy @ line 182, column 5."

image

also: in this case is the return variable resp.Data ?

close your apiGet with a } before the def of getStatusHandler:

asynchttpGet("getStatusHandler", params)
}
def getStatusHandler(resp, data)

In other words, apiGet() finishes.. the hub gets control back and goes off and does other work... when the response comes in.. maybe 2 seconds later, the getStatusHandler method runs.

hmm..i thought being a "def" not a "private" meant you were defining a type of variable, not creating a new routine. OK that makes sense. i thought your } was a typo :slight_smile:

yea, that's the problem with snippets.. context is lost, traded for intense focus :smiley: