LAN device and Device Driver - Guidance Needed

For the last few days I've been playing around with a few different ESP8266 devices using ST_Anything, and it works just fine. I'm interested in playing around with deep sleep capabilities, so I started digging under the covers, particularly using the SmartThingsESP8266WiFi class directly.

I've realized that there is some tribal knowledge that I'm missing.

It is not clear from reading any of the samples I've found, or even going back to the ST DTH docs, how actions I do on the ESP map into the right things in the driver.

I started out by implementing a switch, using the example here: Hubduino Question

No problem. I understand how the Switch capability defines the interface requiring on() and off() methods. But I also noticed that the ESP code sends back a state update like this:


From that I inferred that there is some kind of mapping between the payload of the message being sent and the capability (or attribute?) that is being targeted. So in the case above, it is setting the state of the switch to off.

Is that correct?

If so, then if I want to add the "Contact Sensor" capability, after adding it to the metadata block in the driver, shouldn't I just have to do this in the ESP code?


But clearly I'm missing something here. After adding the capability, I don't see any contact state information in the device page... not at the beginning and also not after sending from the device.

I think what I'm really missing here is an understanding of the message passing / mapping between the hub and the device.

If it would help, I'm happy to post both the sketch and driver code.


Now I'm more confused. After digging around in some of the other code in ST_Anything, it seems that when sending the rssi value (to fulfill the Signal Strength capability), Dan uses a space instead of a colon, like this:

send( String("rssi ") + String(WiFi.RSSI()) );

There is some kind of a mapping that happens automagically here, and I'm not totally getting it yet.

Digging a bit more...

It seems that my parse() method isn't getting called in the driver for my contact state change, regardless of the form I use for the message payload.

I added the following to my parse method:

log.debug "parse(${description}) called"
def msg = parseLanMessage(description);
log.debug "msg: ${msg}"

And it sees my send call for the switch updates, but not from the contact sensor updates.

I'll keep poking.

Happy to help explain things... one thing to be aware of... I am not very proud of my current string based messaging design . After a few years of learning, I realize that I should have used JSON to send data back and forth between the Hub and the MCU. I would like to improve that some day. Until then, it is what it is...

A little history... ST_Anything was born from a desire to make it easier for everyone to use the SmartThings ThingShield. Back in 2014, most ThingShield examples used simple space delimited name-value pairs for transferring data. So, I simply adopted that style for ST_Anything when my son and I released it in late 2014/early 2015.

When ST stopped selling the ThingShield in late 2016, I decided to look for a LAN connected solution to keep ST_Anything alive for new users. I hit paydirt when I stumbled upon some example code by a ST Community member named Chuck Schwer. @chuck.schwer had figured out a simple, elegant solution for making true, bi-directional LAN connected devices work with ST. I shamelessly stole his work and added it to ST_Anything in early 2017 where it continues to run well to this day.

Fast forward to early 2018 and Hubitat is released. I jumped onboard immediately and started porting ST_Anything over as HubDuino. During the development, and during some of the Hubitat firmware updates, I came to learn that Hubitat did not like my simple space delimited name-value pairs. So, In a few sneaky places, I replaced the space “ “ with “%20” (and vice versa) to allow Hubitat to send the data successfully. This required some behind the scenes work that made the new code base still work with both ST and Hubitat.

As to your specific questions... you can use whatever delimiter you like. Just be consistent on both sides (Arduino and Groovy.) As I mentioned earlier, I’d recommend using JSON formatted data, but that will require some changes to my library code.

The “SmartThings...” communication libraries’ example code is probably not Hubitat compatible. That code was left to allow users familiar with the same ThingShield examples to have a decent starting point.

If you simply want a contact sensor, and want to use the esp8266 deep sleep functionality, I have some good news for you. You simply need to add the deep sleep call(s) to the setup() routine of the sketch. The last line of the setup() routine should put your esp8266 in deep sleep mode. This will prevent the loop() routine from ever running. Since setup() connects to WiFi, creates your contact sensor, and sends a status update to Hubitat, you can safely sleep afterwards. You will see a noticeable delay in how long it takes to send the update from the time the contact sensor opens, due to all of the initialization code. There’s no way around that when using deep sleep.

Let me know if the above makes sense. If not, I can try to find the example from another user where he used deep sleep with a temperature sensor. He configured deep sleep to wake up the esp8266 periodically to monitor a temperature sensor due his desire to use a battery powered solution.

You can use the HubDuino Drivers, along with your own custom sketch, as long as you send strings in the form of “contact1 open” and “contact1 closed”. Note the number following the name. This number is the index needed for having multiple contact sensors within a single sketch. Your second one would be “contact2”, etc...

The naming is critical including the case. The ReadMe, near the bottom, explains the naming convention.

Yes, I already did this using ST_Anything and it worked. As someone who's been developing for more than 30 years, though, I found myself wanting to really understand what was going on, hence this thread. :slight_smile:

1 Like

I think this constraint is part of ST_Anything, not Hubitat itself, right?

Edit: Let me say that differently. I'm really asking about Hubitat's handling, not your groovy code or the ST_Anything magic.

Let me try a different question for you...

If I were using only the SmartThings class and not the rest of the ST_Anything library, what do I need to do on the device side (the send() call) and what do I need to do on the driver side (parse?) to make it work?

As I said above, right now I'm calling send("contact:open") and it never even shows up in my parse() method. So clearly I'm misunderstanding something.

Correct. You can define the network payload however you see fit.

As long as you are sending to port 39501 on your Hubitat hub, and have the MAC address of the ESP8266 as the Hubitat device’s Device Network ID, the parse() routine will be called. Note: the MAC address must be all uppercase with no delimiters.

1 Like

THAT IS IT! The magic sauce that I was missing to understand why device initiated messages weren't getting there.

1 Like

Is the same thing true regarding the send() that is done inside of the device command handler?

Consider this for a switch:

void on()
digitalWrite(PIN_LED, LOW); // turn LED on
smartthing.send("switch:on"); // send message to cloud

Is that send format somehow "mandated" in the driver? I assumed it was working correctly when the state changed on the edit device page, but perhaps I was misinterpreting cause and effect.

The best sample code would be the HubDuino Parent Ethernet Driver. You can strip its parse() routine down to the basics. There are a few important functions needed to send a properly formatted LAN packet to the NodeMCU. Keep those. It also has the proper support for entering the MAC address, IP address, and Port of the Arduino/ESP8266 device. You trash the Button logic if you don't plan on using it.

You can eliminate the createChildDevice() routine, and a few others, if desired... Its all open source, so have fun hacking away at making it your own.

Once you have it down the basics, you can add in Capabilities and modify the parse() routine to create Events to update the intrinsic attributes for each capability.

Although, it really is easier to just use the HubDuino Parent and Child devices, at least to get started. And then start hacking away as you see fit.

The code you're referencing is for the old ST ThingShield, by the way. Not going to work with an ESP8266.

I extracted the SmartThings., SmartThingsEthernet. and SmartThingsESP8266.* files from ST_Anything and just included them in my project.

Everything is working now. Bi-directional and clean.

Thanks so much for your help and for providing the guidance I needed here. Now if I can only figure out why I'm getting a null:null state in my device page, I'll be golden. :slight_smile:

1 Like

Ignore that. I was mis-using sendEvent() when I should've been using createEvent(). All good now!

1 Like