Page 1 of 2

Capture UDP Multicast

PostPosted: Tue Mar 27, 2018 8:24 pm
by mclass
I have a Daikin SkyFi air conditioner that appears to send a UDP broadcast addressed to 255.255.255.255 on port 55222 at approximately 8 second intervals (thanks Wireshark!)

To avoid collision with scripts that I'm using to control and get the status of the air conditioner (so I can capture any changes made by the wall mounted Daikin control panel), I am attempting to use the Daikin UDP broadcast to trigger a status update. My first thought was to use the Cynical Network plugin, but discovered that this doesn't support UDP (clearly stated in the plugin documentation, thank you!).

After a bit of hunting on the internet, I have attempted the following script:
Code: Select all
import socket

UDP_IP = "XXX.XXX.XXX.XXX"  #tried both the sending IP and the 255.255.255.255 destination address
UDP_PORT = 55222

sock = socket.socket(socket.AF_INET, # Internet
                     socket.SOCK_DGRAM) # UDP
sock.bind((UDP_IP, UDP_PORT))

while True:
    data, addr = sock.recvfrom(1024) # buffer size is 1024 bytes
    indigo.server.log(data,"Received Message") ## interim code for testing purposes!


but get the following error:
Code: Select all
 Script Error                    embedded script: [Errno 49] Can't assign requested address
   Script Error                    Exception Traceback (most recent call shown last):

     embedded script, line 8, at top level
     File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/socket.py", line 228, in meth
       return getattr(self._sock,name)(*args)
error: [Errno 49] Can't assign requested address


Any suggestions as to how I can achieve this by improving the script or by other means of Indigo detecting the UDP broadcast (not really interested in the package contents, only the presence of the broadcast!)

Thanks in anticipation!

mclass

Re: Capture UDP Multicast

PostPosted: Tue Mar 27, 2018 8:41 pm
by FlyingDiver
You're trying to listen on that port (not send to it), so you want to use your own IP address. Or use an empty string ('') and it'll bind to the localhost address.

See the echo server example: https://docs.python.org/2/library/socket.html#example

In this case, you're not echoing back to the client, but you're echoing to the log. :)

BTW - Multicast and Broadcast are not the same thing. This is a broadcast.

Re: Capture UDP Multicast

PostPosted: Sun Apr 01, 2018 10:58 am
by MartinG
I do something similar to monitor what my Rako lighting system controller does - this device sends UDP broadcasts on 255.255.255.255:9761 every time a lighting scene is executed. I haven't yet integrated it into Indigo, but feel free to copy & modify if it's helpful.

A python script to receive and decode the messages is pretty straightforward:

Code: Select all
import datetime
import socket

roomdescriptions = {
    9: 'Master Bedroom',
    10: 'Master Ensuite',
    17: 'First Floor Landing',
    25: 'Hall',
    33: 'Master Dressing Room',
    41: 'Kitchen',
    49: 'Upper Landing',
    57: 'Lounge',
    65: 'Outside',
    73: 'Dining Room',
    81: 'Family Room',
    89: 'Landing Blind',
    97: 'Lower Stairs',
    105: 'Upper Stairs'
}

instructiondescriptions = {
    0: 'Off',
    1: 'Fade Up',
    2: 'Fade Down',
    3: 'Scene 1',
    4: 'Scene 2',
    5: 'Scene 3',
    6: 'Scene 4',
    8: 'Ident',
    12: 'Level Set',
    13: 'Store',
    15: 'Stop',
    45: 'Custom 232',
    46: 'Event Control',
    47: 'Holiday',
    48: 'Run Macro',
    49: 'Set Scene',
    50: 'Fade',
    51: 'Toggle Channel',
    52: 'Set Level',
}

print("Started Rakobridge Listener")

# creates socket object
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

print"Socket created",
host = ''
port = 9761

s.bind((host, port))
print "& connected."

# Find Rakobridge
RakobridgeIP = socket.gethostbyname('mybridge.local')
print "Found Rako Bridge on", RakobridgeIP, " Now waiting for data..."

