[ST Edge] UDP Broadcast Listener

I’m attempting to write a LAN edge driver where the device broadcasts a message over UDP whenever its state changes. However, I’m struggling to even get the UDP socket to receive messages when running on the hub. When I run basically the same code locally, it seems to work as I would expect.

Here is my listener code.

local udp = socket.udp()
assert(udp:setoption('broadcast', true)) -- Is this needed just for listening?
assert(udp:settimeout(2))
assert(udp:setsockname('*', 0)) -- Would love to specify port but I can't
local _, p = udp:getsockname() -- Instead, for now, grab the port that was assigned for testing purposes
log.debug('Listening on port ' .. p .. '...')

while true do
    local data, ip, port = udp:receivefrom()
    log.debug('Received from ' .. tostring(ip) .. ':' .. tostring(port) .. ' ' .. tostring(data))
end

When running on the hub, I only receive a message if I send a UDP message to the hub’s IP address and the generated port. If I send it to the broadcast IP (255.255.255.255) on the generated port, the message doesn’t show up. But, if I run locally on my machine, I get messages sent to either that machine IP or the broadcast IP.

It seems that when running on the hub, broadcast messages are ignored. Is this expected? Am I doing this all wrong?

Bonus question: Is there no way around passing 0 as the port number? Alternatively, is it possible to listen for UDP messages on all ports (this is a reach, I know).

You might take a look at what @TAustin has been doing… he’s been exploring a lot of the extra driver options.

Even more relevant to what you are doing than my web requestor driver is my UPnP control point library. It is on github if you want to take a look at it. UPnP uses the gamut of TCP, UDP and multicast communications so it’s all covered in there.

This is how I create a socket for SSDP-type discovery:

local multicast_ip = "239.255.255.250"
  local multicast_port = 1900
  local listen_ip = "0.0.0.0"
  local listen_port = 0
  
  local s = assert(socket.udp(), "create discovery socket")
  
  assert(s:setsockname(listen_ip, listen_port), "discovery socket setsockname")

Not sure if the broadcast IP you’re trying to use is functional on the hub. The above should work both on hub and off.

1 Like

@MrCapitalQ

My prior code snippet was for sending things out on the multicast address. I realized you probably really want this, which is how to listen on the multicast address:

-- initialize multicast socket
  m = assert(socket.udp(), "create discovery socket")
  assert(m:setoption('reuseaddr', true))
  assert(m:setsockname(multicast_ip, multicast_port))
  assert(m:setoption("ip-add-membership", {multiaddr = multicast_ip, interface = "0.0.0.0"}), "join multicast group")
  m:settimeout(0)
1 Like

I really appreciate you taking the time to reply and point me to your library as well as the code snippets you posted. Unfortunately I couldn’t get it to work. I suspect you’re onto something with your comment about not being sure if the broadcast IP is functional on the hub. I don’t really have control over that on the broadcasting device so I can’t address that.

The good news is I was able to accomplish my goal (pushing events to the hub) with a different mechanism that was available.

Again, super thankful for those that chimed in and much love for this community.

1 Like

Just getting started on Lua to port my old device drivers to Lua. I am building the guts on my Mac and then will move them into device drivers. In doing so, I am having trouble getting UDP broadcast sent out on all interfaces. On my Mac, the code below is only sending it out on loopback but not on en0. Have not tried it on the hub as that is much harder to debug than getting it work on the Mac first.

local udp = socket.udp()
udp:setoption("broadcast", true)
udp:setsockname("*",0)
udp:settimeout(2)
udp:sendto(data, "255.255.255.255", 137)

The above code only transmits on loopback interface, but not on en0 (verified via WireShark). I tried changing setsockname to the IP address on en0, but still no dice.

I have also tried using network-prefix.255 instead of 255.255.255.255 - does not make a difference either.

Similar code written in python on the same machine works just fine! Seems like some Lua specific thing I am not doing right - any help?

1 Like

I haven’t tried to get any LUA to run on my development machine, so my mileage may differ, but the hub LUA is pretty limited on udp - it must be multicast and NOT broadcast. Here’s a snip of my code to set up to receive and send on multicast:

(global)
local udpSocket = socket.udp()
local multicastIP = "239.192.1.1"
local multicastPort = 2290

(in my handle_discovery for example)
    ... other code ...
    udpSocket = assert(socket.udp(), "recreate the socket")
    assert(udpSocket:setoption('reuseaddr', true))
    assert(udpSocket:setsockname(multicastIP, multicastPort))
    assert(udpSocket:setoption("ip-add-membership", {multiaddr = multicastIP, interface = "0.0.0.0"}), "join multicast group")
    assert(udpSocket:settimeout(0))  
       
    udpSocket:sendto("DiscoverMotor", multicastIP, multicastPort)
    local beginTime = os.time()
    while os.time() - beginTime < 20 do
      local parseResult, returnIp, returnPort = udpSocket:receivefrom()
      if parseResult then
         -- handle the result data 
      end
    end

Hello,

I’ve installed that driver in it’s basic form onto my hub. I’m running it in discovery and it’s not seeing the device I’m wanting to discover. The device is broadcasting UDP messages on port 50222. I’ve modified the watcher.lua cod to setup the socket defs for both multicas and listen (I saw that listen was doing what appeared to be UPD messages) but I’m still not seeing my device show up. Is there any guidance you can provide?

Are you referring to my UPnP driver? If so, UPnP uses port 1900 on multicast IP 239.255.255.250. Sounds like your device is doing something custom, so you’d have to make some changes.

If all you really want is the SSDP (discovery) part of UPnP, then you might want to use this code and modify it for your needs.

Yes, that is what I’m referring to. To give you my use case, I have a personal weather device that broadcasts data on my LAN on port 50222. I’d like to have a listener on my hub pulling that data and parsing it out to populate a virtual weather device in SmartThings, specifically the temperature, humidity, and illuminance capabilities.

Here is listener I wrote that I have running on a pi, it’s basically what I’d like replicate on the hub tempestListenerPicoW/tempestListenerPicoW.py at master · MABeatty1978/tempestListenerPicoW · GitHub

As @edleno mentioned above, the hub supports multicast, but I don’t think you can listen for broadcast messages. In the Edge environment, drivers cannot request a specific port. Listening on a multicast address is the one exception to this.

You might want to check with @nayelyz to see if she can find out if there is any way to make this work in an Edge driver, but I don’t think so.

1 Like

I wonder if that’s why the manufacturer of my personal weather station stuck the “Works With SmartThings” logo on it’s website in 2020. Then took it down a few weeks later. Then said, “SmartThings integration coming soon in 2021”. Then in 2022 said, “SmartThings support coming late 2022”… and now more crickets.

In any regard, I appreciate you taking the time to response and greatly appreciate all the work you have done for this community. :+1:

Is this a Weatherflow Tempest system? FYI, I’m adding this source to my Edge weather driver.

I know keeping it local would be ideal, but you’d need to use your Pi as an intermediary. My driver will be using their cloud-based REST API.

Yes, it is Tempest. I’m doing some fun stuff with the API (not SmartThings related), and got tired of waiting for them to release their own integration they have been promising. I figured I’d use this as an opportunity to dive into the Edge ecosystem. I was hoping to just use the UDP data it broadcasts to update an edge driver, but I guess that isn’t possible. Tempest integrates with Weather Underground, which integrates with SmartThings, but that’s a Groovy integration, so who knows how long that will survive.

If there is anything I can help with, I would love to do so.

My Edge driver already supports WU too.