]> git.basschouten.com Git - openhab-addons.git/blob
d16613c1577c4e86f4a7c5430a65ca32318c3508
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.openwebnet.internal.discovery;
14
15 import java.util.Arrays;
16 import java.util.HashMap;
17 import java.util.Map;
18 import java.util.Optional;
19 import java.util.Set;
20
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;
38
39 /**
40  * The {@link BusGatewayUpnpDiscovery} is responsible for discovering supported BTicino BUS
41  * gateways devices using UPnP. It implements {@link UpnpDiscoveryParticipant}.
42  *
43  * @author Massimo Valla - Initial contribution
44  */
45 @NonNullByDefault
46 @Component(service = UpnpDiscoveryParticipant.class)
47 public class BusGatewayUpnpDiscovery implements UpnpDiscoveryParticipant {
48
49     private final Logger logger = LoggerFactory.getLogger(BusGatewayUpnpDiscovery.class);
50
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");
59
60         private final String discoveryString, thingId;
61
62         private BusGatewayId(String value, String thingId) {
63             this.discoveryString = value;
64             this.thingId = thingId;
65         }
66
67         public static @Nullable BusGatewayId fromValue(String s) {
68             Optional<BusGatewayId> m = Arrays.stream(values()).filter(val -> s.equals(val.discoveryString)).findFirst();
69             if (m.isPresent()) {
70                 return m.get();
71             } else {
72                 return null;
73             }
74         }
75
76         public String getThingId() {
77             return thingId;
78         }
79     }
80
81     /**
82      * DeviceInfo bean to store device useful info (and log them)
83      */
84     public class DeviceInfo {
85         @Nullable
86         private String friendlyName;
87         private String modelName = "<unknown>";
88         private String modelDescription = "<unknown>";
89         private String modelNumber = "<unknown>";
90         private String serialNumber = "<unknown>";
91         @Nullable
92         private String host;
93         private String manufacturer = "<unknown>";
94         @Nullable
95         private UDN udn;
96         private boolean isBTicino = false;
97
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();
107                 }
108                 deviceLog += "\n| ID.MAX AGE : " + identity.getMaxAgeSeconds();
109             }
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()
117                             + ")";
118                     if (manufacturer.toUpperCase().contains("BTICINO")) {
119                         this.isBTicino = true;
120                     }
121                 }
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() + ")";
130                 }
131                 if (isBTicino) {
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();
138                 }
139             }
140             deviceLog += "\n+==================================================";
141             logger.debug(deviceLog);
142         }
143     } /* DeviceInfo */
144
145     @Override
146     public Set<ThingTypeUID> getSupportedThingTypeUIDs() {
147         return Set.of(OpenWebNetBindingConstants.THING_TYPE_BUS_GATEWAY);
148     }
149
150     @Override
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);
156         }
157         ThingUID thingId = generateThingUID(devInfo);
158         if (thingId != null) {
159             String host = devInfo.host;
160             if (host != null) {
161                 String label = "BUS Gateway";
162                 String fn = devInfo.friendlyName;
163                 if (fn != null) {
164                     if (!fn.isEmpty()) {
165                         label = fn;
166                     }
167                 }
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)
176                         .build();
177                 UDN udn = devInfo.udn;
178                 String udnStr;
179                 if (udn != null) {
180                     udnStr = udn.getIdentifierString();
181                 } else {
182                     udnStr = null;
183                 }
184                 logger.info("Created a DiscoveryResult for gateway '{}' (UDN={})", devInfo.friendlyName, udnStr);
185                 return result;
186             } else {
187                 logger.warn("Could not get host for device (UDN={})", devInfo.udn);
188                 return null;
189             }
190         } else {
191             return null;
192         }
193     }
194
195     @Override
196     public @Nullable ThingUID getThingUID(RemoteDevice device) {
197         return generateThingUID(new DeviceInfo(device));
198     }
199
200     /**
201      * Returns a ThingUID for supported devices from already extracted DeviceInfo
202      *
203      * @param devInfo the device info
204      * @return a new ThingUID, or null if the device is not supported by the binding
205      */
206     private @Nullable ThingUID generateThingUID(DeviceInfo devInfo) {
207         if (devInfo.isBTicino) {
208             UDN udn = devInfo.udn;
209             String idString = null;
210             if (udn != 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]);
216                         if (gwId != null) {
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);
223                             }
224                         }
225                     }
226                 }
227             }
228             logger.info("Found BTicino device: not an OpenWebNet gateway or not supported (UDN={})", idString);
229         }
230         return null;
231     }
232 }