while True:
    data, addr = s.recvfrom(1024)

    if (addr[0] == RakobridgeIP) & (data[0] == 'S'): # if from Rakobridge and initial character is 'S'
        print str(datetime.datetime.now()).split('.')[0], '(', # print a data stamp and prepare to display raw Hex data

            # print the raw packet data in hex
        for character in data:
            print character.encode('hex'),
        else:
            print ')',

        # decode packet and convert data string characters to int
        room = ord(data[3])
        channel = ord(data[4])
        instruction = ord(data[5])

        if room in roomdescriptions:
            roomdesc = roomdescriptions[room]
        else:
            roomdesc = 'UNKNOWN ROOM'

        if instruction in instructiondescriptions:
            instructiondesc = instructiondescriptions[instruction]
        else:
            instructiondesc = 'UNKNOWN INSTRUCTION'

        if channel == 0:
            channeldesc = 'All Channels'
        else:
            channeldesc = 'Channel ' + str(channel)

        print roomdesc, channeldesc, instructiondesc,

        if instructiondesc == 'Set Scene':
            scenenum = ord(data[7])
            print scenenum,

        if instructiondesc == 'Set Level':
            level = int(ord(data[7]) / 255)
            print level,

        if instructiondesc == 'Fade':
            if ord(data[6]) == 0x81:
                print 'Down',
            elif ord(data[6]) == 0x80:
                print 'Up',
            else:
                print 'Unknown',

        print

    #else:
        #print "Other status message.", addr

Re: Capture UDP Multicast

PostPosted: Tue Apr 03, 2018 2:17 am
by mclass
Thank you both for your contributions, and am making slow progress - both with this application and my knowledge of Python!!

mclass

Re: Capture UDP Multicast

PostPosted: Wed Apr 04, 2018 8:33 pm
by mclass
Thanks again for your input. In case there are others facing a similar problem, the code snippet I am using is as follows:

Code: Select all
## DEBUGGING
debug = 0 ## Set to 1 to print debug messages, to 0 for no debug

import socket

## Set variables to use in listening for UDP broadcast
UDP_IP = '' ## All destination IP's
UDP_PORT = 55222  ## Port on which UDP message sent
DaikinIP = "XXX.XXX.XXX.XXX"  ## Address from which UDP message sent )insert your own here!)

##  Wait for UDP broadcast before requesting status

# create socket object
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
## & log if debug enabled
if debug:
   indigo.server.log("Socket created")
   
#Set socket options to avoid "Address already in use" errors
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

# Bind to the socket
s.bind((UDP_IP, UDP_PORT))
## & log if debug enabled
if debug:
   indigo.server.log("Socket connected""")

while True: ## Wait for UDP message
    data, addr = s.recvfrom(1024)
   
    if (addr[0] == DaikinIP): # if message is from Daikin     
       if debug:
          indigo.server.log ("UDP message received from SkyFi")
       s.close() # close the port
       if debug:
          indigo.server.log ("Socket closed")
       break # Break from the While loop and send the status request
if debug:
   indigo.server.log ("Completed test for UDP Message & requesting status")

## ... continue with code to request, receive and parse status



The setsockopt line was included to avoid "Address already in use" errors, but I am not convinced that it's yet successful :(

In preparing this post, I have realised that I need to add some form of error handling in the event that the UDP broadcast is not received, to log that and to bypass the remainder of the status request. This may happen if the Daikin AC unit is off line (eg powered off).

As a Python newbie, I would appreciate any feedback (be cruel :? ) on the code, and any suggestions on improvement welcome.

mclass

Re: Capture UDP Multicast

PostPosted: Fri Apr 27, 2018 2:30 am
by agame
would be so good if someone could cobble together a UDP multicast plugin with similar capabilities to what the Cynical Network plugin does for TCP (ie, manage the connection, and simplify creation of events based on the received data.)

my use case is listening for sensor changes on a Global Cache iTach Flex.

Re: Capture UDP Multicast

PostPosted: Mon Apr 30, 2018 9:48 am
by howartp
agame wrote:
would be so good if someone could cobble together a UDP multicast plugin with similar capabilities to what the Cynical Network plugin does for TCP (ie, manage the connection, and simplify creation of events based on the received data.)


Hear, hear.

I thought as much the other day, but it’s not an area I want to enter with my plugins.

Anyone?


Sent from my iPhone using Tapatalk Pro

Re: Capture UDP Multicast

PostPosted: Mon Apr 30, 2018 12:11 pm
by FlyingDiver
I don't currently have a use (that I can think of), but it's right in line with a bunch of my other plugins. I'll see if I can whip something up.

Re: Capture UDP Multicast

PostPosted: Mon Apr 30, 2018 2:41 pm
by FlyingDiver
First test release: https://github.com/FlyingDiver/Indigo-U ... g/0.0.1-b1

Create a device specifying the port you want to listen for messages on. Create an event to trigger on state changes for that device.

Re: Capture UDP Multicast

PostPosted: Mon Apr 30, 2018 6:49 pm
by mclass
Hi Joe!

