2 * Copyright (c) 2010-2021 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.velux.internal.discovery;
15 import static org.openhab.binding.velux.internal.VeluxBindingConstants.*;
17 import java.util.HashSet;
19 import java.util.concurrent.ScheduledFuture;
20 import java.util.concurrent.TimeUnit;
22 import org.eclipse.jdt.annotation.NonNullByDefault;
23 import org.eclipse.jdt.annotation.Nullable;
24 import org.openhab.binding.velux.internal.VeluxBindingConstants;
25 import org.openhab.binding.velux.internal.VeluxBindingProperties;
26 import org.openhab.binding.velux.internal.config.VeluxBridgeConfiguration;
27 import org.openhab.binding.velux.internal.handler.VeluxBridgeHandler;
28 import org.openhab.binding.velux.internal.things.VeluxProduct;
29 import org.openhab.binding.velux.internal.things.VeluxProductSerialNo;
30 import org.openhab.binding.velux.internal.things.VeluxScene;
31 import org.openhab.binding.velux.internal.utils.Localization;
32 import org.openhab.binding.velux.internal.utils.ManifestInformation;
33 import org.openhab.core.config.discovery.AbstractDiscoveryService;
34 import org.openhab.core.config.discovery.DiscoveryResult;
35 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
36 import org.openhab.core.config.discovery.DiscoveryService;
37 import org.openhab.core.i18n.LocaleProvider;
38 import org.openhab.core.i18n.LocationProvider;
39 import org.openhab.core.i18n.TranslationProvider;
40 import org.openhab.core.thing.ThingTypeUID;
41 import org.openhab.core.thing.ThingUID;
42 import org.osgi.service.component.annotations.Component;
43 import org.osgi.service.component.annotations.Reference;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
48 * The {@link VeluxDiscoveryService} is responsible for discovering actuators and scenes on the current Velux Bridge.
50 * @author Guenther Schreiner - Initial contribution.
53 @Component(service = DiscoveryService.class, configurationPid = "discovery.velux")
54 public class VeluxDiscoveryService extends AbstractDiscoveryService implements Runnable {
55 private final Logger logger = LoggerFactory.getLogger(VeluxDiscoveryService.class);
59 private static final int DISCOVER_TIMEOUT_SECONDS = 60;
61 private @NonNullByDefault({}) LocaleProvider localeProvider;
62 private @NonNullByDefault({}) TranslationProvider i18nProvider;
63 private Localization localization = Localization.UNKNOWN;
64 private final Set<VeluxBridgeHandler> bridgeHandlers = new HashSet<>();
67 private ScheduledFuture<?> backgroundTask = null;
71 @SuppressWarnings("PMD.CompareObjectsWithEquals")
72 private void updateLocalization() {
73 if (localization == Localization.UNKNOWN && localeProvider != null && i18nProvider != null) {
74 logger.trace("updateLocalization(): creating Localization based on locale={},translation={}).",
75 localeProvider, i18nProvider);
76 localization = new Localization(localeProvider, i18nProvider);
83 * Initializes the {@link VeluxDiscoveryService} without any further information.
85 public VeluxDiscoveryService() {
86 super(VeluxBindingConstants.DISCOVERABLE_THINGS, DISCOVER_TIMEOUT_SECONDS);
87 logger.trace("VeluxDiscoveryService(without Bridge) just initialized.");
91 protected void setLocaleProvider(final LocaleProvider givenLocaleProvider) {
92 logger.trace("setLocaleProvider(): provided locale={}.", givenLocaleProvider);
93 localeProvider = givenLocaleProvider;
98 protected void setTranslationProvider(TranslationProvider givenI18nProvider) {
99 logger.trace("setTranslationProvider(): provided translation={}.", givenI18nProvider);
100 i18nProvider = givenI18nProvider;
101 updateLocalization();
107 * Initializes the {@link VeluxDiscoveryService} with a reference to the well-prepared environment with a
108 * {@link VeluxBridgeHandler}.
110 * @param localizationHandler Initialized localization handler.
112 public VeluxDiscoveryService(Localization localizationHandler) {
113 super(VeluxBindingConstants.DISCOVERABLE_THINGS, DISCOVER_TIMEOUT_SECONDS);
114 logger.trace("VeluxDiscoveryService(locale={},i18n={}) just initialized.", localeProvider, i18nProvider);
115 localization = localizationHandler;
121 * Initializes the {@link VeluxDiscoveryService} with a reference to the well-prepared environment with a
122 * {@link VeluxBridgeHandler}.
124 * @param locationProvider Provider for a location.
125 * @param localeProvider Provider for a locale.
126 * @param i18nProvider Provider for the internationalization.
128 public VeluxDiscoveryService(LocationProvider locationProvider, LocaleProvider localeProvider,
129 TranslationProvider i18nProvider) {
130 this(new Localization(localeProvider, i18nProvider));
131 logger.trace("VeluxDiscoveryService(locale={},i18n={}) finished.", localeProvider, i18nProvider);
135 public void deactivate() {
136 logger.trace("deactivate() called.");
141 protected synchronized void startScan() {
142 logger.trace("startScan() called.");
144 logger.debug("startScan(): creating a thing of type binding.");
145 ThingUID thingUID = new ThingUID(THING_TYPE_BINDING, "org_openhab_binding_velux");
146 DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID)
147 .withProperty(VeluxBindingProperties.PROPERTY_BINDING_BUNDLEVERSION,
148 ManifestInformation.getBundleVersion())
149 .withRepresentationProperty(VeluxBindingProperties.PROPERTY_BINDING_BUNDLEVERSION)
150 .withLabel(localization.getText("discovery.velux.binding...label")).build();
151 logger.debug("startScan(): registering new thing {}.", discoveryResult);
152 thingDiscovered(discoveryResult);
154 scheduler.execute(() -> {
158 if (bridgeHandlers.isEmpty()) {
159 logger.debug("startScan(): VeluxDiscoveryService cannot proceed due to missing Velux bridge(s).");
161 logger.debug("startScan(): Starting Velux discovery scan for scenes and actuators.");
165 logger.trace("startScan() done.");
169 public synchronized void stopScan() {
170 logger.trace("stopScan() called.");
172 logger.trace("stopScan() done.");
177 logger.trace("run() called.");
181 * Discover the gateway-defined scenes.
183 private void discoverScenes() {
184 logger.trace("discoverScenes() called.");
185 for (VeluxBridgeHandler bridgeHandlerX : bridgeHandlers) {
186 ThingUID bridgeUID = bridgeHandlerX.getThing().getUID();
187 logger.debug("discoverScenes(): discovering all scenes on bridge {}.", bridgeUID);
188 for (VeluxScene scene : bridgeHandlerX.existingScenes().values()) {
189 String sceneName = scene.getName().toString();
190 logger.trace("discoverScenes(): found scene {}.", sceneName);
192 String label = sceneName.replaceAll("\\P{Alnum}", "_");
193 logger.trace("discoverScenes(): using label {}.", label);
195 ThingTypeUID thingTypeUID = THING_TYPE_VELUX_SCENE;
196 ThingUID thingUID = new ThingUID(thingTypeUID, bridgeUID, label);
197 DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withThingType(thingTypeUID)
198 .withProperty(VeluxBindingProperties.PROPERTY_SCENE_NAME, sceneName)
199 .withRepresentationProperty(VeluxBindingProperties.PROPERTY_SCENE_NAME).withBridge(bridgeUID)
200 .withLabel(label).build();
201 logger.debug("discoverScenes(): registering new thing {}.", discoveryResult);
202 thingDiscovered(discoveryResult);
205 logger.trace("discoverScenes() finished.");
209 * Discover the gateway-defined products/actuators.
211 private void discoverProducts() {
212 logger.trace("discoverProducts() called.");
213 for (VeluxBridgeHandler bridgeHandlerX : bridgeHandlers) {
214 ThingUID bridgeUID = bridgeHandlerX.getThing().getUID();
215 logger.debug("discoverProducts(): discovering all actuators on bridge {}.", bridgeUID);
216 for (VeluxProduct product : bridgeHandlerX.existingProducts().values()) {
217 String serialNumber = product.getSerialNumber();
218 String actuatorName = product.getProductName().toString();
219 logger.trace("discoverProducts() found actuator {} (name {}).", serialNumber, actuatorName);
221 if (serialNumber.equals(VeluxProductSerialNo.UNKNOWN)) {
222 identifier = actuatorName;
224 identifier = serialNumber;
226 String label = actuatorName.replaceAll("\\P{Alnum}", "_");
227 logger.trace("discoverProducts(): using label {}.", label);
228 ThingTypeUID thingTypeUID;
229 boolean isInverted = false;
230 logger.trace("discoverProducts() dealing with {} (type {}).", product, product.getProductType());
231 switch (product.getProductType()) {
233 logger.trace("discoverProducts(): creating window.");
234 thingTypeUID = THING_TYPE_VELUX_WINDOW;
239 logger.trace("discoverProducts(): creating rollershutter.");
240 thingTypeUID = THING_TYPE_VELUX_ROLLERSHUTTER;
244 logger.trace("discoverProducts(): creating actuator.");
245 thingTypeUID = THING_TYPE_VELUX_ACTUATOR;
247 ThingUID thingUID = new ThingUID(thingTypeUID, bridgeUID, label);
249 DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withThingType(thingTypeUID)
250 .withProperty(VeluxBindingProperties.CONFIG_ACTUATOR_SERIALNUMBER, identifier)
251 .withProperty(VeluxBindingProperties.PROPERTY_ACTUATOR_NAME, actuatorName)
252 .withProperty(VeluxBindingProperties.PROPERTY_ACTUATOR_INVERTED, isInverted)
253 .withRepresentationProperty(VeluxBindingProperties.CONFIG_ACTUATOR_SERIALNUMBER)
254 .withBridge(bridgeUID).withLabel(actuatorName).build();
255 logger.debug("discoverProducts(): registering new thing {}.", discoveryResult);
256 thingDiscovered(discoveryResult);
259 logger.trace("discoverProducts() finished.");
263 * Add a {@link VeluxBridgeHandler} to the {@link VeluxDiscoveryService}
265 * @param bridge Velux bridge handler.
266 * @return true if the bridge was added, or false if it was already present
268 public boolean addBridge(VeluxBridgeHandler bridge) {
269 if (!bridgeHandlers.contains(bridge)) {
270 logger.trace("VeluxDiscoveryService(): registering bridge {} for discovery.", bridge);
271 bridgeHandlers.add(bridge);
274 logger.trace("VeluxDiscoveryService(): bridge {} already registered for discovery.", bridge);
279 * Remove a {@link VeluxBridgeHandler} from the {@link VeluxDiscoveryService}
281 * @param bridge Velux bridge handler.
282 * @return true if the bridge was removed, or false if it was not present
284 public boolean removeBridge(VeluxBridgeHandler bridge) {
285 return bridgeHandlers.remove(bridge);
289 * Check if the {@link VeluxDiscoveryService} list of {@link VeluxBridgeHandler} is empty
291 * @return true if empty
293 public boolean isEmpty() {
294 return bridgeHandlers.isEmpty();
298 * Discover any bridges on the network that are not yet instantiated.
300 private void discoverBridges() {
301 // discover the list of IP addresses of bridges on the network
302 Set<String> foundBridgeIpAddresses = VeluxBridgeFinder.discoverIpAddresses(scheduler);
303 // publish discovery results
304 for (String ipAddr : foundBridgeIpAddresses) {
305 ThingUID thingUID = new ThingUID(THING_TYPE_BRIDGE, ipAddr.replace(".", "_"));
306 DiscoveryResult result = DiscoveryResultBuilder.create(thingUID).withThingType(THING_TYPE_BRIDGE)
307 .withProperty(VeluxBridgeConfiguration.BRIDGE_IPADDRESS, ipAddr)
308 .withRepresentationProperty(VeluxBridgeConfiguration.BRIDGE_IPADDRESS)
309 .withLabel(String.format("Velux Bridge (%s)", ipAddr)).build();
310 thingDiscovered(result);
315 protected void startBackgroundDiscovery() {
316 logger.trace("startBackgroundDiscovery() called.");
317 ScheduledFuture<?> task = this.backgroundTask;
318 if (task == null || task.isCancelled()) {
319 this.backgroundTask = scheduler.scheduleWithFixedDelay(this::startScan, 10, 600, TimeUnit.SECONDS);
324 protected void stopBackgroundDiscovery() {
325 logger.trace("stopBackgroundDiscovery() called.");
326 ScheduledFuture<?> task = this.backgroundTask;