]> git.basschouten.com Git - openhab-addons.git/blob
4eadd57ae1cdaa0207233dfded8c9ba3a0416e27
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 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.velux.internal.discovery;
14
15 import static org.openhab.binding.velux.internal.VeluxBindingConstants.*;
16
17 import java.util.HashSet;
18 import java.util.Set;
19
20 import org.eclipse.jdt.annotation.NonNullByDefault;
21 import org.openhab.binding.velux.internal.VeluxBindingConstants;
22 import org.openhab.binding.velux.internal.VeluxBindingProperties;
23 import org.openhab.binding.velux.internal.config.VeluxBridgeConfiguration;
24 import org.openhab.binding.velux.internal.handler.VeluxBridgeHandler;
25 import org.openhab.binding.velux.internal.things.VeluxProduct;
26 import org.openhab.binding.velux.internal.things.VeluxProductSerialNo;
27 import org.openhab.binding.velux.internal.things.VeluxScene;
28 import org.openhab.binding.velux.internal.utils.Localization;
29 import org.openhab.binding.velux.internal.utils.ManifestInformation;
30 import org.openhab.core.config.discovery.AbstractDiscoveryService;
31 import org.openhab.core.config.discovery.DiscoveryResult;
32 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
33 import org.openhab.core.config.discovery.DiscoveryService;
34 import org.openhab.core.i18n.LocaleProvider;
35 import org.openhab.core.i18n.LocationProvider;
36 import org.openhab.core.i18n.TranslationProvider;
37 import org.openhab.core.thing.ThingTypeUID;
38 import org.openhab.core.thing.ThingUID;
39 import org.osgi.service.component.annotations.Component;
40 import org.osgi.service.component.annotations.Reference;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43
44 /**
45  * The {@link VeluxDiscoveryService} is responsible for discovering actuators and scenes on the current Velux Bridge.
46  *
47  * @author Guenther Schreiner - Initial contribution.
48  */
49 @NonNullByDefault
50 @Component(service = DiscoveryService.class, configurationPid = "discovery.velux")
51 public class VeluxDiscoveryService extends AbstractDiscoveryService implements Runnable {
52     private final Logger logger = LoggerFactory.getLogger(VeluxDiscoveryService.class);
53
54     // Class internal
55
56     private static final int DISCOVER_TIMEOUT_SECONDS = 60;
57
58     private @NonNullByDefault({}) LocaleProvider localeProvider;
59     private @NonNullByDefault({}) TranslationProvider i18nProvider;
60     private Localization localization = Localization.UNKNOWN;
61     private final Set<VeluxBridgeHandler> bridgeHandlers = new HashSet<>();
62
63     // Private
64
65     private void updateLocalization() {
66         if (localization == Localization.UNKNOWN && localeProvider != null && i18nProvider != null) {
67             logger.trace("updateLocalization(): creating Localization based on locale={},translation={}).",
68                     localeProvider, i18nProvider);
69             localization = new Localization(localeProvider, i18nProvider);
70         }
71     }
72
73     /**
74      * Constructor
75      * <P>
76      * Initializes the {@link VeluxDiscoveryService} without any further information.
77      */
78     public VeluxDiscoveryService() {
79         super(VeluxBindingConstants.DISCOVERABLE_THINGS, DISCOVER_TIMEOUT_SECONDS);
80         logger.trace("VeluxDiscoveryService(without Bridge) just initialized.");
81     }
82
83     @Reference
84     protected void setLocaleProvider(final LocaleProvider givenLocaleProvider) {
85         logger.trace("setLocaleProvider(): provided locale={}.", givenLocaleProvider);
86         localeProvider = givenLocaleProvider;
87         updateLocalization();
88     }
89
90     @Reference
91     protected void setTranslationProvider(TranslationProvider givenI18nProvider) {
92         logger.trace("setTranslationProvider(): provided translation={}.", givenI18nProvider);
93         i18nProvider = givenI18nProvider;
94         updateLocalization();
95     }
96
97     /**
98      * Constructor
99      * <P>
100      * Initializes the {@link VeluxDiscoveryService} with a reference to the well-prepared environment with a
101      * {@link VeluxBridgeHandler}.
102      *
103      * @param localizationHandler Initialized localization handler.
104      */
105     public VeluxDiscoveryService(Localization localizationHandler) {
106         super(VeluxBindingConstants.DISCOVERABLE_THINGS, DISCOVER_TIMEOUT_SECONDS);
107         logger.trace("VeluxDiscoveryService(locale={},i18n={}) just initialized.", localeProvider, i18nProvider);
108         localization = localizationHandler;
109     }
110
111     /**
112      * Constructor
113      * <P>
114      * Initializes the {@link VeluxDiscoveryService} with a reference to the well-prepared environment with a
115      * {@link VeluxBridgeHandler}.
116      *
117      * @param locationProvider Provider for a location.
118      * @param localeProvider Provider for a locale.
119      * @param i18nProvider Provider for the internationalization.
120      */
121     public VeluxDiscoveryService(LocationProvider locationProvider, LocaleProvider localeProvider,
122             TranslationProvider i18nProvider) {
123         this(new Localization(localeProvider, i18nProvider));
124         logger.trace("VeluxDiscoveryService(locale={},i18n={}) finished.", localeProvider, i18nProvider);
125     }
126
127     @Override
128     public void deactivate() {
129         logger.trace("deactivate() called.");
130         super.deactivate();
131     }
132
133     @Override
134     protected synchronized void startScan() {
135         logger.trace("startScan() called.");
136
137         logger.debug("startScan(): creating a thing of type binding.");
138         ThingUID thingUID = new ThingUID(THING_TYPE_BINDING, "org_openhab_binding_velux");
139         DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID)
140                 .withProperty(VeluxBindingProperties.PROPERTY_BINDING_BUNDLEVERSION,
141                         ManifestInformation.getBundleVersion())
142                 .withRepresentationProperty(VeluxBindingProperties.PROPERTY_BINDING_BUNDLEVERSION)
143                 .withLabel(localization.getText("discovery.velux.binding...label")).build();
144         logger.debug("startScan(): registering new thing {}.", discoveryResult);
145         thingDiscovered(discoveryResult);
146
147         scheduler.execute(() -> {
148             discoverBridges();
149         });
150
151         if (bridgeHandlers.isEmpty()) {
152             logger.debug("startScan(): VeluxDiscoveryService cannot proceed due to missing Velux bridge(s).");
153         } else {
154             logger.debug("startScan(): Starting Velux discovery scan for scenes and actuators.");
155             discoverScenes();
156             discoverProducts();
157         }
158         logger.trace("startScan() done.");
159     }
160
161     @Override
162     public synchronized void stopScan() {
163         logger.trace("stopScan() called.");
164         super.stopScan();
165         logger.trace("stopScan() done.");
166     }
167
168     @Override
169     public void run() {
170         logger.trace("run() called.");
171     }
172
173     /**
174      * Discover the gateway-defined scenes.
175      */
176     private void discoverScenes() {
177         logger.trace("discoverScenes() called.");
178         for (VeluxBridgeHandler bridgeHandlerX : bridgeHandlers) {
179             ThingUID bridgeUID = bridgeHandlerX.getThing().getUID();
180             logger.debug("discoverScenes(): discovering all scenes on bridge {}.", bridgeUID);
181             for (VeluxScene scene : bridgeHandlerX.existingScenes().values()) {
182                 String sceneName = scene.getName().toString();
183                 logger.trace("discoverScenes(): found scene {}.", sceneName);
184
185                 String label = sceneName.replaceAll("\\P{Alnum}", "_");
186                 logger.trace("discoverScenes(): using label {}.", label);
187
188                 ThingTypeUID thingTypeUID = THING_TYPE_VELUX_SCENE;
189                 ThingUID thingUID = new ThingUID(thingTypeUID, bridgeUID, label);
190                 DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withThingType(thingTypeUID)
191                         .withProperty(VeluxBindingProperties.PROPERTY_SCENE_NAME, sceneName)
192                         .withRepresentationProperty(VeluxBindingProperties.PROPERTY_SCENE_NAME).withBridge(bridgeUID)
193                         .withLabel(label).build();
194                 logger.debug("discoverScenes(): registering new thing {}.", discoveryResult);
195                 thingDiscovered(discoveryResult);
196             }
197         }
198         logger.trace("discoverScenes() finished.");
199     }
200
201     /**
202      * Discover the gateway-defined products/actuators.
203      */
204     private void discoverProducts() {
205         logger.trace("discoverProducts() called.");
206         for (VeluxBridgeHandler bridgeHandlerX : bridgeHandlers) {
207             ThingUID bridgeUID = bridgeHandlerX.getThing().getUID();
208             logger.debug("discoverProducts(): discovering all actuators on bridge {}.", bridgeUID);
209             for (VeluxProduct product : bridgeHandlerX.existingProducts().values()) {
210                 String serialNumber = product.getSerialNumber();
211                 String actuatorName = product.getProductName().toString();
212                 logger.trace("discoverProducts() found actuator {} (name {}).", serialNumber, actuatorName);
213                 String identifier;
214                 if (serialNumber.equals(VeluxProductSerialNo.UNKNOWN)) {
215                     identifier = actuatorName;
216                 } else {
217                     identifier = serialNumber;
218                 }
219                 String label = actuatorName.replaceAll("\\P{Alnum}", "_");
220                 logger.trace("discoverProducts(): using label {}.", label);
221                 ThingTypeUID thingTypeUID;
222                 boolean isInverted = false;
223                 logger.trace("discoverProducts() dealing with {} (type {}).", product, product.getProductType());
224                 switch (product.getProductType()) {
225                     case SLIDER_WINDOW:
226                         logger.trace("discoverProducts(): creating window.");
227                         thingTypeUID = THING_TYPE_VELUX_WINDOW;
228                         isInverted = true;
229                         break;
230
231                     case SLIDER_SHUTTER:
232                         logger.trace("discoverProducts(): creating rollershutter.");
233                         thingTypeUID = THING_TYPE_VELUX_ROLLERSHUTTER;
234                         break;
235
236                     default:
237                         logger.trace("discoverProducts(): creating actuator.");
238                         thingTypeUID = THING_TYPE_VELUX_ACTUATOR;
239                 }
240                 ThingUID thingUID = new ThingUID(thingTypeUID, bridgeUID, label);
241
242                 DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withThingType(thingTypeUID)
243                         .withProperty(VeluxBindingProperties.CONFIG_ACTUATOR_SERIALNUMBER, identifier)
244                         .withProperty(VeluxBindingProperties.PROPERTY_ACTUATOR_NAME, actuatorName)
245                         .withProperty(VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED, isInverted)
246                         .withRepresentationProperty(VeluxBindingProperties.CONFIG_ACTUATOR_SERIALNUMBER)
247                         .withBridge(bridgeUID).withLabel(actuatorName).build();
248                 logger.debug("discoverProducts(): registering new thing {}.", discoveryResult);
249                 thingDiscovered(discoveryResult);
250             }
251         }
252         logger.trace("discoverProducts() finished.");
253     }
254
255     /**
256      * Add a {@link VeluxBridgeHandler} to the {@link VeluxDiscoveryService}
257      *
258      * @param bridge Velux bridge handler.
259      * @return true if the bridge was added, or false if it was already present
260      */
261     public boolean addBridge(VeluxBridgeHandler bridge) {
262         if (!bridgeHandlers.contains(bridge)) {
263             logger.trace("VeluxDiscoveryService(): registering bridge {} for discovery.", bridge);
264             bridgeHandlers.add(bridge);
265             return true;
266         }
267         logger.trace("VeluxDiscoveryService(): bridge {} already registered for discovery.", bridge);
268         return false;
269     }
270
271     /**
272      * Remove a {@link VeluxBridgeHandler} from the {@link VeluxDiscoveryService}
273      *
274      * @param bridge Velux bridge handler.
275      * @return true if the bridge was removed, or false if it was not present
276      */
277     public boolean removeBridge(VeluxBridgeHandler bridge) {
278         return bridgeHandlers.remove(bridge);
279     }
280
281     /**
282      * Check if the {@link VeluxDiscoveryService} list of {@link VeluxBridgeHandler} is empty
283      *
284      * @return true if empty
285      */
286     public boolean isEmpty() {
287         return bridgeHandlers.isEmpty();
288     }
289
290     /**
291      * Discover any bridges on the network that are not yet instantiated.
292      */
293     private void discoverBridges() {
294         // discover the list of IP addresses of bridges on the network
295         Set<String> foundBridgeIpAddresses = VeluxBridgeFinder.discoverIpAddresses(scheduler);
296         // publish discovery results
297         for (String ipAddr : foundBridgeIpAddresses) {
298             ThingUID thingUID = new ThingUID(THING_TYPE_BRIDGE, ipAddr.replace(".", "_"));
299             DiscoveryResult result = DiscoveryResultBuilder.create(thingUID).withThingType(THING_TYPE_BRIDGE)
300                     .withProperty(VeluxBridgeConfiguration.BRIDGE_IPADDRESS, ipAddr)
301                     .withRepresentationProperty(VeluxBridgeConfiguration.BRIDGE_IPADDRESS)
302                     .withLabel(String.format("Velux Bridge (%s)", ipAddr)).build();
303             thingDiscovered(result);
304         }
305     }
306 }