LAN device driver questions

I'm using the following community driver... the basis for a new driver (for the same device) to change it from a "pull" to a "push". The existing driver works fine, but I'm using this as a learning experience.

For the first step, I simply added a parse method to the parent driver to log the incoming data...

void parse(String msg) {
logDebug("Parse method driven...");

I deleted the device, re-added it using my modified driver, etc. The driver has a preference field for the device's IP address, and it is set correctly.

Whenever the server pushes data, it gets to HE, but I get the log message...

Received data from, no matching device found for

Is there anything I need to do to "register" the driver to the server's IP address other than the existence of the IP address preference field?

The Device Network Id (DNI) needs to match one of the values for the MAC address in order for HE to find the HE device to send the data to.

If you set the DNI on the Device Details page to one of the values you are likely seeing in the error log message, that should let HE find it.

Eventually you should have the driver do this, either via a Preference Setting or whatever works for you.

@sburke781 I set the DNI to the hex value of the IP address and that worked. Thanks.

Leaving this "open" because I'm sure I will have more questions very soon.

1 Like

Here's where it gets confusing for me (as a long time software developer with no experience in OO languages like Java or Groovy)...

I added the following method to the parent driver...

Parse method

The server is set to push data (XML) to HE every 30 seconds, which it does...

Here's what the XML data looks like when viewed via the OW-Server's web admin page. Note that the following data is not a view of the exact same data in the log above, i.e., the snapshots were at different times...

Concentrating on the relative humidity and temperature values (circled in blue in the log snapshot above), how do I get those values updated on the child screen? Note that there is one child driver for each sensor, and there can be up to 22 sensors. I only have one sensor at this time, but there will be several more when this stuff "goes production" in my home's crawl space. It looks like each push will contain data for all the sensors.

Another question...

Here's a screenshot from the server web page that I assume shows (partially) what the data looks like in "XML format" when delivered to HE...

In Groovy, how do I write the value for "Temperature" in the section that represents the sensor to the log, i.e., using log.debug?

This syntax is killing me.

I'm looking for a sample LAN-based device driver that receives data (i.e., the LAN device pushes data to HE on port 39501) in XML format. It would be especially helpful if the driver consists of a parent driver and multiple child drivers, and the LAN device pushes data for all child drivers/devices on every push.

I've looked at the Ecowitt driver and learned a lot from it. However, it's not XML-based. The biggest problem I'm having now is understanding how to get values out of the XML data and into variables that can be processed by the parent and/or child drivers.

I read the new "Building a LAN or Cloud Driver" topic in the doc, but all it says about this stuff is...

The data (passed in as the first and only parameter) can then be further processed (e.g., with parseLanMessage()) or dealt with as necessary.

I need to know what to do with the data after the parseLanMessage() method call, and how to "deal with it as necessary".

The parse(String message) method receives the full HTTP request string.

parseLanMessage(String message) is a helper that breaks it into a Map of its constituent parts, and you are probably most interested in the body member, which will contain the raw XML response as a String.

Then you can use the nice Groovy parsing functions described in this tutorial to convert the XML string into something you can work with to actually get at the data in the response.

So, try something like this:

def parse(String message)
   String xmlStr = parseLanMessage(message)?.body
   // do as needed with xmlStr according to the tutorial above

Incidentally, I just noticed that Hubitat has a built in method called GPathResult parseXML(String stringToParse), which is probably just a shorthand for XmlSlurper. But I usually use the Groovy libraries directly just so it's clear to me exactly what is executing.

1 Like

@tomw I realize you replied to the other thread I started to ask for a sample LAN driver, so you probably didn't see my earlier posts in this thread. Late yesterday, I had already gotten to the point of extracting the body of the XML (although using a little different code to do so). So this is what the body looks like in the live log...

What I'm having a hard time figuring out is the syntax for accessing individual elements in the XML. Using the XML data screen shot above to see the elements, how do I write the value of "PollCount" in the parent element to the log using log.debug? How do I write the value of "Name" in the child element? What if there are multiple child elements?

That looks like the full HTTP request, including headers and the rest). You will need to get just the XML document out of that. I'd recommend the method I described above.

Then you can traverse the XML document as described in the tutorial that I linked above. Did you try what was described there? Please share your code and describe more about how it is failing.

I only see one thread/topic on this question now. Your first post "there" (now a few posts up here) was a duplicate of the post above this, so I merged the two into the same, existing topic to make things easier for everyone. (Also, please don't make duplicate posts. :smiley: )

@tomw Here's a current snapshot of the log entries...

...created by this code...

Parse method

The "whole message" contains more data than the "message body", so I assumed the value of msgBody was correct.

I've tried a lot of variations on line 209, but they all get some type of exception.

@bertabcd1234 I started the other thread because I was specifically looking for an existing driver that did the "basic LAN stuff" using XML data being pushed to HE, etc., that I could learn from and hopefully reduce the number or questions in this thread.

My concern was that people got tired of reading this thread (I can get a little verbose at times), and/or lost interest. That's why I specifically asked for a sample LAN driver in the other thread (hopefully without going into too much detail). I hadn't asked for a sample driver in this thread, so I didn't think of it as a duplicate.

You're getting less data, but it's not enough less to actually only be the XML content. :wink:

Add something like this to see what I'm talking about:

void logXml(String xml)
    log.debug groovy.xml.XmlUtil.escapeXml(xml)

What you need to do is split the body across boundary, then trim non-XML content, then parse each one.

def parse(String message)
    Map lanMsg = parseLanMessage(message)
    String bodyStr = lanMsg?.body
    String boundaryStr = lanMsg?.headers?.boundary    
    List bodyContents = bodyStr?.tokenize(boundaryStr)
    bodyContents.each { logXml(it) }
    // in the step above, strip the extra parts that aren't XML off
    // ... this string parsing is your task to figure out    
    // the result is mocked up here:
    def xml = '''
<Devices-Detail-Response xmlns="" xmlns:xsi="">
    List onlyXmlContents = [xml]
    // then do something like this with what's left
        def xmlObj = parseXML(it)
        log.debug xmlObj.PollCount

@tomw I copied your code and added the escapeXml method for debugging purposes. I understand that I need to somehow get rid of the data that is before and after the Devices-Detail-Response element, but the code didn't get very far. Here's the log showing the exception and the first part of the XML data...

Here's the code...

Ok, that error should tell you that a value on line 209 is null. There's likely an issue with the calculation of boundaryStr in my example.

For line 208, it should be something more like:

String boundaryStr = lanMsg?.headers?.getAt('Content-Type')?.split('boundary=')?.getAt(1)

Then check to make sure boundaryStr isn't null before you use it on the next line. You may want to print it to the log just to make sure the value is as expected.

@bertabcd1234 I still can't get this to work. Can you close this thread so I can open a new thread specifically about the problem I'm having trying to parse/process the XML code coming from the LAN device server? I'm concerned that the general nature of the topic and the initial posts with different questions are resulting in people ignoring this thread now (except for @tomw, who has been graciously replying).

I'm not sure that will change things (it rarely does), but sure. :slight_smile:

For continued discussion, see new topic regarding above question per request from OP.