openhab-core icon indicating copy to clipboard operation
openhab-core copied to clipboard

[RfC] Central UPnP/mDNS thing discovery for suggesting add-ons to install

Open kaikreuzer opened this issue 3 years ago • 6 comments

I'm creating this issue as I mentioned an idea over in another discussion. I haven't detailed it out in any way, but we could use this issue to discuss whether it makes any sense to pursue it at all.

The rough outline is: Many "Things" can be discovered nowadays through mDNS+UPnP. openHAB currently only adds them to the Inbox, if the user has already installed the according add-on(s). Especially for new users, it would be very nice if this wouldn't be possible: openHAB could identify supported devices and suggest the user to install certain add-ons instead (e.g. as a step in the setup wizard that @ghys created with the main UI).

There might be different ways to realize this:

  1. The bindings currently implement a DiscoveryParticipant service. These are usually in a separate package and pretty independent of the other binding code. We could create a single new bundle in openhab-core that contains all these DiscoveryParticipants and which is part of the default installation. The problem with this approach would be that add-on specific code would have to be added to openhab-core as well. This is imho neither from an architectural point of view nor from a contribution process a desired setup.
  2. We could come up with some new concept, like additional metadata within the openhab-addons repo, which only contains the discriminators that are required to identify a supported device from a mDNS/UPnP info object. This metadata could be made available as a file (or files in a folder) to the distro and some generic core feature, which evaluates this data and that is capable to provide a list of add-ons to the UI that the user might want to install. While this approach won't be able to directly add Things to the Inbox, it might be a suitable way to also bridge the gap between discovery and still requiring the user to install the add-on before a discovery result can anyhow be accepted.

Let me know what you think!

kaikreuzer avatar Dec 26 '21 00:12 kaikreuzer

Thanks for this @kaikreuzer. As mentioned earlier I think it is a brilliant idea, and am very willing to contribute. Following are some thoughts concerning such an "auto-disco" module, in no particular sequence..

1. SCOPE OF ADD-ONS

The scope of bindings covered by auto-disco, is a list of binding names. This could be either a) a list maintained by the openhab/add-ons maintainers where only those add-ons that fulfil specific technical requirements are included, or b) where binding developers can opt-in for their binding to be included by adding it to the list, or c) automated based on presence or absence of some metadata in the binding code as implied by @kaikreuzer above.

2. TOOLS & REVIEW PROCESS FOR ADDING BINDINGS TO SCOPE

Perhaps the CI build tools could/should include tests to ensure that a binding (if it is in the scope list) is compliant. Perhaps there should be a JUNIT test class for this functionality; either a common module covering all bindings, or a template to assist developers to create their own JUNIT test class. And openhab/add-ons maintainers / reviewers should be made aware to check results of such tests.

3. TECHNICAL SCOPE OF BINDINGS COVERED

In principle any binding that implements a DiscoveryParticipant can be included. There are probably three sub-classes of these -- namely those that implement UPNPDiscoveryParticipant, those that implement MDNSDiscoveryParticipant, and those that implement a native/other form of DiscoveryParticipant service.

4. LOCATION OF AUTO-DISCO

In principle the auto-disco component could itself be an add-on. But it is probably better to be a part of the OH core (just as the Inbox is part of the core).

5. SOLUTION DEVELOPMENT FRAMEWORK

The solution is an OSGI Component, that..

  • For each binding name in the scope list (see above) it imports the respective java UPNPDiscoveryParticipant / MDNSDiscoveryParticipant / DiscoveryParticipant class (and their dependencies).
  • When created, the component instantiates an instance of each of the above mentioned classes, and marshals them to begin scanning.
  • The marshalling code for UPNPDiscoveryParticipant / MDNSDiscoveryParticipant / DiscoveryParticipant class instances will look a lot like the code in the existing core Inbox.
  • When activated, the marshalling code scans each UPNPDiscoveryParticipant / MDNSDiscoveryParticipant / DiscoveryParticipant class instance according to the marshalling principles mentioned above.
  • When the scan is completed, the component produces an output list of binding names whose respective xxxDiscoveryParticipant's have found a potential Thing.

