Attention: a lot of informations are incomming
After I’ve read a lot of informations about Upnp and SSDP in general as well as the help I received in this forum, I set up a test enviorment to come a step closer in understanding Upnp Discovery with OpenHab2. I’ve implemented the following parts:
-
A discovery service for my custom binding (general setup to get some console output. There is no actual discovery result created) [OpenHab2]
-
A device simulation, which waits for a M-Search message and responses accordingly to SSDP specification (just basic stuff) [DeviceSimulation]
-
A tomcat server, which provides the location XML file as stated in the RFC [DeviceSimulation]
My understanding goes as far as this: When I start the OpenHab discovery by pressing the scan button OpenHab sends out a UDP Message which looks like this:
M-SEARCH * HTTP/1.1
ST: ssdp:all
HOST: 239.255.255.250:1900
MX: 3
MAN: ssdp:discover
This matches with the SSDP RFC. My device needs to listen to 239.255.255.250:1900. If a message is noticed, it checks if the message is an M-SEARCH message. If it is, it response. Therefore thefollowing HTTP 200 is created:
HTTP/1.1 200 OK
HOST: 239.255.255.250:1900
EXT:
CACHE-CONTROL: max-age=100
LOCATION: http://localhost:8090/demorest/discovery.xml
SERVER: FreeRTOS/7.4.2, UPnP/1.0, IpBridge/1.15.0
hue-bridgeid: 12
ST: service:hue
USN: uuid:hueIdentification::upnp:rootdevice
(Let’s ignore for the moment which informations are passed as the location and so on). This message is sent back to OpenHab. It’s not sent to the Broadcast Adress but directly to OpenHabs IP and Port, where the M-SEARCH message came from. OpenHab should receive this information and should access the location XML file (by visiting the URL that’s written after “LOCATION”). At this point my understanding fades out. I would expect that OpenHab2 would retrieve the XML file and then the method createResult() of the implemented UpnpDiscoveryParticipant is called. But somehow this doesn’t seem to happen.
Right now I have 3 central question that needs to be solved:
-
Is my understanding of the SSDP workflow with OpenHab and my simulated device correct?
-
Which informations must the XML file contain?
-
Why is the mehtode createResult not called for my device (it’s called for other Upnp devices tho)? I guess the XML file was accessed by OpenHab but sadly I cannot get a proof for that.
Here is my actual code (dirty quick typing, no real concepts):
Discovery Service
package o.e.sh.binding.drehbinding.discovery;
import static o.e.sh.binding.drehbinding.DrehBindingBindingConstants.THING_TYPE_DREHKNOPF;
import java.util.Collections;
import java.util.Set;
import org.eclipse.smarthome.config.discovery.DiscoveryResult;
import org.eclipse.smarthome.config.discovery.upnp.UpnpDiscoveryParticipant;
import org.eclipse.smarthome.core.thing.ThingTypeUID;
import org.eclipse.smarthome.core.thing.ThingUID;
import org.jupnp.model.meta.RemoteDevice;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link DrehKnopfUpnpDiscoveryService} is responsible processing the
* results of searches for UPNP devices
*
* @author Karel Goderis - Initial contribution
*/
@Component(immediate = true)
public class DrehKnopfUpnpDiscoveryService implements UpnpDiscoveryParticipant {
private final Logger logger = LoggerFactory.getLogger(DrehKnopfUpnpDiscoveryService.class);
@Override
public Set<ThingTypeUID> getSupportedThingTypeUIDs() {
return Collections.singleton(THING_TYPE_DREHKNOPF);
}
@Override
public DiscoveryResult createResult(RemoteDevice device) {
ThingUID uid = getThingUID(device);
logger.debug(device.getDisplayString());
return null;
}
@Override
public ThingUID getThingUID(RemoteDevice device) {
logger.debug(device.getDisplayString());
return null;
}
}
MyDevice Listener and Responder for M-Search
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.MulticastSocket;
import java.net.NetworkInterface;
import java.util.concurrent.TimeUnit;
public class SearchListener extends Thread {
private static final int UPNP_PORT = 1900;
private static final String MULTICAST_ADDR = "239.255.255.250";
private static final String LOCATION = "http://localhost:8090/demorest/discovery.xml";
private static final String HUE_ID = "12";
private static final String ST = "service:hue";
private static final String USN = "hueIdentification";
private String responseMsg = "HTTP/1.1 200 OK\r\n" + "HOST: %s:%d\r\n" + "EXT:\r\n"
+ "CACHE-CONTROL: max-age=100\r\n" + "LOCATION: %s\r\n"
+ "SERVER: FreeRTOS/7.4.2, UPnP/1.0, IpBridge/1.15.0\r\n" + "hue-bridgeid: %s\r\n" + "ST: %s\r\n"
+ "USN: uuid:%s::upnp:rootdevice\r\n\r\n";
private MulticastSocket multiSocket;
private byte[] buf;
private DatagramPacket recv;
private DatagramSocket sendSocket;
public SearchListener() throws IOException {
multiSocket = new MulticastSocket(UPNP_PORT);
multiSocket.joinGroup(InetAddress.getByName(MULTICAST_ADDR));
sendSocket = new DatagramSocket();
buf = new byte[1024];
recv = new DatagramPacket(buf, buf.length);
}
@Override
public void run() {
try {
System.out.println("Running");
while (true) {
if (receiveMessage()) {
sendResponse();
}
TimeUnit.SECONDS.sleep(3);
}
} catch (IOException | InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private boolean receiveMessage() throws IOException {
System.out.println("Waiting for message");
// System.out.println(multiSocket.getLocalAddress() + ":" + multiSocket.getLocalPort() + multiSocket.getPort());
multiSocket.receive(recv);
System.out.println("Received message from " + recv.getAddress() + ":" + recv.getPort());
if (recv.getLength() > 0) {
String data = new String(recv.getData());
if (data.startsWith("M-SEARCH")) {
return true;
}
}
return false;
}
private void sendResponse() throws IOException {
String msg = String.format(responseMsg, MULTICAST_ADDR, UPNP_PORT, LOCATION, HUE_ID, ST, USN);
sendSocket.send(new DatagramPacket(msg.getBytes(), msg.getBytes().length, recv.getAddress(), recv.getPort()));
System.out.println("Sent out message to: " + recv.getAddress() + ":" + recv.getPort());
System.out.println(msg);
}
}
The XML file
I guess I’m missing something really essential but I need to remind you, that I’ve just started working with Upnp and SSDP.
Hope you can help me out once again.
Thanks in advance!