]> git.basschouten.com Git - openhab-addons.git/blob
e44f15e49f16747b1dd0e69326b810cf3e12760a
[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.jablotron.internal.handler;
14
15 import static org.openhab.binding.jablotron.JablotronBindingConstants.*;
16
17 import java.util.Collection;
18 import java.util.Collections;
19 import java.util.List;
20 import java.util.concurrent.ExecutionException;
21 import java.util.concurrent.ScheduledFuture;
22 import java.util.concurrent.TimeUnit;
23 import java.util.concurrent.TimeoutException;
24
25 import org.eclipse.jdt.annotation.NonNullByDefault;
26 import org.eclipse.jdt.annotation.Nullable;
27 import org.eclipse.jetty.client.HttpClient;
28 import org.eclipse.jetty.client.api.ContentResponse;
29 import org.eclipse.jetty.client.api.Request;
30 import org.eclipse.jetty.client.util.StringContentProvider;
31 import org.eclipse.jetty.http.HttpHeader;
32 import org.eclipse.jetty.http.HttpMethod;
33 import org.openhab.binding.jablotron.internal.config.JablotronBridgeConfig;
34 import org.openhab.binding.jablotron.internal.discovery.JablotronDiscoveryService;
35 import org.openhab.binding.jablotron.internal.model.*;
36 import org.openhab.binding.jablotron.internal.model.ja100f.JablotronGetPGResponse;
37 import org.openhab.binding.jablotron.internal.model.ja100f.JablotronGetSectionsResponse;
38 import org.openhab.core.thing.*;
39 import org.openhab.core.thing.binding.BaseBridgeHandler;
40 import org.openhab.core.thing.binding.ThingHandlerService;
41 import org.openhab.core.types.Command;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44
45 import com.google.gson.Gson;
46 import com.google.gson.JsonSyntaxException;
47
48 /**
49  * The {@link JablotronBridgeHandler} is responsible for handling commands, which are
50  * sent to one of the channels.
51  *
52  * @author Ondrej Pecta - Initial contribution
53  */
54 @NonNullByDefault
55 public class JablotronBridgeHandler extends BaseBridgeHandler {
56
57     private final Logger logger = LoggerFactory.getLogger(JablotronBridgeHandler.class);
58
59     private final Gson gson = new Gson();
60
61     final HttpClient httpClient;
62
63     private @Nullable ScheduledFuture<?> future = null;
64
65     /**
66      * Our configuration
67      */
68     public JablotronBridgeConfig bridgeConfig = new JablotronBridgeConfig();
69
70     public JablotronBridgeHandler(Bridge thing, HttpClient httpClient) {
71         super(thing);
72         this.httpClient = httpClient;
73     }
74
75     @Override
76     public Collection<Class<? extends ThingHandlerService>> getServices() {
77         return Collections.singleton(JablotronDiscoveryService.class);
78     }
79
80     @Override
81     public void handleCommand(ChannelUID channelUID, Command command) {
82     }
83
84     @Override
85     public void initialize() {
86         bridgeConfig = getConfigAs(JablotronBridgeConfig.class);
87         scheduler.execute(this::login);
88         future = scheduler.scheduleWithFixedDelay(this::updateAlarmThings, 30, bridgeConfig.getRefresh(),
89                 TimeUnit.SECONDS);
90     }
91
92     private void updateAlarmThings() {
93         logger.debug("Updating overall alarm's statuses...");
94         @Nullable
95         List<JablotronDiscoveredService> services = discoverServices();
96         if (services != null) {
97             Bridge localBridge = getThing();
98             if (localBridge != null && ThingStatus.ONLINE != localBridge.getStatus()) {
99                 updateStatus(ThingStatus.ONLINE);
100             }
101             for (JablotronDiscoveredService service : services) {
102                 updateAlarmThing(service);
103             }
104         }
105     }
106
107     private void updateAlarmThing(JablotronDiscoveredService service) {
108         for (Thing th : getThing().getThings()) {
109             if (ThingStatus.ONLINE != th.getStatus()) {
110                 logger.debug("Thing {} is not online", th.getUID());
111                 continue;
112             }
113
114             JablotronAlarmHandler handler = (JablotronAlarmHandler) th.getHandler();
115
116             if (handler == null) {
117                 logger.debug("Thing handler is null");
118                 continue;
119             }
120
121             if (String.valueOf(service.getId()).equals(handler.thingConfig.getServiceId())) {
122                 if ("ENABLED".equals(service.getStatus())) {
123                     if (!service.getWarning().isEmpty()) {
124                         logger.debug("Alarm with service id: {} warning: {}", service.getId(), service.getWarning());
125                     }
126                     handler.setStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE, service.getWarning());
127                     if ("ALARM".equals(service.getWarning()) || "TAMPER".equals(service.getWarning())) {
128                         handler.triggerAlarm(service);
129                     }
130                     handler.setInService("SERVICE".equals(service.getWarning()));
131                 } else {
132                     logger.debug("Alarm with service id: {} is offline", service.getId());
133                     handler.setStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, service.getStatus());
134                 }
135             }
136         }
137     }
138
139     @Override
140     public void dispose() {
141         super.dispose();
142         ScheduledFuture<?> localFuture = future;
143         if (localFuture != null) {
144             localFuture.cancel(true);
145         }
146         logout();
147     }
148
149     private @Nullable <T> T sendJsonMessage(String url, String urlParameters, Class<T> classOfT) {
150         return sendMessage(url, urlParameters, classOfT, APPLICATION_JSON, true);
151     }
152
153     private @Nullable <T> T sendJsonMessage(String url, String urlParameters, Class<T> classOfT, boolean relogin) {
154         return sendMessage(url, urlParameters, classOfT, APPLICATION_JSON, relogin);
155     }
156
157     private @Nullable <T> T sendUrlEncodedMessage(String url, String urlParameters, Class<T> classOfT) {
158         return sendMessage(url, urlParameters, classOfT, WWW_FORM_URLENCODED, true);
159     }
160
161     private @Nullable <T> T sendMessage(String url, String urlParameters, Class<T> classOfT, String encoding,
162             boolean relogin) {
163         String line = "";
164         try {
165             ContentResponse resp = createRequest(url).content(new StringContentProvider(urlParameters), encoding)
166                     .send();
167
168             logger.trace("Request: {} with data: {}", url, urlParameters);
169             line = resp.getContentAsString();
170             logger.trace("Response: {}", line);
171             return gson.fromJson(line, classOfT);
172         } catch (TimeoutException e) {
173             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
174                     "Timeout during calling url: " + url);
175         } catch (InterruptedException e) {
176             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
177                     "Interrupt during calling url: " + url);
178             Thread.currentThread().interrupt();
179         } catch (JsonSyntaxException e) {
180             logger.debug("Invalid JSON received: {}", line);
181             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
182                     "Syntax error during calling url: " + url);
183         } catch (ExecutionException e) {
184             if (relogin) {
185                 if (e.getMessage().contains(AUTHENTICATION_CHALLENGE)) {
186                     relogin();
187                     return null;
188                 }
189             }
190             logger.debug("Error during calling url: {}", url, e);
191             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
192                     "Error during calling url: " + url);
193         }
194         return null;
195     }
196
197     protected synchronized void login() {
198         String url = JABLOTRON_API_URL + "userAuthorize.json";
199         String urlParameters = "{\"login\":\"" + bridgeConfig.getLogin() + "\", \"password\":\""
200                 + bridgeConfig.getPassword() + "\"}";
201         JablotronLoginResponse response = sendJsonMessage(url, urlParameters, JablotronLoginResponse.class, false);
202
203         if (response == null) {
204             return;
205         }
206
207         if (response.getHttpCode() != 200) {
208             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
209                     "Http error: " + response.getHttpCode());
210         } else {
211             updateStatus(ThingStatus.ONLINE);
212         }
213     }
214
215     protected void logout() {
216         String url = JABLOTRON_API_URL + "logout.json";
217         String urlParameters = "system=" + SYSTEM;
218
219         try {
220             ContentResponse resp = createRequest(url)
221                     .content(new StringContentProvider(urlParameters), WWW_FORM_URLENCODED).send();
222
223             if (logger.isTraceEnabled()) {
224                 String line = resp.getContentAsString();
225                 logger.trace("logout response: {}", line);
226             }
227         } catch (ExecutionException | TimeoutException | InterruptedException e) {
228             // Silence
229         }
230     }
231
232     public @Nullable List<JablotronDiscoveredService> discoverServices() {
233         String url = JABLOTRON_API_URL + "serviceListGet.json";
234         String urlParameters = "{\"list-type\": \"EXTENDED\",\"visibility\": \"VISIBLE\"}";
235         JablotronGetServiceResponse response = sendJsonMessage(url, urlParameters, JablotronGetServiceResponse.class);
236
237         if (response == null) {
238             return null;
239         }
240
241         if (response.getHttpCode() != 200) {
242             logger.debug("Error during service discovery, got http code: {}", response.getHttpCode());
243         }
244
245         return response.getData().getServices();
246     }
247
248     protected @Nullable JablotronControlResponse sendUserCode(Thing th, String section, String key, String status,
249             String code) {
250         JablotronAlarmHandler handler = (JablotronAlarmHandler) th.getHandler();
251
252         if (handler == null) {
253             logger.debug("Thing handler is null");
254             return null;
255         }
256
257         if (handler.isInService()) {
258             logger.debug("Cannot send command because the alarm is in the service mode");
259             return null;
260         }
261
262         String url = JABLOTRON_API_URL + "controlSegment.json";
263         String urlParameters = "service=" + th.getThingTypeUID().getId() + "&serviceId="
264                 + handler.thingConfig.getServiceId() + "&segmentId=" + section + "&segmentKey=" + key
265                 + "&expected_status=" + status + "&control_time=0&control_code=" + code + "&system=" + SYSTEM;
266
267         JablotronControlResponse response = sendUrlEncodedMessage(url, urlParameters, JablotronControlResponse.class);
268
269         if (response == null) {
270             return null;
271         }
272
273         if (!response.isStatus()) {
274             logger.debug("Error during sending user code: {}", response.getErrorMessage());
275         }
276         return response;
277     }
278
279     protected @Nullable List<JablotronHistoryDataEvent> sendGetEventHistory(Thing th, String alarm) {
280         String url = JABLOTRON_API_URL + alarm + "/eventHistoryGet.json";
281         JablotronAlarmHandler handler = (JablotronAlarmHandler) th.getHandler();
282
283         if (handler == null) {
284             logger.debug("Thing handler is null");
285             return null;
286         }
287
288         String urlParameters = "{\"limit\":1, \"service-id\":" + handler.thingConfig.getServiceId() + "}";
289         JablotronGetEventHistoryResponse response = sendJsonMessage(url, urlParameters,
290                 JablotronGetEventHistoryResponse.class);
291
292         if (response == null) {
293             return null;
294         }
295
296         if (200 != response.getHttpCode()) {
297             logger.debug("Got error while getting history with http code: {}", response.getHttpCode());
298         }
299         return response.getData().getEvents();
300     }
301
302     protected @Nullable JablotronDataUpdateResponse sendGetStatusRequest(Thing th) {
303         String url = JABLOTRON_API_URL + "dataUpdate.json";
304         JablotronAlarmHandler handler = (JablotronAlarmHandler) th.getHandler();
305
306         if (handler == null) {
307             logger.debug("Thing handler is null");
308             return null;
309         }
310
311         String urlParameters = "data=[{ \"filter_data\":[{\"data_type\":\"section\"},{\"data_type\":\"pgm\"},{\"data_type\":\"thermometer\"},{\"data_type\":\"thermostat\"}],\"service_type\":\""
312                 + th.getThingTypeUID().getId() + "\",\"service_id\":" + handler.thingConfig.getServiceId()
313                 + ",\"data_group\":\"serviceData\"}]&system=" + SYSTEM;
314
315         return sendUrlEncodedMessage(url, urlParameters, JablotronDataUpdateResponse.class);
316     }
317
318     protected @Nullable JablotronGetPGResponse sendGetProgrammableGates(Thing th, String alarm) {
319         String url = JABLOTRON_API_URL + alarm + "/programmableGatesGet.json";
320         JablotronAlarmHandler handler = (JablotronAlarmHandler) th.getHandler();
321
322         if (handler == null) {
323             logger.debug("Thing handler is null");
324             return null;
325         }
326
327         String urlParameters = "{\"connect-device\":false,\"list-type\":\"FULL\",\"service-id\":"
328                 + handler.thingConfig.getServiceId() + ",\"service-states\":true}";
329
330         return sendJsonMessage(url, urlParameters, JablotronGetPGResponse.class);
331     }
332
333     protected @Nullable JablotronGetSectionsResponse sendGetSections(Thing th, String alarm) {
334         String url = JABLOTRON_API_URL + alarm + "/sectionsGet.json";
335         JablotronAlarmHandler handler = (JablotronAlarmHandler) th.getHandler();
336
337         if (handler == null) {
338             logger.debug("Thing handler is null");
339             return null;
340         }
341
342         String urlParameters = "{\"connect-device\":false,\"list-type\":\"FULL\",\"service-id\":"
343                 + handler.thingConfig.getServiceId() + ",\"service-states\":true}";
344
345         return sendJsonMessage(url, urlParameters, JablotronGetSectionsResponse.class);
346     }
347
348     protected @Nullable JablotronGetSectionsResponse controlComponent(Thing th, String code, String action,
349             String value, String componentId) throws SecurityException {
350         JablotronAlarmHandler handler = (JablotronAlarmHandler) th.getHandler();
351
352         if (handler == null) {
353             logger.debug("Thing handler is null");
354             return null;
355         }
356
357         if (handler.isInService()) {
358             logger.debug("Cannot control component because the alarm is in the service mode");
359             return null;
360         }
361
362         String url = JABLOTRON_API_URL + handler.getAlarmName() + "/controlComponent.json";
363         String urlParameters = "{\"authorization\":{\"authorization-code\":\"" + code
364                 + "\"},\"control-components\":[{\"actions\":{\"action\":\"" + action + "\",\"value\":\"" + value
365                 + "\"},\"component-id\":\"" + componentId + "\"}],\"service-id\":" + handler.thingConfig.getServiceId()
366                 + "}";
367
368         return sendJsonMessage(url, urlParameters, JablotronGetSectionsResponse.class);
369     }
370
371     private Request createRequest(String url) {
372         return httpClient.newRequest(url).method(HttpMethod.POST).header(HttpHeader.ACCEPT, APPLICATION_JSON)
373                 .header(HttpHeader.ACCEPT_LANGUAGE, bridgeConfig.getLang()).header(HttpHeader.ACCEPT_ENCODING, "*")
374                 .header("x-vendor-id", VENDOR).agent(AGENT).timeout(TIMEOUT_SEC, TimeUnit.SECONDS);
375     }
376
377     private void relogin() {
378         logger.debug("doing relogin");
379         login();
380     }
381 }