httpGet of continuous stream?

Okay. I'm a total newb and not a SW person, so I may be asking a really dumb question, but I genuinely tried to figure this out for over a week and couldn't get anywhere.

I'm trying to read and parse an XML stream containing alert data from a Hikvision IP camera. The documentation says the client "can call this API to initialize a stream of event information" and that "a connection is established with the device when this function is called, and stays open to constantly receive event notifications." It also says the response is "server push with the MIME type multipart/mixed."

The path to the alert stream is: /ISAPI/Event/notification/alertStream

When I use a REST client, I see a normal HTTP 200 response and the response header like this, which is expected:

MIME-Version: 1.0
Content-Type: multipart/mixed; boundary=boundary

When I enter the url in my browser, the browser treats the response as a file and continually appends to the file indefinitely (until I cancel the download). Upon inspection, it contains the data that I expect. Here's a snippet:

EventNotificationAlert version="2.0" xmlns="http://www.hikvision.com/ver20/XMLSchema">
<ipAddress>192.168.1.226</ipAddress>
<portNo>80</portNo>
<protocol>HTTP</protocol>
<macAddress>c0:56:e3:xx:xx:xx</macAddress>
<channelID>1</channelID>
<dateTime>2021-02-04T21:40:27-8:00</dateTime>
<activePostCount>4</activePostCount>
<eventType>PIR</eventType>
<eventState>active</eventState>
<eventDescription>PIR alarm</eventDescription>
</EventNotificationAlert>
--boundary
Content-Type: application/xml; charset="UTF-8"
Content-Length: 466

<EventNotificationAlert version="2.0" xmlns="http://www.hikvision.com/ver20/XMLSchema">
<ipAddress>192.168.1.226</ipAddress>
<portNo>80</portNo>
<protocol>HTTP</protocol>
<macAddress>c0:56:e3:xx:xx:xx</macAddress>
<channelID>1</channelID>
<dateTime>2021-02-04T21:40:27-8:00</dateTime>
<activePostCount>5</activePostCount>
<eventType>PIR</eventType>
<eventState>active</eventState>
<eventDescription>PIR alarm</eventDescription>
</EventNotificationAlert>
--boundary
Content-Type: application/xml; charset="UTF-8"
Content-Length: 466

Now, my driver code just contains some hard-coded test code (sorry for my coding style, I'm not very experienced):

def httpParse(response, data) {
	log.debug "httpParse called!"
    log.debug response.getStatus()
	log.debug response.getHeaders()
	//log.debug response.getXml()
    log.debug response.getData()
}

def hikGet() {
	def params = [
		uri: "http://192.168.1.133",
		path: "/ISAPI/Event/notification/alertStream",
		//path: "/ISAPI/System/capabilities",
		//path: "/ISAPI/System/deviceInfo",
		//path: "/ISAPI/System/status",
		headers: ["authorization": "Basic bm90UmVhbExvZ2luOm5vdFJlYWxQdw=="]
	]
	asynchttpGet("httpParse", params)
	
    /*
	httpGet(params) { resp ->
    def event = "eventType = $resp.data.eventType"
    //def event = "model = $resp.data.model"
	log.debug event}
    */
    log.debug "done"
}

I can access other parts of the API that give a standard non-stream response (like /ISAPI/System/deviceInfo) and it works fine and I can see the expected response in the log. But if I try httpGet() or asynchhttpGet() with /ISAPI/Event/notification/alertStream path, it seems to do nothing, but I suspect it is just continuously collecting the response which never ends until it overflows.. With asynchhttpGet(), I see something like this in the log:

[dev:583]2021-02-11 12:54:12.658 am [error] java.lang.OutOfMemoryError: Java heap space (hikGet)
[dev:583]2021-02-11 12:08:40.986 am [debug]done

Seems like the call to asynchhttpGet() is successful, but the never-ending response just overflows the heap about 40 minutes later and I get an error and the callback method is never called (obviously). Note I'm not doing anything during this time.

So maybe this is super-obvious and I didn't need to explain this in such detail, but I don't know how to properly phrase my question with the correct terminology. First, I don't know how to handle a continuous stream of response. Is there some way to read/parse the stream in pieces delimited by the defined boundary embedded in the response header(boundary=boundary) ?

I haven't found very many examples of a continuous stream response (maybe I just don't know the correct search terms). I guess video would be one. I was able to find someone on IPCamTalk forums that wrote roughly what I'm looking for in C#. this is a snippet of his code:

void GetStream()
{
    HttpWebRequest wr = (HttpWebRequest)WebRequest.Create("http://" + IPtxt.Text + "/ISAPI/Event/notification/alertStream");
    wr.Method = "GET";
    wr.Accept = "*/*";
    wr.ReadWriteTimeout = 5000;
    wr.KeepAlive = false;
    wr.Credentials = new NetworkCredential(Usertxt.Text, Passtxt.Text);
    Color Detection = Color.DimGray;
    int Counter = 0;
    using (WebResponse response = wr.GetResponse())
    {
        using (StreamReader reader = new StreamReader(response.GetResponseStream()))
        {
            string line;
            while ((line = reader.ReadLine()) != null)
            {
                if (RunDetection)
                {
                    if (Counter++ > 500)
                    {
                        GC.Collect();
                        Counter = 0;
                    }
                    if (ByPassMotion) Detection = Color.Green;
                    else
                    Detection = line == "<eventState>active</eventState>" ? Color.Green : line == "<eventState>inactive</eventState>" ? Color.DimGray : Detection;
                    line = string.Empty;
                    ISAPIStatus.Invoke((MethodInvoker)(() => ISAPIStatus.BackColor = Detection));
                }
            }
        }
    }
}

Here, I can see he's using GetResponseStream() to get each line of the response and process it line-by-line looking for a simply match. Is there some way in Hubitat/Groovy to read/process streams? Is there a stream type?

1 Like

Is the EventStream Interface what I need? I'm starting to look at this: EventStream Interface - Hubitat Documentation