6. SOLUTION RUNTIME / UI

  • The OSGI Component is loaded and runs automatically on a virgin new system (requires some config param to know this).
  • Can also be loaded and run manually on existing systems via a manual command (requires a UI page with a switch to start the scan).
  • The component's output list of binding names with potential found Things, is piped into a UI page for the user to select & confirm which bindings for potential found Things should actually be installed. e.g. check box on each such binding with "select all" option; where already installed bindings are grayed out.
  • After the UI selection has been made e.g. "Proceed Button" is pressed, the component installs the binding JARs for those respective bindings who have the check box set.
  • Once all the selected binding JARs are installed, component unloads itself. And the regular Inbox will start to do its normal thing as usual.

andrewfg avatar Dec 26 '21 17:12 andrewfg

As suggested above from a UI perspective the step in the setup wizard where you can install add-ons i.e. this:

image

is a low hanging fruit - if there were an API endpoint like /rest/bindings/suggestions to call with at least a list of suggested bindings in return, then the list of add-ons to install in this part of the wizard could be pre-initialized. Details about the discovery results are t.b.d. but could be shown as well if necessary. I believe adding things is out of scope at this stage, when the add-ons are installed and the wizard is finished users can be invited to go to the Inbox to see what they can add.

If the discovery process takes some time, it could be initiated as soon as the wizard starts so that it happens while the user goes through it, and by the time they get to the "Install Add-ons" step the discovery results would be in. I even wanted to eventually add a step to (optionally) initialize the semantic model with a hierarchy of locations, causing the wizard to take even longer to go through (if those steps aren't skipped), allowing more time for the discovery to finish.

ghys avatar Dec 26 '21 18:12 ghys

I'd go for the add-on metadata (https://github.com/openhab/openhab-core/issues/2058) approach, which can then be aggregated in the add-ons repo and made available for all add-ons without having to install them. This would also allow for some other nice additions like suggesting/filtering country specific add-ons and maybe even localizing the add-on names (https://github.com/openhab/openhab-webui/issues/1213). :wink:

wborn avatar Dec 29 '21 13:12 wborn

  1. The problem with this approach would be that add-on specific code would have to be added to openhab-core as well. This is imho neither from an architectural point of view nor from a contribution process a desired setup.

This is exactly the issue we would have with the Wemo binding, as it starts a special UPnP scan at start.

hmerk avatar Jan 01 '22 12:01 hmerk

I don't know the Wemo but I am pretty sure that 'special scans' would in any case be out of scope. As far as I recall the Velux also has a 'special' hard coded mDNS discovery because the core OH JMDNS discovery did not work for that hub.

andrewfg avatar Jan 01 '22 15:01 andrewfg

Maybe proper approach is going with plugin mechanism. The mdns discovery is part of core bundles, so it is installed by default. The addons provide discovery participants but they could provide also separate descriptors/mappings/filters which could be extracted and embedded while building up final a product (OH distribution). By this way you can keep core clear or addon specific stuff, let addons bring their mdns logic through filters and just pack it up in assembly so runtime does the work. Each part does what it is supposed to do.

splatch avatar Apr 05 '22 22:04 splatch

This issue has been mentioned on openHAB Community. There might be relevant details there:

https://community.openhab.org/t/openhab-4-0-wishlist/142388/101

openhab-bot avatar Dec 30 '22 06:12 openhab-bot

This issue has been mentioned on openHAB Community. There might be relevant details there:

https://community.openhab.org/t/openhab-4-0-wishlist/142388/353

openhab-bot avatar Feb 07 '23 15:02 openhab-bot

This issue has been mentioned on openHAB Community. There might be relevant details there:

https://community.openhab.org/t/openhab-marketing-is-lacking/149280/114

openhab-bot avatar Sep 06 '23 20:09 openhab-bot

There has been quite a bit of interest in this in the forum lately (https://community.openhab.org/t/openhab-4-0-wishlist/142388/101). I do think it would be a good idea to have some form of 'addon suggestion service' that takes a light approach to trying to identify bindings that could be of use by scanning the network. We would certainly not be able to discover everything, but a few suggestions would be nice.

I'd go for the add-on metadata (#2058) approach, which can then be aggregated in the add-ons repo and made available for all add-ons without having to install them. This would also allow for some other nice additions like suggesting/filtering country specific add-ons and maybe even localizing the add-on names (openhab/openhab-webui#1213). 😉

I like the idea of additional add-on metadata to drive this. But we would need to come up with a kind of filter syntax that could be used by a core discovery service. The 2 technologies behind the core services we currently have (mDNS and UPnP) each have there own way of doing things. I suspect mDNS might actually be the simpler one. Just filtering with a pattern on device name and service string may be sufficient. Extending the addon metadata with some pattern fields for device name and service may be sufficient to create a binding suggestion list.

Therefore, what about first trying this for mDNS? We would need:

  • extension to addon metadata, could be something like:
<mdns-discovery>
    <name-pattern>FP*</name-pattern>
    <service-pattern>*_http._tcp</service-pattern>
</mdns-discovery>

This would identify the Niko Home Control system in your network. avahi-browse output would show this;

E Ifce Prot Name                                          Type                 Domain
+   eth0 IPv4 FP00112A22751D                                _http._tcp           local

An addon developer would be responsible for adding this metadata to the addon.xml.

  • a core AddonSuggestionService, keeps a list of addon suggestions and returns info on suggested addons, filters out already installed addons. Some of its suggestions could be based on discovery, but doesn't have to be. One could imagine a the service to also suggest e.g. a weather service.
  • a core MDNSAddonSuggestionService extending this AddonSuggestion Service, similar to MDNSDiscoveryService, but only keeping a list of suggested addons, no links to things or installed bindings. It would use the mdns-discovery addon metadata to filter discovered mdns services. Or it could be MDNSDiscoveryService implementing an interface for adding/removing an AddonSuggestion to the MDNS AddonSuggestionService.
  • a REST endpoint (/rest/addons/suggestions) to return a list of suggested addons.
  • UI extending the wizard with the suggestions, and a filter in the addon-store to show suggested (or show them first). I would not only do that in the wizard, but allow to use it later. Has upgrades happen, one would then be able to find new bindings that support hardware you already have.

While something similar would be possible for UPnP, I expect the filter criteria needed to be more complex. But it would again be possible to do something like this for UPnP as well.

mherwege avatar Sep 07 '23 13:09 mherwege

I expect the filter criteria needed to be more complex.

There are probably two main search criteria..

  • Wildcard search for all types of UPnP Devices (and/or Basic devices) and filter on those whose metadata contains a specific word or word template; example is the Hue bridge.
  • Search for specific device types; example urn:schemas-upnp-org:device:MediaRenderer:1
<upnp-discovery>
    <device-type>urn:schemas-upnp-org:device:Basic:1</device-type>
    <filter field-name="Model number" field-value="BSB" exact-match="false"/> // Hue bridge
</upnp-discovery>

<upnp-discovery>
    <device-type>urn:schemas-upnp-org:device:MediaRenderer:1</device-type> // Media renderer
</upnp-discovery>

<upnp-discovery>
    <device-type>urn:custom-org:custom-device:custom:*</device-type> // Custom device
</upnp-discovery>

Note: you would need to do something special with the upnpcontrol binding which is a binding that uses UPnP to find other UPnP things..

andrewfg avatar Sep 08 '23 11:09 andrewfg

Note: you would need to do something special with the upnpcontrol binding which is a binding that uses UPnP to find other UPnP things..

I don't think there is an exception. The upnpcontrol binding only makes sense when there are UPnP MediaRenderers in the network. So just detecting these would be fine. It is only about suggesting which bindings to install, not about doing the full discovery. At a minimum the media renderers, they could be used as an audio sink. If there are also UPnP MediaServers in the network the full potential of the binding can be used. I do run into something else that is a bit specific to it as well though (and which is not supported by the current upnpcontrol binding). Some media servers/renderers are embedded in other devices. I have a rework of the binding that discovers these as well, but I can't figure out how to make use of the embedded devices (don't get any of the information back). Of course, in the context of binding discovery, it would also mean that, to be complete, the matching might have to look in embedded devices as well.

mherwege avatar Sep 08 '23 12:09 mherwege

I suggest that binding suggestions can also be done based on country when bindings are cloud only.

clinique avatar Sep 09 '23 06:09 clinique

This issue has been mentioned on openHAB Community. There might be relevant details there:

https://community.openhab.org/t/openhab-marketing-is-lacking/149280/200

openhab-bot avatar Sep 16 '23 01:09 openhab-bot

Is anyone writing code for this? If so, please let me know, since otherwise I will start to write something.

andrewfg avatar Sep 16 '23 12:09 andrewfg

Is anyone writing code for this? If so, please let me know, since otherwise I will start to write something.

I wanted to start with it, but ran into this: https://github.com/openhab/openhab-core/issues/3795#issuecomment-1711226452 The first thing to solve is to get access to all metadata of the available addons before they are installed. That’s where I stopped. I don’t have enough time in the next few weeks to dedicate to this, so it would be great if you can work on it.

mherwege avatar Sep 16 '23 14:09 mherwege

@mherwege thanks for the info. I am not very experienced on OH core programming, so perhaps you can give me some coaching? Some questions below..

  1. In what core module should we locate this? I am thinking openhab.core.config.discovery ?
  2. I am thinking that the architecture shall consist of a BindingFinder class that a) gets a list of all non installed bindings, b) gets some meta data from them, c) for those that contain a certain tag 'isDiscoverable' say, depending on additional metadata e.g mdns search target, or upnp search target, use the existing core mdns resp. upnp discovery classes to do a search for things, d) for those where a thing is found add them to a list of 'suggested bindings'. Does that make sense?
  3. Can you point me to the existing code that runs thing inbox searches? I would harness the same code to do binding thing searches..
  4. Any other suggestions?

