M-SEARCH multicast to discover UPNP devices in your network

You can post your code snippets here for others to use and learn from
Post Reply
User avatar
mrupp
Posts: 147
Joined: Sun Jan 31, 2021 7:44 pm
Location: Switzerland
Contact:

M-SEARCH multicast to discover UPNP devices in your network

Post by mrupp »

Hi there

I'd like to share my efforts to scan the network for Sonos speakers. I think it might be useful to others who set themselves up for a similar task.
The code looks simple and straightforward enough when you look at it now, but it took me about 2 days to figure out how to do it.

The script sends an M-SEARCH request to the multicast IP 239.255.255.250:1900, receive and output its answers and stop searching after a timeout of 10s.

Code: Select all

@APPTITLE "M-SEARCH-Test"
@APPAUTHOR "Michael Rupp"
@VERSION 5,0 ; untestet (I use 9.0), but according to the docs this should work since 5.0
@APPDESCRIPTION "The script sends an M-SEARCH request to the multicast IP 239.255.255.250:1900, receive and output its answers and stop searching after a timeout of 10s."

Global m_discoverTimeout = 10000 ; 10s
Global m_marginLeft = 20
Global m_top = 10

/*
* Sending the M-SEARCH request to the multicast IP.
*/
Function p_Discover()
	Local ip$ = "239.255.255.250" ; multicast IP
	Local port = 1900 ; multicast port
	Local udpServerId = CreateUDPObject(Nil)

	; ST:urn:schemas-upnp-org:device:ZonePlayer:1 is to look for Sonos speakers, change it to your needs
	Local data$ = [[M-SEARCH * HTTP/1.1
HOST: ]] .. ip$ .. ":" .. port .. [[
ST:upnp:rootdevice
MAN:"ssdp:discover"
MX:1
ST:urn:schemas-upnp-org:device:ZonePlayer:1
]]

	SendUDPData(udpServerId, data$, ip$, port)

	; after m_discoverTimeout, close the UDP object to stop the search.
	SetTimeout(Nil,
		Function()
			TextOut(m_marginLeft, m_top + 10, "Finished.")
			CloseUDPObject(udpServerId)
		EndFunction, m_discoverTimeout)
EndFunction

/*
* Receive and handle answers from the multicast IP.
*/
Function p_OnReceiveUDPData(data)
	p_PrintTable(data)

	Local result$ = ReceiveUDPData(data.id)
	DebugPrint(result$)
	DebugPrint("-----------------------------------------")

	Local locaton$ = p_FindHeaderValue(result$, "LOCATION")
	TextOut(m_marginLeft, m_top, locaton$, { ListMode = True })
	m_top = m_top + 24
EndFunction

/*
* DebugPrint() for table
*/
Function p_PrintTable(table)
	For k, v In Pairs(table)
		If GetType(v) = #TABLE
			DebugPrint(k, ":")
			p_PrintTable(v)
		Else
			DebugPrint(k, ":", v)
		EndIf
	Next
	DebugPrint("-----------------------------------------")
EndFunction

/*
* Find a value in a multiline string formatted like this:
* KEY1: VALUE1
* KEY2: VALUE2
* ...
*/
Function p_FindHeaderValue(header$, name$, endOfValue$)
	Local ret$ = ""
	If IsNil(endOfValue$)
		endOfValue$ = "\n"
	EndIf
	name$ = name$ .. ":"
	Local start = FindStr(header$, name$, False)
	If start > -1
		start = start + StrLen(name$)
		Local end = FindStr(header$, endOfValue$, False, start)
		ret$ = StripStr(MidStr(header$, start, end - start))
	EndIf
	Return(ret$)
EndFunction

InstallEventHandler({ OnReceiveUDPData = p_OnReceiveUDPData })

SetFont(#SANS, 36)
SetFontColor(#LIME)
SetFontStyle(#ANTIALIAS)
TextOut(m_marginLeft, m_top, "M-SEARCH-Test")
m_top = m_top + 46
SetFont(#SANS, 24)

p_Discover()

Repeat
  WaitEvent
Forever
See Wikipedia for more information on the Simple Service Discovery Protocol (SSDP): https://de.wikipedia.org/wiki/Simple_Se ... y_Protocol

Cheers,
Michael
plouf
Posts: 462
Joined: Sun Feb 04, 2018 11:51 pm
Location: Athens,Greece

Re: M-SEARCH multicast to discover UPNP devices in your network

Post by plouf »

Thanks for sharing.
Will be very usefull for any future reference !
Christos
Post Reply