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.handler.NikoHomeControlBridgeHandler2;
26 import org.openhab.binding.nikohomecontrol.internal.protocol.NhcAccess;
27 import org.openhab.binding.nikohomecontrol.internal.protocol.NhcAction;
28 import org.openhab.binding.nikohomecontrol.internal.protocol.NhcMeter;
29 import org.openhab.binding.nikohomecontrol.internal.protocol.NhcThermostat;
30 import org.openhab.binding.nikohomecontrol.internal.protocol.NikoHomeControlCommunication;
31 import org.openhab.core.config.discovery.AbstractThingHandlerDiscoveryService;
32 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
33 import org.openhab.core.thing.ThingUID;
34 import org.osgi.service.component.annotations.Component;
35 import org.osgi.service.component.annotations.ServiceScope;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
40 * If a Niko Home Control bridge is added or if the user scans manually for things this
41 * {@link NikoHomeControlDiscoveryService} is used to return Niko Home Control Actions as things to the framework.
43 * @author Mark Herwege - Initial Contribution
45 @Component(scope = ServiceScope.PROTOTYPE, service = NikoHomeControlDiscoveryService.class)
47 public class NikoHomeControlDiscoveryService
48 extends AbstractThingHandlerDiscoveryService<NikoHomeControlBridgeHandler> {
49 private final Logger logger = LoggerFactory.getLogger(NikoHomeControlDiscoveryService.class);
51 private volatile @Nullable ScheduledFuture<?> nhcDiscoveryJob;
53 private static final int TIMEOUT_S = 5;
54 private static final int INITIAL_DELAY_S = 5; // initial delay for polling to allow time for initial request to NHC
55 // controller to complete
56 private static final int REFRESH_INTERVAL_S = 60;
58 private @Nullable ThingUID bridgeUID;
60 public NikoHomeControlDiscoveryService() {
61 super(NikoHomeControlBridgeHandler.class, SUPPORTED_THING_TYPES_UIDS, TIMEOUT_S, true);
62 logger.debug("device discovery service started");
66 public void dispose() {
68 removeOlderResults(Instant.now().toEpochMilli());
72 * Discovers devices connected to a Niko Home Control controller
74 public void discoverDevices() {
75 NikoHomeControlCommunication nhcComm = thingHandler.getCommunication();
77 if ((nhcComm == null) || !nhcComm.communicationActive()) {
78 logger.warn("not connected");
81 logger.debug("getting devices on {}", thingHandler.getThing().getUID().getId());
83 discoverActionDevices(thingHandler, nhcComm);
84 discoverThermostatDevices(thingHandler, nhcComm);
85 discoverMeterDevices(thingHandler, nhcComm);
86 discoverAccessDevices(thingHandler, nhcComm);
89 private void discoverActionDevices(NikoHomeControlBridgeHandler bridgeHandler,
90 NikoHomeControlCommunication nhcComm) {
91 Map<String, NhcAction> actions = nhcComm.getActions();
93 actions.forEach((deviceId, nhcAction) -> {
94 String thingName = nhcAction.getName();
95 String thingLocation = nhcAction.getLocation();
97 switch (nhcAction.getType()) {
99 addDevice(new ThingUID(THING_TYPE_PUSHBUTTON, bridgeHandler.getThing().getUID(), deviceId),
100 CONFIG_ACTION_ID, deviceId, thingName, thingLocation);
103 addDevice(new ThingUID(THING_TYPE_ON_OFF_LIGHT, bridgeHandler.getThing().getUID(), deviceId),
104 CONFIG_ACTION_ID, deviceId, thingName, thingLocation);
107 addDevice(new ThingUID(THING_TYPE_DIMMABLE_LIGHT, bridgeHandler.getThing().getUID(), deviceId),
108 CONFIG_ACTION_ID, deviceId, thingName, thingLocation);
111 addDevice(new ThingUID(THING_TYPE_BLIND, bridgeHandler.getThing().getUID(), deviceId),
112 CONFIG_ACTION_ID, deviceId, thingName, thingLocation);
115 logger.debug("unrecognized action type {} for {} {}", nhcAction.getType(), deviceId, thingName);
120 private void discoverThermostatDevices(NikoHomeControlBridgeHandler bridgeHandler,
121 NikoHomeControlCommunication nhcComm) {
122 Map<String, NhcThermostat> thermostats = nhcComm.getThermostats();
124 thermostats.forEach((deviceId, nhcThermostat) -> {
125 String thingName = nhcThermostat.getName();
126 String thingLocation = nhcThermostat.getLocation();
127 addDevice(new ThingUID(THING_TYPE_THERMOSTAT, bridgeHandler.getThing().getUID(), deviceId),
128 CONFIG_THERMOSTAT_ID, deviceId, thingName, thingLocation);
132 private void discoverMeterDevices(NikoHomeControlBridgeHandler bridgeHandler,
133 NikoHomeControlCommunication nhcComm) {
134 if (bridgeHandler instanceof NikoHomeControlBridgeHandler2) {
135 // disable discovery of NHC II energy meters to avoid overload in Niko Home Control cloud, can be removed
136 // when Niko solves their issue with the controller sending all live power data to their cloud
140 Map<String, NhcMeter> meters = nhcComm.getMeters();
142 meters.forEach((deviceId, nhcMeter) -> {
143 String thingName = nhcMeter.getName();
144 String thingLocation = nhcMeter.getLocation();
146 switch (nhcMeter.getType()) {
148 addDevice(new ThingUID(THING_TYPE_ENERGYMETER_LIVE, bridgeHandler.getThing().getUID(), deviceId),
149 METER_ID, deviceId, thingName, thingLocation);
152 addDevice(new ThingUID(THING_TYPE_ENERGYMETER, bridgeHandler.getThing().getUID(), deviceId),
153 METER_ID, deviceId, thingName, thingLocation);
156 addDevice(new ThingUID(THING_TYPE_GASMETER, bridgeHandler.getThing().getUID(), deviceId), METER_ID,
157 deviceId, thingName, thingLocation);
160 addDevice(new ThingUID(THING_TYPE_WATERMETER, bridgeHandler.getThing().getUID(), deviceId),
161 METER_ID, deviceId, thingName, thingLocation);
164 logger.debug("unrecognized meter type {} for {} {}", nhcMeter.getType(), deviceId, thingName);
169 private void discoverAccessDevices(NikoHomeControlBridgeHandler bridgeHandler,
170 NikoHomeControlCommunication nhcComm) {
171 Map<String, NhcAccess> accessDevices = nhcComm.getAccessDevices();
173 accessDevices.forEach((deviceId, nhcAccess) -> {
174 String thingName = nhcAccess.getName();
175 String thingLocation = nhcAccess.getLocation();
177 switch (nhcAccess.getType()) {
180 addDevice(new ThingUID(THING_TYPE_ACCESS, bridgeHandler.getThing().getUID(), deviceId),
181 CONFIG_ACCESS_ID, deviceId, thingName, thingLocation);
185 new ThingUID(THING_TYPE_ACCESS_RINGANDCOMEIN, bridgeHandler.getThing().getUID(), deviceId),
186 CONFIG_ACCESS_ID, deviceId, thingName, thingLocation);
189 logger.debug("unrecognized access type {} for {} {}", nhcAccess.getType(), deviceId, thingName);
194 private void addDevice(ThingUID uid, String deviceIdKey, String deviceId, String thingName,
195 @Nullable String thingLocation) {
196 DiscoveryResultBuilder discoveryResultBuilder = DiscoveryResultBuilder.create(uid).withBridge(bridgeUID)
197 .withLabel(thingName).withProperty(deviceIdKey, deviceId).withRepresentationProperty(deviceIdKey);
198 if (thingLocation != null) {
199 discoveryResultBuilder.withProperty("Location", thingLocation);
201 thingDiscovered(discoveryResultBuilder.build());
205 protected void startScan() {
210 protected synchronized void stopScan() {
212 removeOlderResults(getTimestampOfLastScan());
216 protected void startBackgroundDiscovery() {
217 logger.debug("Start device background discovery");
218 ScheduledFuture<?> job = nhcDiscoveryJob;
219 if (job == null || job.isCancelled()) {
220 nhcDiscoveryJob = scheduler.scheduleWithFixedDelay(this::discoverDevices, INITIAL_DELAY_S,
221 REFRESH_INTERVAL_S, TimeUnit.SECONDS);
226 protected void stopBackgroundDiscovery() {
227 logger.debug("Stop device background discovery");
228 ScheduledFuture<?> job = nhcDiscoveryJob;
229 if (job != null && !job.isCancelled()) {
231 nhcDiscoveryJob = null;
236 public void initialize() {
237 bridgeUID = thingHandler.getThing().getUID();