andrewfg avatar Sep 16 '23 15:09 andrewfg

I strongly suggest to start with an "easy" implementation where just mDNS and UPnP filtering/matching are used. Other cases to find by country or other cloud services could be left for future versions.

Also count on me for testing a first version with the BTicino binding (local connection, UPnP discovery)

mvalla avatar Sep 17 '23 09:09 mvalla

cases to find by country or other cloud services could be left for future versions

I agree with you. Indeed I am not even convinced about the idea to filter by country since if a user has an actual discoverable device in their house then they probably want to use that device regardless of what country they live in. And the cloud issue is irrelevant because mdns and upnp can only discover local devices.

andrewfg avatar Sep 17 '23 09:09 andrewfg

Using country info (that is provided at beginning of the new install wizard) could help identify general purpose cloud information binding (eg a weather service, trash services...). No upnp neither mdns need here.

clinique avatar Sep 17 '23 10:09 clinique

@mherwege thanks for the info. I am not very experienced on OH core programming, so perhaps you can give me some coaching? Some questions below..

  1. In what core module should we locate this? I am thinking openhab.core.config.discovery ?
  2. I am thinking that the architecture shall consist of a BindingFinder class that a) gets a list of all non installed bindings, b) gets some meta data from them, c) for those that contain a certain tag 'isDiscoverable' say, depending on additional metadata e.g mdns search target, or upnp search target, use the existing core mdns resp. upnp discovery classes to do a search for things, d) for those where a thing is found add them to a list of 'suggested bindings'. Does that make sense?
  3. Can you point me to the existing code that runs thing inbox searches? I would harness the same code to do binding thing searches..
  4. Any other suggestions?

