2 * Copyright (c) 2010-2024 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.shelly.internal.discovery;
15 import static org.openhab.binding.shelly.internal.ShellyBindingConstants.*;
16 import static org.openhab.binding.shelly.internal.util.ShellyUtils.*;
17 import static org.openhab.core.thing.Thing.*;
19 import java.io.IOException;
20 import java.util.Hashtable;
22 import java.util.TreeMap;
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.eclipse.jetty.client.HttpClient;
27 import org.openhab.binding.shelly.internal.api.ShellyApiException;
28 import org.openhab.binding.shelly.internal.api.ShellyApiInterface;
29 import org.openhab.binding.shelly.internal.api.ShellyApiResult;
30 import org.openhab.binding.shelly.internal.api.ShellyDeviceProfile;
31 import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsDevice;
32 import org.openhab.binding.shelly.internal.api1.Shelly1HttpApi;
33 import org.openhab.binding.shelly.internal.api2.Shelly2ApiRpc;
34 import org.openhab.binding.shelly.internal.config.ShellyBindingConfiguration;
35 import org.openhab.binding.shelly.internal.config.ShellyThingConfiguration;
36 import org.openhab.binding.shelly.internal.handler.ShellyBaseHandler;
37 import org.openhab.binding.shelly.internal.handler.ShellyThingTable;
38 import org.openhab.binding.shelly.internal.provider.ShellyTranslationProvider;
39 import org.openhab.core.config.discovery.AbstractDiscoveryService;
40 import org.openhab.core.config.discovery.DiscoveryResult;
41 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
42 import org.openhab.core.config.discovery.DiscoveryService;
43 import org.openhab.core.thing.ThingTypeUID;
44 import org.openhab.core.thing.ThingUID;
45 import org.osgi.framework.BundleContext;
46 import org.osgi.framework.ServiceRegistration;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
51 * Device discovery creates a thing in the inbox for each vehicle
52 * found in the data received from {@link ShellyBasicDiscoveryService}.
54 * @author Markus Michels - Initial Contribution
58 public class ShellyBasicDiscoveryService extends AbstractDiscoveryService {
59 private final Logger logger = LoggerFactory.getLogger(ShellyBasicDiscoveryService.class);
61 private final BundleContext bundleContext;
62 private final ShellyThingTable thingTable;
63 private static final int TIMEOUT = 10;
64 private @Nullable ServiceRegistration<?> discoveryService;
66 public ShellyBasicDiscoveryService(BundleContext bundleContext, ShellyThingTable thingTable) {
67 super(SUPPORTED_THING_TYPES_UIDS, TIMEOUT);
68 this.bundleContext = bundleContext;
69 this.thingTable = thingTable;
72 public void registerDeviceDiscoveryService() {
73 if (discoveryService == null) {
74 discoveryService = bundleContext.registerService(DiscoveryService.class.getName(), this, new Hashtable<>());
79 protected void startScan() {
80 logger.debug("Starting BLU Discovery");
81 thingTable.startScan();
84 public void discoveredResult(ThingTypeUID tuid, String model, String serviceName, String address,
85 Map<String, Object> properties) {
86 ThingUID uid = ShellyThingCreator.getThingUID(serviceName, model, "", true);
87 logger.debug("Adding discovered thing with id {}", uid.toString());
88 properties.put(PROPERTY_MAC_ADDRESS, address);
89 String thingLabel = "Shelly BLU " + model + " (" + serviceName + ")";
90 DiscoveryResult result = DiscoveryResultBuilder.create(uid).withProperties(properties)
91 .withRepresentationProperty(PROPERTY_DEV_NAME).withLabel(thingLabel).build();
92 thingDiscovered(result);
95 public void discoveredResult(DiscoveryResult result) {
96 thingDiscovered(result);
99 public void unregisterDeviceDiscoveryService() {
100 if (discoveryService != null) {
101 discoveryService.unregister();
106 public void deactivate() {
108 unregisterDeviceDiscoveryService();
111 public static @Nullable DiscoveryResult createResult(boolean gen2, String hostname, String ipAddress,
112 ShellyBindingConfiguration bindingConfig, HttpClient httpClient, ShellyTranslationProvider messages) {
113 Logger logger = LoggerFactory.getLogger(ShellyBasicDiscoveryService.class);
114 ThingUID thingUID = null;
115 ShellyDeviceProfile profile;
116 ShellySettingsDevice devInfo;
117 ShellyApiInterface api = null;
118 boolean auth = false;
122 String name = hostname;
123 String deviceName = "";
124 String thingType = "";
125 Map<String, Object> properties = new TreeMap<>();
128 ShellyThingConfiguration config = fillConfig(bindingConfig, ipAddress);
129 api = gen2 ? new Shelly2ApiRpc(name, config, httpClient) : new Shelly1HttpApi(name, config, httpClient);
131 devInfo = api.getDeviceInfo();
132 mac = getString(devInfo.mac);
133 model = devInfo.type;
134 auth = getBool(devInfo.auth);
135 if (name.isEmpty() || name.startsWith("shellyplusrange")) {
136 name = devInfo.hostname;
138 if (devInfo.name != null) {
139 deviceName = devInfo.name;
142 thingType = substringBeforeLast(name, "-");
143 profile = api.getDeviceProfile(thingType, devInfo);
145 deviceName = profile.name;
147 properties = ShellyBaseHandler.fillDeviceProperties(profile);
149 // get thing type from device name
150 thingUID = ShellyThingCreator.getThingUID(name, model, mode, false);
151 } catch (ShellyApiException e) {
152 ShellyApiResult result = e.getApiResult();
153 if (result.isHttpAccessUnauthorized()) {
154 // create shellyunknown thing - will be changed during thing initialization with valid credentials
155 thingUID = ShellyThingCreator.getThingUID(name, model, mode, true);
157 } catch (IllegalArgumentException | IOException e) { // maybe some format description was buggy
158 logger.debug("Discovery: Unable to discover thing", e);
165 if (thingUID != null) {
166 addProperty(properties, PROPERTY_MAC_ADDRESS, mac);
167 addProperty(properties, CONFIG_DEVICEIP, ipAddress);
168 addProperty(properties, PROPERTY_MODEL_ID, model);
169 addProperty(properties, PROPERTY_SERVICE_NAME, name);
170 addProperty(properties, PROPERTY_DEV_NAME, deviceName);
171 addProperty(properties, PROPERTY_DEV_TYPE, thingType);
172 addProperty(properties, PROPERTY_DEV_GEN, gen2 ? "2" : "1");
173 addProperty(properties, PROPERTY_DEV_MODE, mode);
174 addProperty(properties, PROPERTY_DEV_AUTH, auth ? "yes" : "no");
176 String thingLabel = deviceName.isEmpty() ? name + " - " + ipAddress
177 : deviceName + " (" + name + "@" + ipAddress + ")";
178 return DiscoveryResultBuilder.create(thingUID).withProperties(properties).withLabel(thingLabel)
179 .withRepresentationProperty(PROPERTY_SERVICE_NAME).build();
185 public static ShellyThingConfiguration fillConfig(ShellyBindingConfiguration bindingConfig, String address)
187 ShellyThingConfiguration config = new ShellyThingConfiguration();
188 config.deviceIp = address;
189 config.userId = bindingConfig.defaultUserId;
190 config.password = bindingConfig.defaultPassword;
194 private static void addProperty(Map<String, Object> properties, String key, @Nullable String value) {
195 properties.put(key, value != null ? value : "");