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.benqprojector.internal.discovery;
15 import static org.openhab.binding.benqprojector.internal.BenqProjectorBindingConstants.*;
17 import java.io.IOException;
20 import java.util.concurrent.ScheduledFuture;
21 import java.util.concurrent.TimeUnit;
23 import org.eclipse.jdt.annotation.NonNullByDefault;
24 import org.eclipse.jdt.annotation.Nullable;
25 import org.openhab.core.config.discovery.AbstractDiscoveryService;
26 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
27 import org.openhab.core.config.discovery.DiscoveryService;
28 import org.openhab.core.i18n.LocaleProvider;
29 import org.openhab.core.i18n.TranslationProvider;
30 import org.openhab.core.net.NetworkAddressService;
31 import org.openhab.core.thing.Thing;
32 import org.openhab.core.thing.ThingTypeUID;
33 import org.openhab.core.thing.ThingUID;
34 import org.osgi.framework.Bundle;
35 import org.osgi.framework.FrameworkUtil;
36 import org.osgi.service.component.annotations.Activate;
37 import org.osgi.service.component.annotations.Component;
38 import org.osgi.service.component.annotations.Reference;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
43 * The {@link BenqProjectorDiscoveryService} class implements a service
44 * for discovering BenQ projectors using the AMX Device Discovery protocol.
46 * @author Mark Hilbush - Initial contribution
47 * @author Michael Lobstein - Adapted for the BenQ Projector binding
50 @Component(service = DiscoveryService.class, configurationPid = "discovery.benqprojector")
51 public class BenqProjectorDiscoveryService extends AbstractDiscoveryService {
52 private final Logger logger = LoggerFactory.getLogger(BenqProjectorDiscoveryService.class);
53 private @Nullable ScheduledFuture<?> benqDiscoveryJob;
55 // Discovery parameters
56 public static final boolean BACKGROUND_DISCOVERY_ENABLED = true;
57 public static final int BACKGROUND_DISCOVERY_DELAY_TIMEOUT_SEC = 10;
59 private NetworkAddressService networkAddressService;
60 private final TranslationProvider translationProvider;
61 private final LocaleProvider localeProvider;
62 private final @Nullable Bundle bundle;
64 private boolean terminate = false;
67 public BenqProjectorDiscoveryService(@Reference NetworkAddressService networkAddressService,
68 @Reference TranslationProvider translationProvider, @Reference LocaleProvider localeProvider) {
69 super(SUPPORTED_THING_TYPES_UIDS, 0, BACKGROUND_DISCOVERY_ENABLED);
70 this.networkAddressService = networkAddressService;
71 this.translationProvider = translationProvider;
72 this.localeProvider = localeProvider;
73 this.bundle = FrameworkUtil.getBundle(BenqProjectorDiscoveryService.class);
75 benqDiscoveryJob = null;
80 public Set<ThingTypeUID> getSupportedThingTypes() {
81 return SUPPORTED_THING_TYPES_UIDS;
85 protected void startBackgroundDiscovery() {
86 if (benqDiscoveryJob == null) {
88 logger.debug("Starting background discovery job in {} seconds", BACKGROUND_DISCOVERY_DELAY_TIMEOUT_SEC);
89 benqDiscoveryJob = scheduler.schedule(this::discover, BACKGROUND_DISCOVERY_DELAY_TIMEOUT_SEC,
95 protected void stopBackgroundDiscovery() {
96 ScheduledFuture<?> benqDiscoveryJob = this.benqDiscoveryJob;
97 if (benqDiscoveryJob != null) {
99 benqDiscoveryJob.cancel(false);
100 this.benqDiscoveryJob = null;
105 public void startScan() {
109 public void stopScan() {
112 private synchronized void discover() {
113 logger.debug("Discovery job is running");
114 MulticastListener benqMulticastListener;
115 String local = "127.0.0.1";
118 String ip = networkAddressService.getPrimaryIpv4HostAddress();
119 benqMulticastListener = new MulticastListener((ip != null ? ip : local));
120 } catch (IOException ioe) {
121 logger.debug("Discovery job got IO exception creating multicast socket: {}", ioe.getMessage());
127 // Wait for a discovery beacon to return properties for a BenQ projector.
128 Map<String, Object> thingProperties = benqMulticastListener.waitForBeacon();
130 if (thingProperties != null) {
131 // The MulticastListener found a projector, add it as new thing
132 String uid = (String) thingProperties.get(Thing.PROPERTY_MAC_ADDRESS);
133 String ipAddress = (String) thingProperties.get(THING_PROPERTY_HOST);
136 logger.trace("Projector with UID {} discovered at IP: {}", uid, ipAddress);
138 ThingUID thingUid = new ThingUID(THING_TYPE_PROJECTOR_TCP, uid);
139 logger.trace("Creating BenQ projector discovery result for: {}, IP={}", uid, ipAddress);
141 DiscoveryResultBuilder.create(thingUid).withProperties(thingProperties)
142 .withLabel(translationProvider.getText(bundle,
143 "thing-type.benqprojector.discovery.label", "BenQ Projector",
144 localeProvider.getLocale()) + " " + uid)
145 .withRepresentationProperty(Thing.PROPERTY_MAC_ADDRESS).build());
148 } catch (IOException ioe) {
149 logger.debug("Discovery job got exception waiting for beacon: {}", ioe.getMessage());
152 benqMulticastListener.shutdown();
153 logger.debug("Discovery job is exiting");