I think you can just create a service (AddonSuggestionService) that registers as a participant to MDNSDiscoveryParticipant and UPnPDiscoveryParticipant (I would do it in 2 implementations of an AddonSuggestionService interface). That way, that service will get all MDNS and UPnP discovery messages. The service will than need to filter based on the filters coming from the binding metadata and create a new AddonSuggestion. You could have an AddonSuggestionRegistry keeping track of suggestions. A REST endpoint would list the suggestions. I was also thinking it should probably go into openhab.core.config.discovery, although I could imagine putting it in a new package all together as well. After all, we do not do full discovery, just use some of the infrastructure for MDNS and UPnP. Other discovery mechanisms do not have a core service backing it, and may need to be coded independently. The REST endpoint (rest/addons/suggestions) should go in org.openhab.core.io.rest.core.

All this seems quite feasible and not to hard to me. The key challenge is getting the metadata. If the addon is available from the marketplace, all metadata is available, even when the addon is not installed. However, if the addon is part of the distribution, it is installed through Karaf. After installation, OH has access to the metadata in the .jar file. However, before installation, all that metadata is not available, only some minimal bundle info. To understand that in more detail, have a look at the org.openhab.addon and org.openhab.addon.marketplace packages. You can try the REST API and try the /rest/addons endpoint. You will notice country and connection are only populated for installed addons, not for the others. And that info comes from the metadata in the addon.xml file. Ideally, that is where we should get the info from, also for the non-installed addons. For this to work, a few things would need to happen:

  1. Extend the addon.xml schema to include more fields to support discovery.
  2. Add code in the AddonInfo class, org.openhab.core.addon package that adds these fields.
  3. Extend the REST API to include extra fields that are necessary for discovery. Have a look at this PR to know where to do this: https://github.com/openhab/openhab-core/pull/3797
  4. And the most difficult one: have a build step in the addons repository that extract the relevant fields from all addon.xml files and makes it available to the AddonSuggestionService class in core. I don't know how to do that. It requires more than java, but also an extra build step.

