]> git.basschouten.com Git - openhab-addons.git/blob
7ddaea238c722e628d6a02796420630cf9409580
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 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.nikohomecontrol.internal.discovery;
14
15 import static org.openhab.binding.nikohomecontrol.internal.NikoHomeControlBindingConstants.*;
16
17 import java.time.Instant;
18 import java.util.Map;
19 import java.util.concurrent.ScheduledFuture;
20 import java.util.concurrent.TimeUnit;
21
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;
38
39 /**
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.
42  *
43  * @author Mark Herwege - Initial Contribution
44  */
45 @Component(scope = ServiceScope.PROTOTYPE, service = NikoHomeControlDiscoveryService.class)
46 @NonNullByDefault
47 public class NikoHomeControlDiscoveryService
48         extends AbstractThingHandlerDiscoveryService<NikoHomeControlBridgeHandler> {
49     private final Logger logger = LoggerFactory.getLogger(NikoHomeControlDiscoveryService.class);
50
51     private volatile @Nullable ScheduledFuture<?> nhcDiscoveryJob;
52
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;
57
58     private @Nullable ThingUID bridgeUID;
59
60     public NikoHomeControlDiscoveryService() {
61         super(NikoHomeControlBridgeHandler.class, SUPPORTED_THING_TYPES_UIDS, TIMEOUT_S, true);
62         logger.debug("device discovery service started");
63     }
64
65     @Override
66     public void dispose() {
67         super.dispose();
68         removeOlderResults(Instant.now().toEpochMilli());
69     }
70
71     /**
72      * Discovers devices connected to a Niko Home Control controller
73      */
74     public void discoverDevices() {
75         NikoHomeControlCommunication nhcComm = thingHandler.getCommunication();
76
77         if ((nhcComm == null) || !nhcComm.communicationActive()) {
78             logger.warn("not connected");
79             return;
80         }
81         logger.debug("getting devices on {}", thingHandler.getThing().getUID().getId());
82
83         discoverActionDevices(thingHandler, nhcComm);
84         discoverThermostatDevices(thingHandler, nhcComm);
85         discoverMeterDevices(thingHandler, nhcComm);
86         discoverAccessDevices(thingHandler, nhcComm);
87     }
88
89     private void discoverActionDevices(NikoHomeControlBridgeHandler bridgeHandler,
90             NikoHomeControlCommunication nhcComm) {
91         Map<String, NhcAction> actions = nhcComm.getActions();
92
93         actions.forEach((deviceId, nhcAction) -> {
94             String thingName = nhcAction.getName();
95             String thingLocation = nhcAction.getLocation();
96
97             switch (nhcAction.getType()) {
98                 case TRIGGER:
99                     addDevice(new ThingUID(THING_TYPE_PUSHBUTTON, bridgeHandler.getThing().getUID(), deviceId),
100                             CONFIG_ACTION_ID, deviceId, thingName, thingLocation);
101                     break;
102                 case RELAY:
103                     addDevice(new ThingUID(THING_TYPE_ON_OFF_LIGHT, bridgeHandler.getThing().getUID(), deviceId),
104                             CONFIG_ACTION_ID, deviceId, thingName, thingLocation);
105                     break;
106                 case DIMMER:
107                     addDevice(new ThingUID(THING_TYPE_DIMMABLE_LIGHT, bridgeHandler.getThing().getUID(), deviceId),
108                             CONFIG_ACTION_ID, deviceId, thingName, thingLocation);
109                     break;
110                 case ROLLERSHUTTER:
111                     addDevice(new ThingUID(THING_TYPE_BLIND, bridgeHandler.getThing().getUID(), deviceId),
112                             CONFIG_ACTION_ID, deviceId, thingName, thingLocation);
113                     break;
114                 default:
115                     logger.debug("unrecognized action type {} for {} {}", nhcAction.getType(), deviceId, thingName);
116             }
117         });
118     }
119
120     private void discoverThermostatDevices(NikoHomeControlBridgeHandler bridgeHandler,
121             NikoHomeControlCommunication nhcComm) {
122         Map<String, NhcThermostat> thermostats = nhcComm.getThermostats();
123
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);
129         });
130     }
131
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
137             return;
138         }
139
140         Map<String, NhcMeter> meters = nhcComm.getMeters();
141
142         meters.forEach((deviceId, nhcMeter) -> {
143             String thingName = nhcMeter.getName();
144             String thingLocation = nhcMeter.getLocation();
145
146             switch (nhcMeter.getType()) {
147                 case ENERGY_LIVE:
148                     addDevice(new ThingUID(THING_TYPE_ENERGYMETER_LIVE, bridgeHandler.getThing().getUID(), deviceId),
149                             METER_ID, deviceId, thingName, thingLocation);
150                     break;
151                 case ENERGY:
152                     addDevice(new ThingUID(THING_TYPE_ENERGYMETER, bridgeHandler.getThing().getUID(), deviceId),
153                             METER_ID, deviceId, thingName, thingLocation);
154                     break;
155                 case GAS:
156                     addDevice(new ThingUID(THING_TYPE_GASMETER, bridgeHandler.getThing().getUID(), deviceId), METER_ID,
157                             deviceId, thingName, thingLocation);
158                     break;
159                 case WATER:
160                     addDevice(new ThingUID(THING_TYPE_WATERMETER, bridgeHandler.getThing().getUID(), deviceId),
161                             METER_ID, deviceId, thingName, thingLocation);
162                     break;
163                 default:
164                     logger.debug("unrecognized meter type {} for {} {}", nhcMeter.getType(), deviceId, thingName);
165             }
166         });
167     }
168
169     private void discoverAccessDevices(NikoHomeControlBridgeHandler bridgeHandler,
170             NikoHomeControlCommunication nhcComm) {
171         Map<String, NhcAccess> accessDevices = nhcComm.getAccessDevices();
172
173         accessDevices.forEach((deviceId, nhcAccess) -> {
174             String thingName = nhcAccess.getName();
175             String thingLocation = nhcAccess.getLocation();
176
177             switch (nhcAccess.getType()) {
178                 case BASE:
179                 case BELLBUTTON:
180                     addDevice(new ThingUID(THING_TYPE_ACCESS, bridgeHandler.getThing().getUID(), deviceId),
181                             CONFIG_ACCESS_ID, deviceId, thingName, thingLocation);
182                     break;
183                 case RINGANDCOMEIN:
184                     addDevice(
185                             new ThingUID(THING_TYPE_ACCESS_RINGANDCOMEIN, bridgeHandler.getThing().getUID(), deviceId),
186                             CONFIG_ACCESS_ID, deviceId, thingName, thingLocation);
187                     break;
188                 default:
189                     logger.debug("unrecognized access type {} for {} {}", nhcAccess.getType(), deviceId, thingName);
190             }
191         });
192     }
193
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);
200         }
201         thingDiscovered(discoveryResultBuilder.build());
202     }
203
204     @Override
205     protected void startScan() {
206         discoverDevices();
207     }
208
209     @Override
210     protected synchronized void stopScan() {
211         super.stopScan();
212         removeOlderResults(getTimestampOfLastScan());
213     }
214
215     @Override
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);
222         }
223     }
224
225     @Override
226     protected void stopBackgroundDiscovery() {
227         logger.debug("Stop device background discovery");
228         ScheduledFuture<?> job = nhcDiscoveryJob;
229         if (job != null && !job.isCancelled()) {
230             job.cancel(true);
231             nhcDiscoveryJob = null;
232         }
233     }
234
235     @Override
236     public void initialize() {
237         bridgeUID = thingHandler.getThing().getUID();
238         super.initialize();
239     }
240 }