I use the following code to get an image that I then embed in a custom notification. I was hoping to use asynchttpGet but I don't see anything in the documentation for AsyncResponse object that looks like the ByteArrayInputStream that httpGet returns in response.data. Is this possible with asynchttpGet?
httpGet(
uri: data.imageUrl,
headers:
[
"Accept": "image/jpeg"
]
) {response ->
def is = response.data
ByteArrayOutputStream imageBuffer = new ByteArrayOutputStream();
int nRead;
byte[] readData = new byte[1024];
while ((nRead = is.read(readData, 0, readData.length)) != -1) {
imageBuffer.write(readData, 0, nRead);
}
imageBuffer.flush();
imageData = imageBuffer.toByteArray();
}
@mlciskey Did you ever figure out the answer to this?
@chuck.schwer I'm examining the response after I call out with an async GET. I can see headers, status (which is 200), warning messages (which are empty) and the data is null. Are you not passing content type image/jpg through in the response? I would like to pull in a thumbnail for a camera. After I get the bytes of the image back I plan to base64 encode it and store it in state. For example:
You don’t necessarily need to do the fetch using async calls. You could implement a background image refresh using the scheduler and synchronous calls. I’m assuming that the scheduler tasks execute on a different thread and shouldn’t block anything other than possibly other scheduled tasks.
In the syncronous call response.data returns a ByteArrayInputStream. In the Async call the call back function gets has a response object with a status of 200 but response.data and the call back function's data parameter are both null.
def imageTest() {
httpGet(
uri: "my url",
headers:
[
"Accept": "image/jpeg"
]
) {response ->
log.trace "response - ${response.properties.collect{it}.join(',')}"
def is = response.data
log.debug "is available = ${is.available()}"
ByteArrayOutputStream imageBuffer = new ByteArrayOutputStream();
int nRead;
byte[] readData = new byte[1024];
while ((nRead = is.read(readData, 0, readData.length)) != -1) {
imageBuffer.write(readData, 0, nRead)
}
imageBuffer.flush();
imageData = imageBuffer.toByteArray()
log.debug "size = ${imageBuffer.size()}"
}
def params = [
uri: "my url",
headers:
[
"Accept": "image/jpeg"
]
]
asynchttpGet(sendHttpResponse, params)
}
def sendHttpResponse(response, data)
{
log.debug "sendHttpResponse"
try {
log.debug "data available = ${data.available()}"
}
catch (e)
{
log.error "unable to find data size"
}
def is = response.data
try {
log.debug "is available = ${is.available()}"
}
catch (e)
{
log.error "unable to find 'is' size"
}
ByteArrayOutputStream imageBuffer = new ByteArrayOutputStream();
int nRead;
byte[] readData = new byte[1024];
while ((nRead = is.read(readData, 0, readData.length)) != -1) {
imageBuffer.write(readData, 0, nRead);
}
imageBuffer.flush();
imageData = imageBuffer.toByteArray();
log.debug "size = ${imageBuffer.size()}"
}
Thanks for the example. We do not parse the response if it is a ByteArrayInputStream. I'm open to suggestions on how to handle this. The response.data returns a String to be backwards compatible. I'm open to encoding the incoming bytes as Base64 or a Hex String and putting that result into data, any preference for type of encoding?
Well, maybe you can tell me what I want. I'm trying to pull a thumbnail and store it on a device as the most recent snapshot from a camera. I had assumed that I would base64 encode the image to a string and show it with an <img> HTML tag. So for that I would want a base64 String. Would there be a better way to do this type of thing?
Why not support additional request content types and return a ByteArrayOutputStream for the binary content types. This would be consistent with SmartThings.
Depending on the use case there could be added overhead, especially in terms of storage. Of course SmartThings offered a still image storage object which Hubitat does not. That would be the best solution for this so the dev does not have to rely on state.
Apologies.. Thought you were just looking for an input stream example.. I've not coded anything async with a bytestream. On ST wasn't necessary.. On HE I would be concerned with blocking the main event thread.
I suggested earlier in this thread using the scheduler and synchronous http calls to accomplish this.. But I am not sure if scheduled events will block the main event loop or other hub even processing, except possibly for other scheduled events.
I would keep it the same between sync and async calls. If sync calls return an input stream than do the same for async calls. Both calls do the same thing, so why implement a different result? It also makes it easier from a documentation perspective.... just my two cents though....
I agree, but we did not make that decision, we are just trying to remain compatible with existing code. If we change it now (or changed it then) all existing async get calls would have had to change when porting to Hubitat.
I might be wrong but I thought the issue was that the data is not passed through at all when it is an image and he got a null value. What I am saying is that it should be passed through as a byte stream in this case (same as the sync call) and not changing every response. Obviously, I don't know the underlying code so I can't determine what if that is even possible. It really depends on why data is null when he uses the async functions and if the async functions does different things based on content type.... I am just shooting in the dark with very opinionated bullets
@chuck.schwer was there ever any progress on this or is there still no way to get image data back from an async call? I'm running into this same issue on a project now.
My original question was never answered.. The existing definition is that getData() returns a String. It was done that way because that other system defined it like that and we wanted to remain compatible. So my proposal would be to return the data as a base 64 encoded string, is that sufficient for your requirements?
Indeed it was not answered! It would work for my needs since I'm actually taking the data and base64'ing it myself. I believe @codahq is doing the same. My gut says, @srwhite wants to do something similar. Codahq and I are working on grabbing screenshots from Ring, he was doing Arlo so I'm guessing b64 works for him as well.