Also, don't call it BindingFinder. It should be about Addons. I think it ultimately would apply for more than bindings, specifically IO services, maybe UI's, but also could also imagine it ultimately applies to rule templates. Don't push it too far just yet though. Let's start small but keep naming open.

mherwege avatar Sep 17 '23 14:09 mherwege

However, before installation, all that metadata is not available, only some minimal bundle info.

You can try the REST API and try the /rest/addons endpoint. You will notice country and connection are only populated for installed addons, not for the others.

How are the current metadata fields (eg. name of the addon and the minimal bundle info you mention) extracted from the addon repository then? It should be done already via a step like the n. 4. you listed, right? Otherwise I cannot understand how a fresh OH isntallation can list all addons available in the distribution. Can one just extend the existing extraction step with the additional metadata needed for discovery?

mvalla avatar Sep 17 '23 15:09 mvalla

create a service (AddonSuggestionService) that registers as a participant to MDNSDiscoveryParticipant .. That way, that service will get all MDNS and UPnP discovery messages.

Umm. That approach is probably oversimplified: MDNSDiscoveryParticipant has a method getServiceType() which returns a single string that 'Defines the mDNS service type this participant listens to`. So we would need to create multiple MDNSDiscoveryParticipant listener instances to listen to different service types from the meta data. But it is still doable.

andrewfg avatar Sep 17 '23 16:09 andrewfg

How are the current metadata fields (eg. name of the addon and the minimal bundle info you mention) extracted from the addon repository then? It should be done already via a step like the n. 4. you listed, right?

Karaf only provides id, name and label. And that´s what you see now. I looked at some Karaf doc and didn´t see an easy way to extend that.

mherwege avatar Sep 17 '23 16:09 mherwege

@mherwege I just committed some initial code here; perhaps you can have a look at it?

The current code creates multiple UpnpDiscoveryParticipant and MDNSDiscoveryParticipant on the fly; but I think this will not work, since multiple dynamically instantiated OSGI components are not allowed. So I have to think of another approach..

