]> git.basschouten.com Git - openhab-addons.git/blob
276c75fb0acd4ccedbbe536805b1e101195eaec1
[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.fsinternetradio.internal;
14
15 import static org.openhab.binding.fsinternetradio.internal.FSInternetRadioBindingConstants.*;
16
17 import java.util.HashMap;
18 import java.util.HashSet;
19 import java.util.Map;
20 import java.util.Set;
21
22 import org.jupnp.model.meta.DeviceDetails;
23 import org.jupnp.model.meta.ManufacturerDetails;
24 import org.jupnp.model.meta.ModelDetails;
25 import org.jupnp.model.meta.RemoteDevice;
26 import org.jupnp.model.meta.RemoteDeviceIdentity;
27 import org.openhab.core.config.discovery.DiscoveryResult;
28 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
29 import org.openhab.core.config.discovery.upnp.UpnpDiscoveryParticipant;
30 import org.openhab.core.thing.ThingTypeUID;
31 import org.openhab.core.thing.ThingUID;
32 import org.osgi.service.component.annotations.Component;
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
35
36 /**
37  * This is the discovery service for internet radios based on the fontier silicon chipset. Unfortunately, it is not
38  * easily possible to detect from the upnp information which devices are supported. So currently, discovery only works
39  * for medion internet radios. {@link FSInternetRadioDiscoveryParticipant#getThingUID(RemoteDevice)} must be extended to
40  * add further supported devices!
41  *
42  * @author Patrick Koenemann - Initial contribution
43  * @author Mihaela Memova - removed the getLabel(RemoteDevice device) method due to its unreachable code lines
44  * @author Markus Michels - support for Teufel 3sixty discovery
45  */
46 @Component
47 public class FSInternetRadioDiscoveryParticipant implements UpnpDiscoveryParticipant {
48     private final Logger logger = LoggerFactory.getLogger(FSInternetRadioDiscoveryParticipant.class);
49
50     /** Map from UPnP manufacturer to model number for supported radios; filled in static initializer below. */
51     private static final Map<String, Set<String>> SUPPORTED_RADIO_MODELS = new HashMap<>();
52
53     static {
54         // to allow case-insensitive match: add all values UPPER-CASE!
55
56         // format: MANUFACTURER -> MODEL NAME, as shown e.g. by UPnP Tester as explained here:
57         // https://community.openhab.org/t/internet-radio-i-need-your-help/2131
58
59         // list of medion internet radios taken from: http://internetradio.medion.com/
60         final Set<String> medionRadios = new HashSet<>();
61         SUPPORTED_RADIO_MODELS.put("MEDION AG", medionRadios);
62         medionRadios.add("MD83813");
63         medionRadios.add("MD84017");
64         medionRadios.add("MD85651");
65         medionRadios.add("MD86062");
66         medionRadios.add("MD86250");
67         medionRadios.add("MD86562");
68         medionRadios.add("MD86672");
69         medionRadios.add("MD86698");
70         medionRadios.add("MD86869");
71         medionRadios.add("MD86891");
72         medionRadios.add("MD86955");
73         medionRadios.add("MD86988");
74         medionRadios.add("MD87090");
75         medionRadios.add("MD87180");
76         medionRadios.add("MD87238");
77         medionRadios.add("MD87267");
78
79         // list of hama internet radios taken from:
80         // https://www.hama.com/action/searchCtrl/search?searchMode=1&q=Internet%20Radio
81         final Set<String> hamaRadios = new HashSet<>();
82         SUPPORTED_RADIO_MODELS.put("HAMA", hamaRadios);
83         hamaRadios.add("IR100");
84         hamaRadios.add("IR110");
85         hamaRadios.add("IR250");
86         hamaRadios.add("IR320");
87         hamaRadios.add("DIR3000");
88         hamaRadios.add("DIR3100");
89         hamaRadios.add("DIR3110");
90
91         // as reported in: https://community.openhab.org/t/internet-radio-i-need-your-help/2131/19
92         // and: https://community.openhab.org/t/internet-radio-i-need-your-help/2131/20
93         // and: https://community.openhab.org/t/internet-radio-i-need-your-help/2131/23
94         // these radios do not provide model number, but the model name should also be ok
95         final Set<String> radiosWithoutManufacturer = new HashSet<>();
96         radiosWithoutManufacturer.add(""); // empty manufacturer / model name
97         radiosWithoutManufacturer.add(null); // missing manufacturer / model name
98         SUPPORTED_RADIO_MODELS.put("SMRS18A1", radiosWithoutManufacturer);
99         SUPPORTED_RADIO_MODELS.put("SMRS30A1", radiosWithoutManufacturer);
100         SUPPORTED_RADIO_MODELS.put("SMRS35A1", radiosWithoutManufacturer);
101
102         final Set<String> teufelRadios = new HashSet<>();
103         SUPPORTED_RADIO_MODELS.put("Teufel", teufelRadios);
104         teufelRadios.add("Radio 3sixty");
105
106         // as reported in: https://community.openhab.org/t/internet-radio-i-need-your-help/2131/5
107         final Set<String> ttmicroRadios = new HashSet<>();
108         SUPPORTED_RADIO_MODELS.put("TTMICRO AS", ttmicroRadios);
109         ttmicroRadios.add("PINELL SUPERSOUND");
110
111         // as reported in: https://community.openhab.org/t/internet-radio-i-need-your-help/2131/7
112         final Set<String> revoRadios = new HashSet<>();
113         SUPPORTED_RADIO_MODELS.put("REVO TECHNOLOGIES LTD", revoRadios);
114         revoRadios.add("S10");
115
116         // as reported in: https://community.openhab.org/t/internet-radio-i-need-your-help/2131/10
117         // and: https://community.openhab.org/t/internet-radio-i-need-your-help/2131/21
118         final Set<String> robertsRadios = new HashSet<>();
119         SUPPORTED_RADIO_MODELS.put("ROBERTS RADIO LIMITED", robertsRadios);
120         robertsRadios.add("ROBERTS STREAM 93I");
121         robertsRadios.add("ROBERTS STREAM 83I");
122
123         // as reported in: https://community.openhab.org/t/internet-radio-i-need-your-help/2131/11
124         final Set<String> aunaRadios = new HashSet<>();
125         SUPPORTED_RADIO_MODELS.put("AUNA", aunaRadios);
126         aunaRadios.add("10028154 & 10028155");
127         aunaRadios.add("10028154");
128         aunaRadios.add("10028155");
129
130         // as reported in: https://community.openhab.org/t/internet-radio-i-need-your-help/2131/22
131         final Set<String> sangeanRadios = new HashSet<>();
132         SUPPORTED_RADIO_MODELS.put("SANGEAN RADIO LIMITED", sangeanRadios);
133         sangeanRadios.add("28");
134
135         // as reported in: https://community.openhab.org/t/internet-radio-i-need-your-help/2131/25
136         final Set<String> rokuRadios = new HashSet<>();
137         SUPPORTED_RADIO_MODELS.put("ROKU", rokuRadios);
138         rokuRadios.add("M1001");
139     }
140
141     @Override
142     public Set<ThingTypeUID> getSupportedThingTypeUIDs() {
143         return Set.of(THING_TYPE_RADIO);
144     }
145
146     @Override
147     public DiscoveryResult createResult(RemoteDevice device) {
148         final ThingUID uid = getThingUID(device);
149         if (uid != null) {
150             final Map<String, Object> properties = new HashMap<>(1);
151             final String ip = getIp(device);
152             if (ip != null) {
153                 properties.put(CONFIG_PROPERTY_IP, ip);
154
155                 // add manufacturer and model, if provided
156                 final String manufacturer = getManufacturer(device);
157                 if (manufacturer != null) {
158                     properties.put(PROPERTY_MANUFACTURER, manufacturer);
159                 }
160                 final String dm = getModel(device);
161                 final String model = dm != null ? dm : getFriendlyName(device);
162                 if (model != null) {
163                     properties.put(PROPERTY_MODEL, model);
164                 }
165                 final String thingName = (manufacturer == null) && (getModel(device) == null) ? getFriendlyName(device)
166                         : device.getDisplayString();
167                 return DiscoveryResultBuilder.create(uid).withProperties(properties).withLabel(thingName).build();
168             }
169         }
170         return null;
171     }
172
173     private String getManufacturer(RemoteDevice device) {
174         final DeviceDetails details = device.getDetails();
175         if ((details != null) && (details.getManufacturerDetails() != null)) {
176             String manufacturer = details.getManufacturerDetails().getManufacturer().trim();
177             return manufacturer.isEmpty() ? null : manufacturer;
178         }
179         return null;
180     }
181
182     private String getModel(RemoteDevice device) {
183         final DeviceDetails details = device.getDetails();
184         if ((details != null) && (details.getModelDetails().getModelNumber() != null)) {
185             String model = details.getModelDetails().getModelNumber().trim();
186             return model.isEmpty() ? null : model;
187         }
188         return null;
189     }
190
191     private String getFriendlyName(RemoteDevice device) {
192         final DeviceDetails details = device.getDetails();
193         if ((details != null) && (details.getFriendlyName() != null)) {
194             String name = details.getFriendlyName().trim();
195             return name.isEmpty() ? null : name;
196         }
197         return null;
198     }
199
200     private String getIp(RemoteDevice device) {
201         final DeviceDetails details = device.getDetails();
202         if (details != null) {
203             if (details.getBaseURL() != null) {
204                 return details.getBaseURL().getHost();
205             }
206         }
207         final RemoteDeviceIdentity identity = device.getIdentity();
208         if (identity != null) {
209             if (identity.getDescriptorURL() != null) {
210                 return identity.getDescriptorURL().getHost();
211             }
212         }
213         return null;
214     }
215
216     /**
217      * If <code>device</code> is a supported device, a unique thing ID (e.g. serial number) must be returned. Further
218      * supported devices should be added here, based on the available UPnP information.
219      */
220     @SuppressWarnings("null")
221     @Override
222     public ThingUID getThingUID(RemoteDevice device) {
223         final DeviceDetails details = device.getDetails();
224         final String friendlyName = details.getFriendlyName();
225         logger.debug("Discovered unit:  {}", friendlyName);
226
227         if (details != null) {
228             final ManufacturerDetails manufacturerDetails = details.getManufacturerDetails();
229             final ModelDetails modelDetails = details.getModelDetails();
230             if (modelDetails != null) {
231                 // check manufacturer and model number
232                 final String manufacturer = manufacturerDetails == null ? null : manufacturerDetails.getManufacturer();
233                 final String modelNumber = modelDetails.getModelNumber();
234                 String serialNumber = details.getSerialNumber();
235                 logger.debug("Discovered unit: {} {} - {}", manufacturer, modelNumber, friendlyName);
236                 if (modelNumber != null) {
237                     if (manufacturer != null) {
238                         final Set<String> supportedRadios = SUPPORTED_RADIO_MODELS
239                                 .get(manufacturer.trim().toUpperCase());
240                         if (supportedRadios != null && supportedRadios.contains(modelNumber.toUpperCase())) {
241                             return new ThingUID(THING_TYPE_RADIO, serialNumber);
242                         }
243                     }
244                     // check model name and number
245                     final String modelName = modelDetails.getModelName();
246                     if (modelName != null) {
247                         final Set<String> supportedRadios = SUPPORTED_RADIO_MODELS.get(modelName.trim().toUpperCase());
248                         if (supportedRadios != null && supportedRadios.contains(modelNumber.toUpperCase())) {
249                             return new ThingUID(THING_TYPE_RADIO, serialNumber);
250                         }
251                         // Teufel reports empty manufacturer and model, but friendly name
252                         if (friendlyName.contains("Teufel")) {
253                             logger.debug("haha");
254                         }
255                         if (!friendlyName.isEmpty()) {
256                             for (Set<String> models : SUPPORTED_RADIO_MODELS.values()) {
257                                 for (String model : models) {
258                                     if ((model != null) && !model.isEmpty() && friendlyName.contains(model)) {
259                                         return new ThingUID(THING_TYPE_RADIO, serialNumber);
260                                     }
261                                 }
262                             }
263                         }
264                     }
265                 }
266
267                 if (((manufacturer == null) || manufacturer.trim().isEmpty())
268                         && ((modelNumber == null) || modelNumber.trim().isEmpty())) {
269                     // Some devices report crappy UPnP device description so manufacturer and model are ""
270                     // In this case we try to find the match in friendlyName
271                     final String uname = friendlyName.toUpperCase();
272                     for (Set<String> set : SUPPORTED_RADIO_MODELS.values()) {
273                         for (String model : set) {
274                             if ((model != null) && !model.isEmpty() && uname.contains(model)) {
275                                 return new ThingUID(THING_TYPE_RADIO, serialNumber);
276                             }
277                         }
278                     }
279                 }
280             }
281             // maybe we can add further indicators, whether the device is a supported one
282         }
283         // device not supported
284         return null;
285     }
286 }