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.nikohomecontrol.internal.discovery;
15 import static org.openhab.binding.nikohomecontrol.internal.NikoHomeControlBindingConstants.*;
17 import java.time.Instant;
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.nikohomecontrol.internal.handler.NikoHomeControlBridgeHandler;
25 import org.openhab.binding.nikohomecontrol.internal.protocol.NhcAction;
26 import org.openhab.binding.nikohomecontrol.internal.protocol.NhcEnergyMeter;
27 import org.openhab.binding.nikohomecontrol.internal.protocol.NhcThermostat;
28 import org.openhab.binding.nikohomecontrol.internal.protocol.NikoHomeControlCommunication;
29 import org.openhab.core.config.discovery.AbstractThingHandlerDiscoveryService;
30 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
31 import org.openhab.core.thing.ThingUID;
32 import org.osgi.service.component.annotations.Component;
33 import org.osgi.service.component.annotations.ServiceScope;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
38 * If a Niko Home Control bridge is added or if the user scans manually for things this
39 * {@link NikoHomeControlDiscoveryService} is used to return Niko Home Control Actions as things to the framework.
41 * @author Mark Herwege - Initial Contribution
43 @Component(scope = ServiceScope.PROTOTYPE, service = NikoHomeControlDiscoveryService.class)
45 public class NikoHomeControlDiscoveryService
46 extends AbstractThingHandlerDiscoveryService<NikoHomeControlBridgeHandler> {
47 private final Logger logger = LoggerFactory.getLogger(NikoHomeControlDiscoveryService.class);
49 private volatile @Nullable ScheduledFuture<?> nhcDiscoveryJob;
51 private static final int TIMEOUT_S = 5;
52 private static final int INITIAL_DELAY_S = 5; // initial delay for polling to allow time for initial request to NHC
53 // controller to complete
54 private static final int REFRESH_INTERVAL_S = 60;
56 private @Nullable ThingUID bridgeUID;
58 public NikoHomeControlDiscoveryService() {
59 super(NikoHomeControlBridgeHandler.class, SUPPORTED_THING_TYPES_UIDS, TIMEOUT_S, true);
60 logger.debug("device discovery service started");
64 public void dispose() {
66 removeOlderResults(Instant.now().toEpochMilli());
70 * Discovers devices connected to a Niko Home Control controller
72 public void discoverDevices() {
73 NikoHomeControlCommunication nhcComm = thingHandler.getCommunication();
75 if ((nhcComm == null) || !nhcComm.communicationActive()) {
76 logger.warn("not connected");
79 logger.debug("getting devices on {}", thingHandler.getThing().getUID().getId());
81 Map<String, NhcAction> actions = nhcComm.getActions();
83 actions.forEach((actionId, nhcAction) -> {
84 String thingName = nhcAction.getName();
85 String thingLocation = nhcAction.getLocation();
87 switch (nhcAction.getType()) {
89 addActionDevice(new ThingUID(THING_TYPE_PUSHBUTTON, thingHandler.getThing().getUID(), actionId),
90 actionId, thingName, thingLocation);
93 addActionDevice(new ThingUID(THING_TYPE_ON_OFF_LIGHT, thingHandler.getThing().getUID(), actionId),
94 actionId, thingName, thingLocation);
97 addActionDevice(new ThingUID(THING_TYPE_DIMMABLE_LIGHT, thingHandler.getThing().getUID(), actionId),
98 actionId, thingName, thingLocation);
101 addActionDevice(new ThingUID(THING_TYPE_BLIND, thingHandler.getThing().getUID(), actionId),
102 actionId, thingName, thingLocation);
105 logger.debug("unrecognized action type {} for {} {}", nhcAction.getType(), actionId, thingName);
109 Map<String, NhcThermostat> thermostats = nhcComm.getThermostats();
111 thermostats.forEach((thermostatId, nhcThermostat) -> {
112 String thingName = nhcThermostat.getName();
113 String thingLocation = nhcThermostat.getLocation();
114 addThermostatDevice(new ThingUID(THING_TYPE_THERMOSTAT, thingHandler.getThing().getUID(), thermostatId),
115 thermostatId, thingName, thingLocation);
118 Map<String, NhcEnergyMeter> energyMeters = nhcComm.getEnergyMeters();
120 energyMeters.forEach((energyMeterId, nhcEnergyMeter) -> {
121 String thingName = nhcEnergyMeter.getName();
122 String thingLocation = nhcEnergyMeter.getLocation();
123 addEnergyMeterDevice(new ThingUID(THING_TYPE_ENERGYMETER, thingHandler.getThing().getUID(), energyMeterId),
124 energyMeterId, thingName, thingLocation);
128 private void addActionDevice(ThingUID uid, String actionId, String thingName, @Nullable String thingLocation) {
129 DiscoveryResultBuilder discoveryResultBuilder = DiscoveryResultBuilder.create(uid).withBridge(bridgeUID)
130 .withLabel(thingName).withProperty(CONFIG_ACTION_ID, actionId)
131 .withRepresentationProperty(CONFIG_ACTION_ID);
132 if (thingLocation != null) {
133 discoveryResultBuilder.withProperty("Location", thingLocation);
135 thingDiscovered(discoveryResultBuilder.build());
138 private void addThermostatDevice(ThingUID uid, String thermostatId, String thingName,
139 @Nullable String thingLocation) {
140 DiscoveryResultBuilder discoveryResultBuilder = DiscoveryResultBuilder.create(uid).withBridge(bridgeUID)
141 .withLabel(thingName).withProperty(CONFIG_THERMOSTAT_ID, thermostatId)
142 .withRepresentationProperty(CONFIG_THERMOSTAT_ID);
143 if (thingLocation != null) {
144 discoveryResultBuilder.withProperty("Location", thingLocation);
146 thingDiscovered(discoveryResultBuilder.build());
149 private void addEnergyMeterDevice(ThingUID uid, String energyMeterId, String thingName,
150 @Nullable String thingLocation) {
151 DiscoveryResultBuilder discoveryResultBuilder = DiscoveryResultBuilder.create(uid).withBridge(bridgeUID)
152 .withLabel(thingName).withProperty(CONFIG_ENERGYMETER_ID, energyMeterId)
153 .withRepresentationProperty(CONFIG_ENERGYMETER_ID);
154 if (thingLocation != null) {
155 discoveryResultBuilder.withProperty("Location", thingLocation);
157 thingDiscovered(discoveryResultBuilder.build());
161 protected void startScan() {
166 protected synchronized void stopScan() {
168 removeOlderResults(getTimestampOfLastScan());
172 protected void startBackgroundDiscovery() {
173 logger.debug("Start device background discovery");
174 ScheduledFuture<?> job = nhcDiscoveryJob;
175 if (job == null || job.isCancelled()) {
176 nhcDiscoveryJob = scheduler.scheduleWithFixedDelay(this::discoverDevices, INITIAL_DELAY_S,
177 REFRESH_INTERVAL_S, TimeUnit.SECONDS);
182 protected void stopBackgroundDiscovery() {
183 logger.debug("Stop device background discovery");
184 ScheduledFuture<?> job = nhcDiscoveryJob;
185 if (job != null && !job.isCancelled()) {
187 nhcDiscoveryJob = null;
192 public void initialize() {
193 bridgeUID = thingHandler.getThing().getUID();