2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.openwebnet.internal.discovery;
15 import java.util.Arrays;
16 import java.util.Collections;
17 import java.util.HashMap;
19 import java.util.Optional;
22 import org.eclipse.jdt.annotation.NonNullByDefault;
23 import org.eclipse.jdt.annotation.Nullable;
24 import org.jupnp.model.meta.DeviceDetails;
25 import org.jupnp.model.meta.ManufacturerDetails;
26 import org.jupnp.model.meta.ModelDetails;
27 import org.jupnp.model.meta.RemoteDevice;
28 import org.jupnp.model.meta.RemoteDeviceIdentity;
29 import org.jupnp.model.types.UDN;
30 import org.openhab.binding.openwebnet.internal.OpenWebNetBindingConstants;
31 import org.openhab.core.config.discovery.DiscoveryResult;
32 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
33 import org.openhab.core.config.discovery.upnp.UpnpDiscoveryParticipant;
34 import org.openhab.core.thing.ThingTypeUID;
35 import org.openhab.core.thing.ThingUID;
36 import org.osgi.service.component.annotations.Component;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
41 * The {@link BusGatewayUpnpDiscovery} is responsible for discovering supported BTicino BUS
42 * gateways devices using UPnP. It implements {@link UpnpDiscoveryParticipant}.
44 * @author Massimo Valla - Initial contribution
47 @Component(service = UpnpDiscoveryParticipant.class)
48 public class BusGatewayUpnpDiscovery implements UpnpDiscoveryParticipant {
50 private final Logger logger = LoggerFactory.getLogger(BusGatewayUpnpDiscovery.class);
52 public enum BusGatewayId {
53 MH201("IPscenarioModule", "MH201"),
54 MH202("scheduler", "MH202"),
55 F454("webserver", "F454"),
56 MY_HOME_SERVER1("myhomeserver1", "MYHOMESERVER1"),
57 TOUCH_SCREEN_3_5("touchscreen", "TOUCHSCREEN3_5"),
58 TOUCH_SCREEN_10("ts10", "TOUCHSCREEN10"),
59 MH200N("lightingcontrolunit", "MH200N");
61 private final String discoveryString, thingId;
63 private BusGatewayId(String value, String thingId) {
64 this.discoveryString = value;
65 this.thingId = thingId;
68 public static @Nullable BusGatewayId fromValue(String s) {
69 Optional<BusGatewayId> m = Arrays.stream(values()).filter(val -> s.equals(val.discoveryString)).findFirst();
77 public String getThingId() {
83 * DeviceInfo bean to store device useful info (and log them)
85 public class DeviceInfo {
87 private String friendlyName;
88 private String modelName = "<unknown>";
89 private String modelDescription = "<unknown>";
90 private String modelNumber = "<unknown>";
91 private String serialNumber = "<unknown>";
94 private String manufacturer = "<unknown>";
97 private boolean isBTicino = false;
99 private DeviceInfo(RemoteDevice device) {
100 String deviceLog = "Discovered device:\n+=== UPnP =========================================";
101 RemoteDeviceIdentity identity = device.getIdentity();
102 if (identity != null) {
103 this.udn = identity.getUdn();
104 deviceLog += "\n| ID.UDN : " + udn;
105 if (identity.getDescriptorURL() != null) {
106 deviceLog += "\n| ID.DESC URL : " + identity.getDescriptorURL();
107 this.host = identity.getDescriptorURL().getHost();
109 deviceLog += "\n| ID.MAX AGE : " + identity.getMaxAgeSeconds();
111 deviceLog += "\n| --------------";
112 DeviceDetails details = device.getDetails();
113 if (details != null) {
114 ManufacturerDetails manufacturerDetails = details.getManufacturerDetails();
115 if (manufacturerDetails != null) {
116 this.manufacturer = manufacturerDetails.getManufacturer();
117 deviceLog += "\n| MANUFACTURER : " + manufacturer + " (" + manufacturerDetails.getManufacturerURI()
119 if (manufacturer.toUpperCase().contains("BTICINO")) {
120 this.isBTicino = true;
123 ModelDetails modelDetails = details.getModelDetails();
124 if (modelDetails != null) {
125 // Model Name | Desc | Number (Uri)
126 this.modelName = modelDetails.getModelName();
127 this.modelDescription = modelDetails.getModelDescription();
128 this.modelNumber = modelDetails.getModelNumber();
129 deviceLog += "\n| MODEL : " + modelName + " | " + modelDescription + " | " + modelNumber
130 + " (" + modelDetails.getModelURI() + ")";
133 this.friendlyName = details.getFriendlyName();
134 deviceLog += "\n| FRIENDLY NAME: " + friendlyName;
135 this.serialNumber = details.getSerialNumber();
136 deviceLog += "\n| SERIAL # : " + serialNumber;
137 deviceLog += "\n| BASE URL : " + details.getBaseURL();
138 deviceLog += "\n| UPC : " + details.getUpc();
141 deviceLog += "\n+==================================================";
142 logger.debug(deviceLog);
147 public Set<ThingTypeUID> getSupportedThingTypeUIDs() {
148 return Collections.singleton(OpenWebNetBindingConstants.THING_TYPE_BUS_GATEWAY);
152 public @Nullable DiscoveryResult createResult(RemoteDevice device) {
153 logger.debug("Found device {}", device.getType());
154 DeviceInfo devInfo = new DeviceInfo(device);
155 if (!devInfo.manufacturer.matches("<unknown>")) {
156 logger.debug(" |- {} ({})", devInfo.modelName, devInfo.manufacturer);
158 ThingUID thingId = generateThingUID(devInfo);
159 if (thingId != null) {
160 String host = devInfo.host;
162 String label = "BUS Gateway";
163 String fn = devInfo.friendlyName;
169 label = label + " (" + devInfo.modelName + ", " + devInfo.modelNumber + ", " + devInfo.host + ")";
170 Map<String, Object> properties = new HashMap<>(4);
171 properties.put(OpenWebNetBindingConstants.CONFIG_PROPERTY_HOST, host);
172 properties.put(OpenWebNetBindingConstants.PROPERTY_FIRMWARE_VERSION, devInfo.modelNumber);
173 properties.put(OpenWebNetBindingConstants.PROPERTY_MODEL, devInfo.modelName);
174 properties.put(OpenWebNetBindingConstants.PROPERTY_SERIAL_NO, devInfo.serialNumber);
175 DiscoveryResult result = DiscoveryResultBuilder.create(thingId).withProperties(properties)
176 .withRepresentationProperty(OpenWebNetBindingConstants.PROPERTY_SERIAL_NO).withLabel(label)
178 UDN udn = devInfo.udn;
181 udnStr = udn.getIdentifierString();
185 logger.info("Created a DiscoveryResult for gateway '{}' (UDN={})", devInfo.friendlyName, udnStr);
188 logger.warn("Could not get host for device (UDN={})", devInfo.udn);
197 public @Nullable ThingUID getThingUID(RemoteDevice device) {
198 return generateThingUID(new DeviceInfo(device));
202 * Returns a ThingUID for supported devices from already extracted DeviceInfo
204 * @param devInfo the device info
205 * @return a new ThingUID, or null if the device is not supported by the binding
207 private @Nullable ThingUID generateThingUID(DeviceInfo devInfo) {
208 if (devInfo.isBTicino) {
209 UDN udn = devInfo.udn;
210 String idString = null;
212 idString = udn.getIdentifierString();
213 if (idString != null) {
214 String[] spl = idString.split("-");
215 if (spl.length > 3) {
216 BusGatewayId gwId = BusGatewayId.fromValue(spl[1]);
218 logger.debug("'{}' is a supported gateway", gwId);
219 String mac = spl[3]; // extract MAC address
220 String normalizedMac = mac.toLowerCase().replaceAll("[^a-f0-9]", "");
221 if (!normalizedMac.isEmpty()) {
222 return new ThingUID(OpenWebNetBindingConstants.THING_TYPE_BUS_GATEWAY,
223 gwId.getThingId() + "_" + normalizedMac);
229 logger.info("Found BTicino device: not an OpenWebNet gateway or not supported (UDN={})", idString);