]> git.basschouten.com Git - openhab-addons.git/blob
16be29817b3f884193a77119f02f71716aa857dd
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 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.wlanthermo.internal;
14
15 import java.net.URI;
16 import java.net.URISyntaxException;
17 import java.util.concurrent.*;
18
19 import org.eclipse.jdt.annotation.NonNullByDefault;
20 import org.eclipse.jdt.annotation.Nullable;
21 import org.eclipse.jetty.client.HttpClient;
22 import org.eclipse.jetty.client.api.Authentication;
23 import org.eclipse.jetty.client.api.AuthenticationStore;
24 import org.eclipse.jetty.client.util.DigestAuthentication;
25 import org.eclipse.jetty.client.util.StringContentProvider;
26 import org.openhab.binding.wlanthermo.internal.api.nano.WlanThermoNanoCommandHandler;
27 import org.openhab.binding.wlanthermo.internal.api.nano.data.Data;
28 import org.openhab.binding.wlanthermo.internal.api.nano.settings.Settings;
29 import org.openhab.core.common.ThreadPoolManager;
30 import org.openhab.core.thing.*;
31 import org.openhab.core.thing.binding.BaseThingHandler;
32 import org.openhab.core.types.Command;
33 import org.openhab.core.types.RefreshType;
34 import org.openhab.core.types.State;
35 import org.openhab.core.types.UnDefType;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39 import com.google.gson.Gson;
40
41 /**
42  * The {@link WlanThermoNanoHandler} is responsible for handling commands, which are
43  * sent to one of the channels.
44  *
45  * @author Christian Schlipp - Initial contribution
46  */
47 @NonNullByDefault
48 public class WlanThermoNanoHandler extends BaseThingHandler {
49
50     private final Logger logger = LoggerFactory.getLogger(WlanThermoNanoHandler.class);
51
52     private WlanThermoNanoConfiguration config = new WlanThermoNanoConfiguration();
53     private WlanThermoNanoCommandHandler wlanThermoNanoCommandHandler = new WlanThermoNanoCommandHandler();
54     private final HttpClient httpClient;
55     private @Nullable ScheduledFuture<?> pollingScheduler;
56     private final ScheduledExecutorService scheduler = ThreadPoolManager
57             .getScheduledPool(WlanThermoBindingConstants.WLANTHERMO_THREAD_POOL);
58     private final Gson gson = new Gson();
59     private Data data = new Data();
60     private Settings settings = new Settings();
61
62     public WlanThermoNanoHandler(Thing thing, HttpClient httpClient) {
63         super(thing);
64         this.httpClient = httpClient;
65     }
66
67     @Override
68     public void initialize() {
69         logger.debug("Start initializing WlanThermo Nano!");
70         config = getConfigAs(WlanThermoNanoConfiguration.class);
71
72         updateStatus(ThingStatus.UNKNOWN);
73         try {
74             if (config.getUsername() != null && !config.getUsername().isEmpty() && config.getPassword() != null
75                     && !config.getPassword().isEmpty()) {
76                 AuthenticationStore authStore = httpClient.getAuthenticationStore();
77                 authStore.addAuthentication(new DigestAuthentication(config.getUri(), Authentication.ANY_REALM,
78                         config.getUsername(), config.getPassword()));
79             }
80             scheduler.schedule(this::checkConnection, config.getPollingInterval(), TimeUnit.SECONDS);
81
82             logger.debug("Finished initializing WlanThermo Nano!");
83         } catch (URISyntaxException e) {
84             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
85                     "Failed to initialize WlanThermo Nano!");
86             logger.debug("Failed to initialize WlanThermo Nano!", e);
87         }
88     }
89
90     private void checkConnection() {
91         try {
92             if (httpClient.GET(config.getUri()).getStatus() == 200) {
93                 updateStatus(ThingStatus.ONLINE);
94                 ScheduledFuture<?> oldScheduler = pollingScheduler;
95                 if (oldScheduler != null) {
96                     oldScheduler.cancel(false);
97                 }
98                 pollingScheduler = scheduler.scheduleWithFixedDelay(this::update, 0, config.getPollingInterval(),
99                         TimeUnit.SECONDS);
100             } else {
101                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
102                         "WlanThermo not found under given address.");
103             }
104         } catch (URISyntaxException | InterruptedException | ExecutionException | TimeoutException e) {
105             logger.debug("Failed to connect.", e);
106             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
107                     "Could not connect to WlanThermo at " + config.getIpAddress());
108             ScheduledFuture<?> oldScheduler = pollingScheduler;
109             if (oldScheduler != null) {
110                 oldScheduler.cancel(false);
111             }
112             pollingScheduler = scheduler.schedule(this::checkConnection, config.getPollingInterval(), TimeUnit.SECONDS);
113         }
114     }
115
116     @Override
117     public void handleCommand(ChannelUID channelUID, Command command) {
118         if (command instanceof RefreshType) {
119             State s = wlanThermoNanoCommandHandler.getState(channelUID, data, settings);
120             if (s != null)
121                 updateState(channelUID, s);
122         } else {
123             if (wlanThermoNanoCommandHandler.setState(channelUID, command, data)) {
124                 logger.debug("Data updated, pushing changes");
125                 push();
126             } else {
127                 logger.debug("Could not handle command of type {} for channel {}!",
128                         command.getClass().toGenericString(), channelUID.getId());
129             }
130         }
131     }
132
133     private void update() {
134         try {
135             // Update objects with data from device
136             String json = httpClient.GET(config.getUri("/data")).getContentAsString();
137             data = gson.fromJson(json, Data.class);
138             logger.debug("Received at /data: {}", json);
139             json = httpClient.GET(config.getUri("/settings")).getContentAsString();
140             settings = gson.fromJson(json, Settings.class);
141             logger.debug("Received at /settings: {}", json);
142
143             // Update channels
144             for (Channel channel : thing.getChannels()) {
145                 State state = wlanThermoNanoCommandHandler.getState(channel.getUID(), data, settings);
146                 if (state != null) {
147                     updateState(channel.getUID(), state);
148                 } else {
149                     // if we could not obtain a state, try trigger instead
150                     String trigger = wlanThermoNanoCommandHandler.getTrigger(channel.getUID(), data);
151                     if (trigger != null) {
152                         triggerChannel(channel.getUID(), trigger);
153                     }
154                 }
155             }
156         } catch (URISyntaxException | InterruptedException | ExecutionException | TimeoutException e) {
157             logger.debug("Update failed, checking connection", e);
158             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Update failed, reconnecting...");
159             ScheduledFuture<?> oldScheduler = pollingScheduler;
160             if (oldScheduler != null) {
161                 oldScheduler.cancel(false);
162             }
163             for (Channel channel : thing.getChannels()) {
164                 updateState(channel.getUID(), UnDefType.UNDEF);
165             }
166             checkConnection();
167         }
168     }
169
170     private void push() {
171         data.getChannel().forEach(c -> {
172             try {
173                 String json = gson.toJson(c);
174                 logger.debug("Pushing: {}", json);
175                 URI uri = config.getUri("/setchannels");
176                 int status = httpClient.POST(uri).content(new StringContentProvider(json), "application/json")
177                         .timeout(5, TimeUnit.SECONDS).send().getStatus();
178                 if (status == 401) {
179                     updateStatus(ThingStatus.ONLINE, ThingStatusDetail.COMMUNICATION_ERROR,
180                             "No or wrong login credentials provided. Please configure username/password for write access to WlanThermo!");
181                 } else if (status != 200) {
182                     updateStatus(ThingStatus.ONLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Failed to update channel "
183                             + c.getName() + " on device, Statuscode " + status + " on URI " + uri.toString());
184                 } else {
185                     updateStatus(ThingStatus.ONLINE);
186                 }
187             } catch (InterruptedException | TimeoutException | ExecutionException | URISyntaxException e) {
188                 updateStatus(ThingStatus.ONLINE, ThingStatusDetail.COMMUNICATION_ERROR,
189                         "Failed to update channel " + c.getName() + " on device!");
190                 logger.debug("Failed to update channel {} on device", c.getName(), e);
191             }
192         });
193     }
194
195     @Override
196     public void dispose() {
197         ScheduledFuture<?> oldScheduler = pollingScheduler;
198         if (oldScheduler != null) {
199             boolean stopped = oldScheduler.cancel(true);
200             logger.debug("Stopped polling: {}", stopped);
201         }
202         pollingScheduler = null;
203     }
204 }