Understanding Upnp Discovery and UpnpDiscoveryParticipant workflow in general

Please compare your “DrehKnopfUpnpDiscoveryService” with other existing UpnpDiscoveryServices, as you are missing some essential parts.
E.G. your getThingUID does not have any checks against your discovery.xml, so the Binding will never know that your DIY device should be supported.

Hey hmerk,

I’ve already compared it and I know that I’m missing essential parts. Right now, the only use of the discovery service is, that I can see if the Upnp communication works in general. I don’t want to implement the full service if I haven’t understand some basic concepts (my questions). So I would be thankfull if someone can answer these. The most important one is question number 1)

Btw: Thanks for your effort helping me out during my learning process. I know that my questions may seem dumb to you, but they are very important for my understanding…

Ok, so

  1. Yes, understood correct
  2. Depends on what services your device will offer and how these services and actions will be named
  3. Add some more debug loggers directly after calling the methods, so you can check if they are called
1 Like

Good thing my understanding is correct. Is there a source where I can learn something about the informations needed in the XML file and the structure in general?

For the third point:
Found out my mistake. In the response message I used “service:hue” as the ST. If I change it to “upnp:rootdevice” the method createResult() is called. I wonder why? I thought the ST is just a name, and I can name it any way I want to. Is there just a limited amount of possible ST values?

You can have ST different from upnp:rootdevice, but in that case, you need to trigger a different upnp scan, like I do in the WeMo Binding in WemoDiscoveryService

    @Override
    protected void startScan() {
        logger.debug("Starting UPnP RootDevice search...");
        if (upnpService != null) {
            upnpService.getControlPoint().search(new RootDeviceHeader());
        } else {
            logger.debug("upnpService not set");
        }

On default, openHAB does a search for SSDP:ALL which WeMo Devices do not respond to.

1 Like

Alright thanks for this explanation. So by default OpenHab sends out a SSDP:ALL header. I guess every existing service must respond to this with it’s on respond message with specified ST header?
Still I don’t understand if there is just a limited number of ST values (predefined by the protocol)…

Also: Why is upnp:rootdevice considered to be a service? Can it be considered as an information service, which publishes informations about the root device? Do you may have other examples of ST services?

It is not considered to be a service, it is a deviceType.

ST = search target, not service type

The headers in ssdp are

M-SEARCH:  Search request broadcast method
ST: Header to specify search target
MX: Response delay limit to avoid cluttering as described in HTTP over UDP
MAN: Always set to ssdp:discover
SSDP supports very simple searches without logical expressions, name-value pairs etc. Legal search type (ST) values are:

ssdp:all : to search all UPnP devices
upnp:rootdevice: only root devices . Embedded devices will not respond
uuid:device-uuid: search a device by vendor supplied unique id
urn:schemas-upnp-org:device:deviceType- version: locates all devices of a given type (as defined by working committee)
urn:schemas-upnp-org:service:serviceType- version: locate service of a given type

You might read this page for better understanding.

1 Like

Ohhhhh this explains a lot!

I’ll finish working for today and start tommorow morning again. I’ll search for some new informations about SSDP and the XML file. I guess I cannot implement it properly if I don’t understand it in detail. I hope I can count on your help if I have more (maybe even dumb) question :thinking: Especially if my questions refer to the OpenHab DiscoveryService or Handler :slight_smile:

If I can help, I will do so, but I am no UPnP Expert, just learned by coding…:wink:

1 Like

but I am no UPnP Expert

But it seems that you know your stuff especially when it comes down to OpenHab2 binding workflows :wink:

Thanks! It took quite a long time, as I am no full time developer, just a contributer doing stuff in my spare time, which is lacking atm.
When I started the WeMo Binding, I studied the SONOS and HUE Binding very well to understand the workflows.
For the UPnP related stuff, GOOGLE was a very good friend :stuck_out_tongue_winking_eye:

Update on my learning process:

I think i finally understood UPnP. The link @hmerk provided was very helpfull! It explains UPnP plain and simple, but at the same time it’s detailed enough. In combination with the UPnP 1.1 specification paper it wasn’t that complicated to implement a UPnP discovery service for my device (device side), although it was a bit tricky to understand the schema of the device description document due to the typical “hard to read language” used in specification papers.

For the OpenHab discovery service implemenation: As an orientation I used the UpnpDiscoveryParticipant class for Hue by Philips and the WemoDiscoveryParticipant for WeMo created by @hmerk. Therefore special thanks to him. He provided very good help! The first test of my discovery service worked like a charm. I’m finally able to discover my custom diy device.

Next steps are the implementation of a handler and to find a clean way to provide the services of my device. Also a communication with these services is needed. I’m thinking about takinging a step back from standard UPnP protocol (because I personally don’t like SOAP - I think it’s overkill) and making my services accessible through REST. I’ll keep you all updated with my learning/working process of implementing a binding (device- and OpenHab-sided). Furthermore I’m thinking about publishing a tutorial, which guides binding beginners through an example implementation of a binding with UPnP discovery. I know that there are already tutorials out there, that cover a similar topic, but I want to provide one, that covers both: OpenHab side and Device side. Would be nice to know if there is anyone out there, that has a need for such a tutorial.

Greetings,
Tmirror

1 Like

I’m back with more question for @hmerk:

I’m using your WeMo binding as a reference. Right now, the Handler Factory and the Light Handler are the most interesting to me. I’ve noticed, that you use the GENA subscription pattern for receiving events. If I’ve understood it correctly, the WemoLightHandler therefore subscribes as a UpnpIOParticipant to the UpnpIOService. When an event happens on that subscription the following method of the WemoLightHandler is beeing called

@Override
    public void onValueReceived(String variable, String value, String service) {
        logger.trace("Received pair '{}':'{}' (service '{}') for thing '{}'",
                new Object[] { variable, value, service, this.getThing().getUID() });
        String capabilityId = StringUtils.substringBetween(value, "<CapabilityId>", "</CapabilityId>");
        String newValue = StringUtils.substringBetween(value, "<Value>", "</Value>");
        switch (capabilityId) {
            case "10006":
                OnOffType binaryState = null;
                binaryState = newValue.equals("0") ? OnOffType.OFF : OnOffType.ON;
                if (binaryState != null) {
                    updateState(CHANNEL_STATE, binaryState);
                }
                break;
            case "10008":
                String splitValue[] = newValue.split(":");
                if (splitValue[0] != null) {
                    int newBrightnessValue = Integer.valueOf(splitValue[0]);
                    int newBrightness = Math.round(newBrightnessValue * 100 / 255);
                    State newBrightnessState = new PercentType(newBrightness);
                    updateState(CHANNEL_BRIGHTNESS, newBrightnessState);
                    currentBrightness = newBrightness;
                }
                break;
        }
    }

This method is required due to the UpnpIOParticipant interface.

My question now is the following: For subscription an instance of the UpnpIOService is needed. Where is it created? The instance is given to the WemoLightHandler constructor as a parameter. The constructor is called in the WemoHandlerFactory.

return new WemoLightHandler(thing, upnpIOService, wemoHttpcaller);

But the acutal instance of the UpnpIOService isn’t there created either. I’ve noticed that the variable for the service has to be set somewhere else through calling the method:

@Reference
    protected void setUpnpIOService(UpnpIOService upnpIOService) {
        this.upnpIOService = upnpIOService;
    }

But where is that method called? I can’t find a single entry when using the call hierachy. I’m asking this, because I want to have a look at the actual implementation of an UpnpIOService.

It is called through DS annotations (@Reference tag)