https://github.com/andrewfg/openhab-core/tree/binding-finder/bundles/org.openhab.core.config.discovery.addon

andrewfg avatar Sep 17 '23 19:09 andrewfg

Indeed, it will nor work like that. And the internal UPnP and MDNS discovery services also call thingDiscoverd for each discovered thing. We are not interested in things here. I see two options:

  1. Add functionality to the internal UPnP and MDNS discovery services to call an Addon suggestion service, on top off calling the registered participants.
  2. Create our own services, that directly register to the UPnP and MDNS services, as currently in the discovery services. They can be loosely modelled after the existing discovery services.

mherwege avatar Sep 17 '23 22:09 mherwege

^ I tend to favour option 2. by talking to MDNSClient and UpnpClient directly. I will have another try tomorrow. This code is obviously only the 'mid end'; as you say, the front end mechanism for providing the search meta data is yet to be defined; and the back end for fulfilling the UI likewise..

andrewfg avatar Sep 18 '23 04:09 andrewfg

Ok I just made some new commits, which I think looks a lot better..

https://github.com/andrewfg/openhab-core/tree/binding-finder/bundles/org.openhab.core.config.discovery.addon

The AddonSuggestionFinder supports three external public methods..

  • loadXML() loads the finder with an XML document containing addon suggestion candidates with respective match criteria.
  • startScan() starts scanning for addons whose data match the respective match criteria.
  • getSuggestions() returns a list of addon Ids to be suggested.

The AddonSuggestionFinder also supports a Servlet with the following HTTP methods..

  • POST loads the finder with an XML document containing addon suggestion candidates with respective match criteria, and starts the scan.
  • GET returns a comma delimited list of suggested addon ids.

Notes: as mentioned before..

  1. we still need some way to feed the respective XML into loadXML()
  2. we still need some way to process the list of addon Ids returned by getSuggestions()

An example of the XML is as follows. Each addon author would have to add one or more <candidate> entries for their respective addons.

<?xml version='1.0' encoding='UTF-8'?>
<suggested-addon-candidates>
	<candidate>
		<addon-uid>binding.hpprinter</addon-uid>
		<discovery-method>
			<service-type>mdns</service-type>
			<match-property>
				<name>rp</name>
				<regex>/.*\S.*/</regex>
			</match-property>
			<match-property>
				<name>ty</name>
				<regex>^hp</regex>
			</match-property>
			<mdns-service-type>_printer._tcp.local.</mdns-service-type>
		</discovery-method>
	</candidate>
	<candidate>
		<addon-uid>binding.hue</addon-uid>
		<discovery-method>
			<service-type>upnp</service-type>
			<match-property>
				<name>modelName</name>
				<regex>Philips hue bridge</regex>
			</match-property>
		</discovery-method>
		<discovery-method>
			<service-type>mdns</service-type>
			<mdns-service-type>_hue._tcp.local.</mdns-service-type>
		</discovery-method>
	</candidate>
</suggested-addon-candidates>

andrewfg avatar Sep 18 '23 16:09 andrewfg

I think the backend should be done by creating a rest api for /rest/addons/suggestions in https://github.com/openhab/openhab-core/blob/af4fce1e4f7faf9e5d89ded3ca31a20aac57ecf7/bundles/org.openhab.core.io.rest.core/src/main/java/org/openhab/core/io/rest/core/internal/addons/AddonResource.java. All the rest is the responsability of javascript in MainUI. I don’t think you need a separate servlet. I would think it should be an OSGI service to provide an aggretated view of the addon metadata from the addon repository, or other addon source. So it most likely would need to use the addon service registry to get all of the addon services.

mherwege avatar Sep 18 '23 20:09 mherwege

The POST requirement in the servlet could probably be covered by having all required metadata represented in AddonInfo. What will remain is how to get from the addon xml to AddonInfo in time in the first place. That should be the resposnability of the extra service.

mherwege avatar Sep 18 '23 21:09 mherwege