]> git.basschouten.com Git - openhab-addons.git/blob
27b234cc080e86c7d2195350553762a40d1d2857
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 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.shelly.internal.discovery;
14
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.*;
18
19 import java.io.IOException;
20 import java.util.Hashtable;
21 import java.util.Map;
22 import java.util.TreeMap;
23
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;
49
50 /**
51  * Device discovery creates a thing in the inbox for each vehicle
52  * found in the data received from {@link ShellyBasicDiscoveryService}.
53  *
54  * @author Markus Michels - Initial Contribution
55  *
56  */
57 @NonNullByDefault
58 public class ShellyBasicDiscoveryService extends AbstractDiscoveryService {
59     private final Logger logger = LoggerFactory.getLogger(ShellyBasicDiscoveryService.class);
60
61     private final BundleContext bundleContext;
62     private final ShellyThingTable thingTable;
63     private static final int TIMEOUT = 10;
64     private @Nullable ServiceRegistration<?> discoveryService;
65
66     public ShellyBasicDiscoveryService(BundleContext bundleContext, ShellyThingTable thingTable) {
67         super(SUPPORTED_THING_TYPES_UIDS, TIMEOUT);
68         this.bundleContext = bundleContext;
69         this.thingTable = thingTable;
70     }
71
72     public void registerDeviceDiscoveryService() {
73         if (discoveryService == null) {
74             discoveryService = bundleContext.registerService(DiscoveryService.class.getName(), this, new Hashtable<>());
75         }
76     }
77
78     @Override
79     protected void startScan() {
80         logger.debug("Starting BLU Discovery");
81         thingTable.startScan();
82     }
83
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);
93     }
94
95     public void discoveredResult(DiscoveryResult result) {
96         thingDiscovered(result);
97     }
98
99     public void unregisterDeviceDiscoveryService() {
100         if (discoveryService != null) {
101             discoveryService.unregister();
102         }
103     }
104
105     @Override
106     public void deactivate() {
107         super.deactivate();
108         unregisterDeviceDiscoveryService();
109     }
110
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;
119         String mac = "";
120         String model = "";
121         String mode = "";
122         String name = hostname;
123         String deviceName = "";
124         String thingType = "";
125         Map<String, Object> properties = new TreeMap<>();
126
127         try {
128             ShellyThingConfiguration config = fillConfig(bindingConfig, ipAddress);
129             api = gen2 ? new Shelly2ApiRpc(name, config, httpClient) : new Shelly1HttpApi(name, config, httpClient);
130             api.initialize();
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;
137             }
138             if (devInfo.name != null) {
139                 deviceName = devInfo.name;
140             }
141
142             thingType = substringBeforeLast(name, "-");
143             profile = api.getDeviceProfile(thingType, devInfo);
144             api.close();
145             deviceName = profile.name;
146             mode = devInfo.mode;
147             properties = ShellyBaseHandler.fillDeviceProperties(profile);
148
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);
156             }
157         } catch (IllegalArgumentException | IOException e) { // maybe some format description was buggy
158             logger.debug("Discovery: Unable to discover thing", e);
159         } finally {
160             if (api != null) {
161                 api.close();
162             }
163         }
164
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");
175
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();
180         }
181
182         return null;
183     }
184
185     public static ShellyThingConfiguration fillConfig(ShellyBindingConfiguration bindingConfig, String address)
186             throws IOException {
187         ShellyThingConfiguration config = new ShellyThingConfiguration();
188         config.deviceIp = address;
189         config.userId = bindingConfig.defaultUserId;
190         config.password = bindingConfig.defaultPassword;
191         return config;
192     }
193
194     private static void addProperty(Map<String, Object> properties, String key, @Nullable String value) {
195         properties.put(key, value != null ? value : "");
196     }
197 }