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.HashMap;
18 import java.util.Optional;
21 import org.eclipse.jdt.annotation.NonNullByDefault;
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.jupnp.model.meta.DeviceDetails;
24 import org.jupnp.model.meta.ManufacturerDetails;
25 import org.jupnp.model.meta.ModelDetails;
26 import org.jupnp.model.meta.RemoteDevice;
27 import org.jupnp.model.meta.RemoteDeviceIdentity;
28 import org.jupnp.model.types.UDN;
29 import org.openhab.binding.openwebnet.internal.OpenWebNetBindingConstants;
30 import org.openhab.core.config.discovery.DiscoveryResult;
31 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
32 import org.openhab.core.config.discovery.upnp.UpnpDiscoveryParticipant;
33 import org.openhab.core.thing.ThingTypeUID;
34 import org.openhab.core.thing.ThingUID;
35 import org.osgi.service.component.annotations.Component;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
40 * The {@link BusGatewayUpnpDiscovery} is responsible for discovering supported BTicino BUS
41 * gateways devices using UPnP. It implements {@link UpnpDiscoveryParticipant}.
43 * @author Massimo Valla - Initial contribution
46 @Component(service = UpnpDiscoveryParticipant.class)
47 public class BusGatewayUpnpDiscovery implements UpnpDiscoveryParticipant {
49 private final Logger logger = LoggerFactory.getLogger(BusGatewayUpnpDiscovery.class);
51 public enum BusGatewayId {
52 MH201("IPscenarioModule", "MH201"),
53 MH202("scheduler", "MH202"),
54 F454("webserver", "F454"),
55 MY_HOME_SERVER1("myhomeserver1", "MYHOMESERVER1"),
56 TOUCH_SCREEN_3_5("touchscreen", "TOUCHSCREEN3_5"),
57 TOUCH_SCREEN_10("ts10", "TOUCHSCREEN10"),
58 MH200N("lightingcontrolunit", "MH200N");
60 private final String discoveryString, thingId;
62 private BusGatewayId(String value, String thingId) {
63 this.discoveryString = value;
64 this.thingId = thingId;
67 public static @Nullable BusGatewayId fromValue(String s) {
68 Optional<BusGatewayId> m = Arrays.stream(values()).filter(val -> s.equals(val.discoveryString)).findFirst();
76 public String getThingId() {
82 * DeviceInfo bean to store device useful info (and log them)
84 public class DeviceInfo {
86 private String friendlyName;
87 private String modelName = "<unknown>";
88 private String modelDescription = "<unknown>";
89 private String modelNumber = "<unknown>";
90 private String serialNumber = "<unknown>";
93 private String manufacturer = "<unknown>";
96 private boolean isBTicino = false;
98 private DeviceInfo(RemoteDevice device) {
99 String deviceLog = "Discovered device:\n+=== UPnP =========================================";
100 RemoteDeviceIdentity identity = device.getIdentity();
101 if (identity != null) {
102 this.udn = identity.getUdn();
103 deviceLog += "\n| ID.UDN : " + udn;
104 if (identity.getDescriptorURL() != null) {
105 deviceLog += "\n| ID.DESC URL : " + identity.getDescriptorURL();
106 this.host = identity.getDescriptorURL().getHost();
108 deviceLog += "\n| ID.MAX AGE : " + identity.getMaxAgeSeconds();
110 deviceLog += "\n| --------------";
111 DeviceDetails details = device.getDetails();
112 if (details != null) {
113 ManufacturerDetails manufacturerDetails = details.getManufacturerDetails();
114 if (manufacturerDetails != null) {
115 this.manufacturer = manufacturerDetails.getManufacturer();
116 deviceLog += "\n| MANUFACTURER : " + manufacturer + " (" + manufacturerDetails.getManufacturerURI()
118 if (manufacturer.toUpperCase().contains("BTICINO")) {
119 this.isBTicino = true;
122 ModelDetails modelDetails = details.getModelDetails();
123 if (modelDetails != null) {
124 // Model Name | Desc | Number (Uri)
125 this.modelName = modelDetails.getModelName();
126 this.modelDescription = modelDetails.getModelDescription();
127 this.modelNumber = modelDetails.getModelNumber();
128 deviceLog += "\n| MODEL : " + modelName + " | " + modelDescription + " | " + modelNumber
129 + " (" + modelDetails.getModelURI() + ")";
132 this.friendlyName = details.getFriendlyName();
133 deviceLog += "\n| FRIENDLY NAME: " + friendlyName;
134 this.serialNumber = details.getSerialNumber();
135 deviceLog += "\n| SERIAL # : " + serialNumber;
136 deviceLog += "\n| BASE URL : " + details.getBaseURL();
137 deviceLog += "\n| UPC : " + details.getUpc();
140 deviceLog += "\n+==================================================";
141 logger.debug(deviceLog);
146 public Set<ThingTypeUID> getSupportedThingTypeUIDs() {
147 return Set.of(OpenWebNetBindingConstants.THING_TYPE_BUS_GATEWAY);
151 public @Nullable DiscoveryResult createResult(RemoteDevice device) {
152 logger.debug("Found device {}", device.getType());
153 DeviceInfo devInfo = new DeviceInfo(device);
154 if (!devInfo.manufacturer.matches("<unknown>")) {
155 logger.debug(" |- {} ({})", devInfo.modelName, devInfo.manufacturer);
157 ThingUID thingId = generateThingUID(devInfo);
158 if (thingId != null) {
159 String host = devInfo.host;
161 String label = "BUS Gateway";
162 String fn = devInfo.friendlyName;
168 label = label + " (" + devInfo.modelName + ", " + devInfo.modelNumber + ", " + devInfo.host + ")";
169 Map<String, Object> properties = new HashMap<>(4);
170 properties.put(OpenWebNetBindingConstants.CONFIG_PROPERTY_HOST, host);
171 properties.put(OpenWebNetBindingConstants.PROPERTY_FIRMWARE_VERSION, devInfo.modelNumber);
172 properties.put(OpenWebNetBindingConstants.PROPERTY_MODEL, devInfo.modelName);
173 properties.put(OpenWebNetBindingConstants.PROPERTY_SERIAL_NO, devInfo.serialNumber);
174 DiscoveryResult result = DiscoveryResultBuilder.create(thingId).withProperties(properties)
175 .withRepresentationProperty(OpenWebNetBindingConstants.PROPERTY_SERIAL_NO).withLabel(label)
177 UDN udn = devInfo.udn;
180 udnStr = udn.getIdentifierString();
184 logger.info("Created a DiscoveryResult for gateway '{}' (UDN={})", devInfo.friendlyName, udnStr);
187 logger.warn("Could not get host for device (UDN={})", devInfo.udn);
196 public @Nullable ThingUID getThingUID(RemoteDevice device) {
197 return generateThingUID(new DeviceInfo(device));
201 * Returns a ThingUID for supported devices from already extracted DeviceInfo
203 * @param devInfo the device info
204 * @return a new ThingUID, or null if the device is not supported by the binding
206 private @Nullable ThingUID generateThingUID(DeviceInfo devInfo) {
207 if (devInfo.isBTicino) {
208 UDN udn = devInfo.udn;
209 String idString = null;
211 idString = udn.getIdentifierString();
212 if (idString != null) {
213 String[] spl = idString.split("-");
214 if (spl.length > 3) {
215 BusGatewayId gwId = BusGatewayId.fromValue(spl[1]);
217 logger.debug("'{}' is a supported gateway", gwId);
218 String mac = spl[3]; // extract MAC address
219 String normalizedMac = mac.toLowerCase().replaceAll("[^a-f0-9]", "");
220 if (!normalizedMac.isEmpty()) {
221 return new ThingUID(OpenWebNetBindingConstants.THING_TYPE_BUS_GATEWAY,
222 gwId.getThingId() + "_" + normalizedMac);
228 logger.info("Found BTicino device: not an OpenWebNet gateway or not supported (UDN={})", idString);