]> git.basschouten.com Git - openhab-addons.git/blob
de19b13ce3aa2e69d12aaa5f2756deb49fa64789
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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.tapocontrol.internal.device;
14
15 import java.util.Collection;
16 import java.util.Set;
17 import java.util.concurrent.ScheduledFuture;
18 import java.util.concurrent.TimeUnit;
19
20 import org.eclipse.jdt.annotation.NonNullByDefault;
21 import org.eclipse.jdt.annotation.Nullable;
22 import org.eclipse.jetty.client.HttpClient;
23 import org.openhab.binding.tapocontrol.internal.TapoDiscoveryService;
24 import org.openhab.binding.tapocontrol.internal.api.TapoCloudConnector;
25 import org.openhab.binding.tapocontrol.internal.helpers.TapoCredentials;
26 import org.openhab.binding.tapocontrol.internal.helpers.TapoErrorHandler;
27 import org.openhab.binding.tapocontrol.internal.structures.TapoBridgeConfiguration;
28 import org.openhab.core.thing.Bridge;
29 import org.openhab.core.thing.ChannelUID;
30 import org.openhab.core.thing.Thing;
31 import org.openhab.core.thing.ThingStatus;
32 import org.openhab.core.thing.ThingStatusDetail;
33 import org.openhab.core.thing.ThingUID;
34 import org.openhab.core.thing.binding.BaseBridgeHandler;
35 import org.openhab.core.thing.binding.ThingHandlerService;
36 import org.openhab.core.types.Command;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 import com.google.gson.JsonArray;
41
42 /**
43  * The {@link TapoBridgeHandler} is responsible for handling commands, which are
44  * sent to one of the channels with a bridge.
45  *
46  * @author Christian Wild - Initial contribution
47  */
48 @NonNullByDefault
49 public class TapoBridgeHandler extends BaseBridgeHandler {
50     private final Logger logger = LoggerFactory.getLogger(TapoBridgeHandler.class);
51     private final TapoErrorHandler bridgeError = new TapoErrorHandler();
52     private TapoBridgeConfiguration config = new TapoBridgeConfiguration();
53     private final HttpClient httpClient;
54     private @Nullable ScheduledFuture<?> startupJob;
55     private @Nullable ScheduledFuture<?> pollingJob;
56     private @Nullable ScheduledFuture<?> discoveryJob;
57     private @NonNullByDefault({}) TapoCloudConnector cloudConnector;
58     private @NonNullByDefault({}) TapoDiscoveryService discoveryService;
59     private TapoCredentials credentials;
60
61     private String uid;
62
63     public TapoBridgeHandler(Bridge bridge, HttpClient httpClient) {
64         super(bridge);
65         Thing thing = getThing();
66         this.cloudConnector = new TapoCloudConnector(this, httpClient);
67         this.credentials = new TapoCredentials();
68         this.uid = thing.getUID().toString();
69         this.httpClient = httpClient;
70     }
71
72     /***********************************
73      *
74      * BRIDGE INITIALIZATION
75      *
76      ************************************/
77     @Override
78     /**
79      * INIT BRIDGE
80      * set credentials and login cloud
81      */
82     public void initialize() {
83         this.config = getConfigAs(TapoBridgeConfiguration.class);
84         this.credentials = new TapoCredentials(config.username, config.password);
85         activateBridge();
86     }
87
88     /**
89      * ACTIVATE BRIDGE
90      */
91     private void activateBridge() {
92         // set the thing status to UNKNOWN temporarily and let the background task decide for the real status.
93         updateStatus(ThingStatus.UNKNOWN);
94
95         // background initialization (delay it a little bit):
96         this.startupJob = scheduler.schedule(this::delayedStartUp, 1000, TimeUnit.MILLISECONDS);
97     }
98
99     @Override
100     public void handleCommand(ChannelUID channelUID, Command command) {
101         logger.debug("{} Bridge doesn't handle command: {}", this.uid, command);
102     }
103
104     @Override
105     public void dispose() {
106         stopScheduler(this.startupJob);
107         stopScheduler(this.pollingJob);
108         stopScheduler(this.discoveryJob);
109         super.dispose();
110     }
111
112     /**
113      * ACTIVATE DISCOVERY SERVICE
114      */
115     @Override
116     public Collection<Class<? extends ThingHandlerService>> getServices() {
117         return Set.of(TapoDiscoveryService.class);
118     }
119
120     /**
121      * Set DiscoveryService
122      * 
123      * @param discoveryService
124      */
125     public void setDiscoveryService(TapoDiscoveryService discoveryService) {
126         this.discoveryService = discoveryService;
127     }
128
129     /***********************************
130      *
131      * SCHEDULER
132      *
133      ************************************/
134
135     /**
136      * delayed OneTime StartupJob
137      */
138     private void delayedStartUp() {
139         loginCloud();
140         startCloudScheduler();
141         startDiscoveryScheduler();
142     }
143
144     /**
145      * Start CloudLogin Scheduler
146      */
147     protected void startCloudScheduler() {
148         int pollingInterval = config.reconnectInterval;
149         TimeUnit timeUnit = TimeUnit.MINUTES;
150         if (pollingInterval > 0) {
151             logger.debug("{} starting cloudScheduler with interval {} {}", this.uid, pollingInterval, timeUnit);
152
153             this.pollingJob = scheduler.scheduleWithFixedDelay(this::loginCloud, pollingInterval, pollingInterval,
154                     timeUnit);
155         } else {
156             logger.debug("({}) cloudScheduler disabled with config '0'", uid);
157             stopScheduler(this.pollingJob);
158         }
159     }
160
161     /**
162      * Start DeviceDiscovery Scheduler
163      */
164     protected void startDiscoveryScheduler() {
165         int pollingInterval = config.discoveryInterval;
166         TimeUnit timeUnit = TimeUnit.MINUTES;
167         if (config.cloudDiscovery && pollingInterval > 0) {
168             logger.debug("{} starting discoveryScheduler with interval {} {}", this.uid, pollingInterval, timeUnit);
169
170             this.discoveryJob = scheduler.scheduleWithFixedDelay(this::discoverDevices, 0, pollingInterval, timeUnit);
171         } else {
172             logger.debug("({}) discoveryScheduler disabled with config '0'", uid);
173             stopScheduler(this.discoveryJob);
174         }
175     }
176
177     /**
178      * Stop scheduler
179      * 
180      * @param scheduler ScheduledFeature<?> which schould be stopped
181      */
182     protected void stopScheduler(@Nullable ScheduledFuture<?> scheduler) {
183         if (scheduler != null) {
184             scheduler.cancel(true);
185             scheduler = null;
186         }
187     }
188
189     /***********************************
190      *
191      * ERROR HANDLER
192      *
193      ************************************/
194     /**
195      * return device Error
196      * 
197      * @return
198      */
199     public TapoErrorHandler getError() {
200         return this.bridgeError;
201     }
202
203     /**
204      * set device error
205      * 
206      * @param tapoError TapoErrorHandler-Object
207      */
208     public void setError(TapoErrorHandler tapoError) {
209         this.bridgeError.set(tapoError);
210     }
211
212     /***********************************
213      *
214      * BRIDGE COMMUNICATIONS
215      *
216      ************************************/
217
218     /**
219      * Login to Cloud
220      * 
221      * @return
222      */
223     public boolean loginCloud() {
224         bridgeError.reset(); // reset ErrorHandler
225         if (!config.username.isBlank() && !config.password.isBlank()) {
226             logger.debug("{} login with user {}", this.uid, config.username);
227             if (cloudConnector.login(config.username, config.password)) {
228                 updateStatus(ThingStatus.ONLINE);
229                 return true;
230             } else {
231                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, bridgeError.getMessage());
232             }
233         } else {
234             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "credentials not set");
235         }
236         return false;
237     }
238
239     /***********************************
240      *
241      * DEVICE DISCOVERY
242      *
243      ************************************/
244
245     /**
246      * START DEVICE DISCOVERY
247      */
248     public void discoverDevices() {
249         this.discoveryService.startScan();
250     }
251
252     /**
253      * GET DEVICELIST CONNECTED TO BRIDGE
254      * 
255      * @return devicelist
256      */
257     public JsonArray getDeviceList() {
258         JsonArray deviceList = new JsonArray();
259         if (config.cloudDiscovery) {
260             logger.trace("{} discover devicelist from cloud", this.uid);
261             deviceList = getDeviceListCloud();
262         } else {
263             logger.info("{} Discovery disabled in bridge settings ", this.uid);
264         }
265         return deviceList;
266     }
267
268     /**
269      * GET DEVICELIST FROM CLOUD
270      * returns all devices stored in cloud
271      * 
272      * @return deviceList from cloud
273      */
274     private JsonArray getDeviceListCloud() {
275         logger.trace("{} getDeviceList from cloud", this.uid);
276         bridgeError.reset(); // reset ErrorHandler
277         JsonArray deviceList = new JsonArray();
278         if (loginCloud()) {
279             deviceList = this.cloudConnector.getDeviceList();
280         }
281         return deviceList;
282     }
283
284     /***********************************
285      *
286      * BRIDGE GETTERS
287      *
288      ************************************/
289
290     public TapoCredentials getCredentials() {
291         return this.credentials;
292     }
293
294     public HttpClient getHttpClient() {
295         return this.httpClient;
296     }
297
298     public ThingUID getUID() {
299         return getThing().getUID();
300     }
301
302     public TapoBridgeConfiguration getBridgeConfig() {
303         return this.config;
304     }
305 }