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