I was delighted to see the arrival of this new plugin, as I have been wrestling since my last post with a script of my own - with limited success :(

I have trialled your plugin, and get the following error message:
Code: Select all
Traceback (most recent call last):
  File "plugin.py", line 99, in runConcurrentThread
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc0 in position 0: ordinal not in range(128)

   UDL Listener Error              plugin runConcurrentThread function returned or failed (will attempt again in 10 seconds)

With my limited knowledge, I suggest that there's something in the UDP message that the plugin is having trouble with! The Daikin AC sends the message at approximately 8 second intervals (or so Wireshark tells me!) and as far as I can tell the message remains unchanged.

With detailed debugging turned on the log records:
Code: Select all
Started plugin "UDL Listener 0.0.1"
   UDL Listener                    Starting UDP Listener
   UDL Listener Debug              Called deviceStartComm(self, device): Daikin Listener (1033420646)
   UDL Listener Debug              Daikin Listener: Device Current Version = 0
   UDL Listener Debug              Daikin Listener: Device Version is up to date
   UDL Listener Debug              Daikin Listener: Starting device (udpListener)
   UDL Listener Threaddebug        Daikin Listener: UDP timeout
   UDL Listener Threaddebug        Daikin Listener: UDP timeout
   UDL Listener Threaddebug        Daikin Listener: UDP timeout
   UDL Listener Threaddebug        Daikin Listener: UDP timeout
   UDL Listener Threaddebug        Daikin Listener: UDP timeout
   UDL Listener Threaddebug        Daikin Listener: UDP timeout
   UDL Listener Threaddebug        Daikin Listener: UDP timeout
   UDL Listener Threaddebug        Daikin Listener: UDP timeout
   UDL Listener Threaddebug        Daikin Listener: UDP timeout
   UDL Listener Threaddebug        Daikin Listener: UDP timeout
   UDL Listener Threaddebug        Daikin Listener: UDP timeout
   UDL Listener Threaddebug        Daikin Listener: UDP timeout
   UDL Listener Threaddebug        Daikin Listener: UDP timeout
   UDL Listener Threaddebug        Daikin Listener: UDP timeout
   UDL Listener Threaddebug        Daikin Listener: UDP timeout
   UDL Listener Threaddebug        Daikin Listener: UDP timeout
   UDL Listener Threaddebug        Daikin Listener: UDP timeout
   UDL Listener Threaddebug        Daikin Listener: UDP timeout
   UDL Listener Threaddebug        Daikin Listener: UDP timeout
   UDL Listener Threaddebug        Daikin Listener: UDP timeout
   UDL Listener Threaddebug        Daikin Listener: UDP timeout
   UDL Listener Error              Error in plugin execution runConcurrentThread:

Traceback (most recent call last):
  File "plugin.py", line 99, in runConcurrentThread
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc0 in position 0: ordinal not in range(128)

   UDL Listener Error              plugin runConcurrentThread function returned or failed (will attempt again in 10 seconds)
   UDL Listener Error              Error in plugin execution runConcurrentThread:



Would love to get this working properly!! And in true "Oliver Twist style" could I request MORE, and include means to optionally log the UDP messages received?
Thanks and keep up the good work!!
mclass

Re: Capture UDP Multicast

PostPosted: Mon Apr 30, 2018 6:52 pm
by FlyingDiver
I suspect it's crashing on the log message. The few tests I was doing had all-ASCII in the message data. Hmm. Give me a minute.

Re: Capture UDP Multicast

PostPosted: Mon Apr 30, 2018 6:55 pm
by FlyingDiver

Re: Capture UDP Multicast

PostPosted: Tue May 01, 2018 12:57 am
by mclass
Thanks for the speedy response!

I downloaded the Beta2 file, but it appears it contains b1 still.

Would you check and make sure that I'm not mistaken?

Thanks
mclass

Re: Capture UDP Multicast

PostPosted: Tue May 01, 2018 4:49 am
by agame
oh wow! thanks so much!!!
I'm experimenting with the beta2 download.
no error messages.... but also no sign of inbound events yet. will continue to fiddle.

Re: Capture UDP Multicast

PostPosted: Tue May 01, 2018 5:29 am
by agame
hmm. still no joy processing any messages...so far as I can tell.

I'm trying to monitor a Global Cache Flex Sensor/Relay 'cable' accessory. It allows a multicast broadcast of sensor status on a configurable port. Have tried a few such as 9131 and 9132.

Also have tried listening for a periodic beacon message that device broadcasts as follows:

The iTach Flex periodic UDP beacon message allows discovery of iTach Flex units on the network. The beacon is sent as a UDP packet to the multicast IP address 239.255.250.250, multicast group destination MAC 01:00:5E:7F:FA:FA, and destination port 9131
.

in all likelihood its a problem at my end but waiting to see if anyone else has success??