]> git.basschouten.com Git - openhab-addons.git/blob
95e7e44c7ce2a9c1ebdf5c8023b6e90172c278d1
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2022 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.orbitbhyve.internal.handler;
14
15 import static org.openhab.binding.orbitbhyve.internal.OrbitBhyveBindingConstants.*;
16
17 import java.io.IOException;
18 import java.net.URI;
19 import java.text.SimpleDateFormat;
20 import java.util.ArrayList;
21 import java.util.Arrays;
22 import java.util.Collection;
23 import java.util.Collections;
24 import java.util.Date;
25 import java.util.List;
26 import java.util.concurrent.ExecutionException;
27 import java.util.concurrent.Future;
28 import java.util.concurrent.ScheduledFuture;
29 import java.util.concurrent.TimeUnit;
30 import java.util.concurrent.TimeoutException;
31
32 import org.eclipse.jdt.annotation.NonNullByDefault;
33 import org.eclipse.jdt.annotation.Nullable;
34 import org.eclipse.jetty.client.HttpClient;
35 import org.eclipse.jetty.client.api.ContentResponse;
36 import org.eclipse.jetty.client.api.Request;
37 import org.eclipse.jetty.client.util.StringContentProvider;
38 import org.eclipse.jetty.http.HttpMethod;
39 import org.eclipse.jetty.websocket.api.Session;
40 import org.eclipse.jetty.websocket.client.WebSocketClient;
41 import org.openhab.binding.orbitbhyve.internal.OrbitBhyveConfiguration;
42 import org.openhab.binding.orbitbhyve.internal.discovery.OrbitBhyveDiscoveryService;
43 import org.openhab.binding.orbitbhyve.internal.model.OrbitBhyveDevice;
44 import org.openhab.binding.orbitbhyve.internal.model.OrbitBhyveProgram;
45 import org.openhab.binding.orbitbhyve.internal.model.OrbitBhyveSessionResponse;
46 import org.openhab.binding.orbitbhyve.internal.model.OrbitBhyveSocketEvent;
47 import org.openhab.binding.orbitbhyve.internal.net.OrbitBhyveSocket;
48 import org.openhab.core.config.core.status.ConfigStatusMessage;
49 import org.openhab.core.library.types.OnOffType;
50 import org.openhab.core.library.types.StringType;
51 import org.openhab.core.thing.Bridge;
52 import org.openhab.core.thing.Channel;
53 import org.openhab.core.thing.ChannelUID;
54 import org.openhab.core.thing.Thing;
55 import org.openhab.core.thing.ThingStatus;
56 import org.openhab.core.thing.ThingStatusDetail;
57 import org.openhab.core.thing.binding.ConfigStatusBridgeHandler;
58 import org.openhab.core.thing.binding.ThingHandlerService;
59 import org.openhab.core.types.Command;
60 import org.slf4j.Logger;
61 import org.slf4j.LoggerFactory;
62
63 import com.google.gson.Gson;
64
65 /**
66  * The {@link OrbitBhyveBridgeHandler} is responsible for handling commands, which are
67  * sent to one of the channels.
68  *
69  * @author Ondrej Pecta - Initial contribution
70  */
71 @NonNullByDefault
72 public class OrbitBhyveBridgeHandler extends ConfigStatusBridgeHandler {
73
74     private final Logger logger = LoggerFactory.getLogger(OrbitBhyveBridgeHandler.class);
75
76     private final HttpClient httpClient;
77
78     private final WebSocketClient webSocketClient;
79
80     private @Nullable ScheduledFuture<?> future = null;
81
82     private @Nullable Session session;
83
84     private @Nullable String sessionToken = null;
85
86     private OrbitBhyveConfiguration config = new OrbitBhyveConfiguration();
87
88     private final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
89
90     // Gson & parser
91     private final Gson gson = new Gson();
92
93     public OrbitBhyveBridgeHandler(Bridge thing, HttpClient httpClient, WebSocketClient webSocketClient) {
94         super(thing);
95         this.httpClient = httpClient;
96         this.webSocketClient = webSocketClient;
97     }
98
99     @Override
100     public Collection<ConfigStatusMessage> getConfigStatus() {
101         return Collections.emptyList();
102     }
103
104     @Override
105     public void handleCommand(ChannelUID channelUID, Command command) {
106     }
107
108     @Override
109     public Collection<Class<? extends ThingHandlerService>> getServices() {
110         return Collections.singleton(OrbitBhyveDiscoveryService.class);
111     }
112
113     @Override
114     public void initialize() {
115         config = getConfigAs(OrbitBhyveConfiguration.class);
116         httpClient.setFollowRedirects(false);
117
118         scheduler.execute(() -> {
119             login();
120             future = scheduler.scheduleWithFixedDelay(this::ping, 0, config.refresh, TimeUnit.SECONDS);
121         });
122         logger.debug("Finished initializing!");
123     }
124
125     @Override
126     public void dispose() {
127         ScheduledFuture<?> localFuture = future;
128         if (localFuture != null) {
129             localFuture.cancel(true);
130         }
131         closeSession();
132         super.dispose();
133     }
134
135     private boolean login() {
136         try {
137             String urlParameters = "{\"session\":{\"email\":\"" + config.email + "\",\"password\":\"" + config.password
138                     + "\"}}";
139             ContentResponse response = httpClient.newRequest(BHYVE_SESSION).method(HttpMethod.POST).agent(AGENT)
140                     .content(new StringContentProvider(urlParameters), "application/json; charset=utf-8")
141                     .timeout(BHYVE_TIMEOUT, TimeUnit.SECONDS).send();
142             if (response.getStatus() == 200) {
143                 if (logger.isTraceEnabled()) {
144                     logger.trace("response: {}", response.getContentAsString());
145                 }
146                 OrbitBhyveSessionResponse session = gson.fromJson(response.getContentAsString(),
147                         OrbitBhyveSessionResponse.class);
148                 sessionToken = session.getOrbitSessionToken();
149                 logger.debug("token: {}", sessionToken);
150                 initializeWebSocketSession();
151             } else {
152                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
153                         "Login response status:" + response.getStatus());
154                 return false;
155             }
156         } catch (TimeoutException | ExecutionException e) {
157             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Exception during login");
158             return false;
159         } catch (InterruptedException e) {
160             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Exception during login");
161             Thread.currentThread().interrupt();
162             return false;
163         }
164         updateStatus(ThingStatus.ONLINE);
165         return true;
166     }
167
168     private synchronized void ping() {
169         if (ThingStatus.OFFLINE == thing.getStatus()) {
170             login();
171         }
172
173         if (ThingStatus.ONLINE == thing.getStatus()) {
174             Session localSession = session;
175             if (localSession == null || !localSession.isOpen()) {
176                 initializeWebSocketSession();
177             }
178             localSession = session;
179             if (localSession != null && localSession.isOpen() && localSession.getRemote() != null) {
180                 try {
181                     logger.debug("Sending ping");
182                     localSession.getRemote().sendString("{\"event\":\"ping\"}");
183                     updateAllStatuses();
184                 } catch (IOException e) {
185                     updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
186                             "Error sending ping to a web socket");
187                 }
188             } else {
189                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Web socket creation error");
190             }
191         }
192     }
193
194     public List<OrbitBhyveDevice> getDevices() {
195         try {
196             ContentResponse response = sendRequestBuilder(BHYVE_DEVICES, HttpMethod.GET).send();
197             if (response.getStatus() == 200) {
198                 if (logger.isTraceEnabled()) {
199                     logger.trace("Devices response: {}", response.getContentAsString());
200                 }
201                 OrbitBhyveDevice[] devices = gson.fromJson(response.getContentAsString(), OrbitBhyveDevice[].class);
202                 return Arrays.asList(devices);
203             } else {
204                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
205                         "Get devices returned response status: " + response.getStatus());
206             }
207         } catch (TimeoutException | ExecutionException e) {
208             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Error during getting devices");
209         } catch (InterruptedException e) {
210             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Error during getting devices");
211             Thread.currentThread().interrupt();
212         }
213         return new ArrayList<>();
214     }
215
216     Request sendRequestBuilder(String uri, HttpMethod method) {
217         return httpClient.newRequest(uri).method(method).agent(AGENT).header("Orbit-Session-Token", sessionToken)
218                 .timeout(BHYVE_TIMEOUT, TimeUnit.SECONDS);
219     }
220
221     public @Nullable OrbitBhyveDevice getDevice(String deviceId) {
222         try {
223             ContentResponse response = sendRequestBuilder(BHYVE_DEVICES + "/" + deviceId, HttpMethod.GET).send();
224             if (response.getStatus() == 200) {
225                 if (logger.isTraceEnabled()) {
226                     logger.trace("Device response: {}", response.getContentAsString());
227                 }
228                 OrbitBhyveDevice device = gson.fromJson(response.getContentAsString(), OrbitBhyveDevice.class);
229                 return device;
230             } else {
231                 logger.debug("Returned status: {}", response.getStatus());
232                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
233                         "Returned status: " + response.getStatus());
234             }
235         } catch (TimeoutException | ExecutionException e) {
236             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
237                     "Error during getting device info: " + deviceId);
238         } catch (InterruptedException e) {
239             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
240                     "Error during getting device info: " + deviceId);
241             Thread.currentThread().interrupt();
242         }
243         return null;
244     }
245
246     public synchronized void processStatusResponse(String content) {
247         updateStatus(ThingStatus.ONLINE);
248         logger.trace("Got message: {}", content);
249         OrbitBhyveSocketEvent event = gson.fromJson(content, OrbitBhyveSocketEvent.class);
250         if (event != null) {
251             processEvent(event);
252         }
253     }
254
255     private void processEvent(OrbitBhyveSocketEvent event) {
256         switch (event.getEvent()) {
257             case "watering_in_progress_notification":
258                 disableZones(event.getDeviceId());
259                 Channel channel = getThingChannel(event.getDeviceId(), event.getStation());
260                 if (channel != null) {
261                     logger.debug("Watering zone: {}", event.getStation());
262                     updateState(channel.getUID(), OnOffType.ON);
263                     String program = event.getProgram().getAsString();
264                     if (!program.isEmpty() && !"manual".equals(program)) {
265                         channel = getThingChannel(event.getDeviceId(), "program_" + program);
266                         if (channel != null) {
267                             updateState(channel.getUID(), OnOffType.ON);
268                         }
269                     }
270                 }
271                 break;
272             case "watering_complete":
273                 logger.debug("Watering complete");
274                 disableZones(event.getDeviceId());
275                 disablePrograms(event.getDeviceId());
276                 updateDeviceStatus(event.getDeviceId());
277                 break;
278             case "change_mode":
279                 logger.debug("Updating mode to: {}", event.getMode());
280                 Channel ch = getThingChannel(event.getDeviceId(), CHANNEL_MODE);
281                 if (ch != null) {
282                     updateState(ch.getUID(), new StringType(event.getMode()));
283                 }
284                 ch = getThingChannel(event.getDeviceId(), CHANNEL_CONTROL);
285                 if (ch != null) {
286                     updateState(ch.getUID(), "off".equals(event.getMode()) ? OnOffType.OFF : OnOffType.ON);
287                 }
288                 updateDeviceStatus(event.getDeviceId());
289                 break;
290             case "rain_delay":
291                 updateDeviceStatus(event.getDeviceId());
292                 break;
293             case "skip_active_station":
294                 disableZones(event.getDeviceId());
295                 break;
296             case "program_changed":
297                 OrbitBhyveProgram program = gson.fromJson(event.getProgram(), OrbitBhyveProgram.class);
298                 if (program != null) {
299                     updateDeviceProgramStatus(program);
300                     updateDeviceStatus(program.getDeviceId());
301                 }
302                 break;
303             default:
304                 logger.debug("Received event: {}", event.getEvent());
305         }
306     }
307
308     private void updateAllStatuses() {
309         List<OrbitBhyveDevice> devices = getDevices();
310         for (Thing th : getThing().getThings()) {
311             if (th.isEnabled()) {
312                 String deviceId = th.getUID().getId();
313                 OrbitBhyveSprinklerHandler handler = (OrbitBhyveSprinklerHandler) th.getHandler();
314                 for (OrbitBhyveDevice device : devices) {
315                     if (deviceId.equals(th.getUID().getId())) {
316                         updateDeviceStatus(device, handler);
317                     }
318                 }
319             }
320         }
321     }
322
323     private void updateDeviceStatus(@Nullable OrbitBhyveDevice device, @Nullable OrbitBhyveSprinklerHandler handler) {
324         if (device != null && handler != null) {
325             handler.setDeviceOnline(device.isConnected());
326             handler.updateDeviceStatus(device.getStatus());
327             handler.updateSmartWatering(device.getWaterSenseMode());
328             return;
329         }
330     }
331
332     private void updateDeviceStatus(String deviceId) {
333         for (Thing th : getThing().getThings()) {
334             if (deviceId.equals(th.getUID().getId())) {
335                 OrbitBhyveSprinklerHandler handler = (OrbitBhyveSprinklerHandler) th.getHandler();
336                 OrbitBhyveDevice device = getDevice(deviceId);
337                 updateDeviceStatus(device, handler);
338             }
339         }
340     }
341
342     private void updateDeviceProgramStatus(OrbitBhyveProgram program) {
343         for (Thing th : getThing().getThings()) {
344             if (program.getDeviceId().equals(th.getUID().getId())) {
345                 OrbitBhyveSprinklerHandler handler = (OrbitBhyveSprinklerHandler) th.getHandler();
346                 if (handler != null) {
347                     handler.updateProgram(program);
348                 }
349             }
350         }
351     }
352
353     private void disableZones(String deviceId) {
354         disableChannel(deviceId, "zone_");
355     }
356
357     private void disablePrograms(String deviceId) {
358         disableChannel(deviceId, "program_");
359     }
360
361     private void disableChannel(String deviceId, String name) {
362         for (Thing th : getThing().getThings()) {
363             if (deviceId.equals(th.getUID().getId())) {
364                 for (Channel ch : th.getChannels()) {
365                     if (ch.getUID().getId().startsWith(name)) {
366                         updateState(ch.getUID(), OnOffType.OFF);
367                     }
368                 }
369                 return;
370             }
371         }
372     }
373
374     private @Nullable Channel getThingChannel(String deviceId, int station) {
375         for (Thing th : getThing().getThings()) {
376             if (deviceId.equals(th.getUID().getId())) {
377                 return th.getChannel("zone_" + station);
378             }
379         }
380         logger.debug("Cannot find zone: {} for device: {}", station, deviceId);
381         return null;
382     }
383
384     private @Nullable Channel getThingChannel(String deviceId, String name) {
385         for (Thing th : getThing().getThings()) {
386             if (deviceId.equals(th.getUID().getId())) {
387                 return th.getChannel(name);
388             }
389         }
390         logger.debug("Cannot find channel: {} for device: {}", name, deviceId);
391         return null;
392     }
393
394     private @Nullable Session createSession() {
395         String url = BHYVE_WS_URL;
396         URI uri = URI.create(url);
397
398         try {
399             // The socket that receives events
400             OrbitBhyveSocket socket = new OrbitBhyveSocket(this);
401             // Attempt Connect
402             Future<Session> fut = webSocketClient.connect(socket, uri);
403             // Wait for Connect
404             return fut.get();
405         } catch (IOException e) {
406             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Cannot connect websocket client");
407         } catch (InterruptedException e) {
408             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Cannot create websocket session");
409             Thread.currentThread().interrupt();
410         } catch (ExecutionException e) {
411             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Cannot create websocket session");
412         }
413         return null;
414     }
415
416     private synchronized void initializeWebSocketSession() {
417         logger.debug("Initializing WebSocket session");
418         closeSession();
419         session = createSession();
420         Session localSession = session;
421         if (localSession != null) {
422             logger.debug("WebSocket connected!");
423             try {
424                 String msg = "{\"event\":\"app_connection\",\"orbit_session_token\":\"" + sessionToken + "\"}";
425                 logger.trace("sending message:\n {}", msg);
426                 localSession.getRemote().sendString(msg);
427             } catch (IOException e) {
428                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
429                         "Cannot send hello string to web socket!");
430             }
431         }
432     }
433
434     private void closeSession() {
435         Session localSession = session;
436         if (localSession != null && localSession.isOpen()) {
437             localSession.close();
438         }
439     }
440
441     public void runZone(String deviceId, String zone, int time) {
442         String dateTime = format.format(new Date());
443         try {
444             ping();
445             Session localSession = session;
446             if (localSession != null && localSession.isOpen() && localSession.getRemote() != null) {
447                 localSession.getRemote()
448                         .sendString("{\"event\":\"change_mode\",\"device_id\":\"" + deviceId + "\",\"timestamp\":\""
449                                 + dateTime + "\",\"mode\":\"manual\",\"stations\":[{\"station\":" + zone
450                                 + ",\"run_time\":" + time + "}]}");
451             }
452         } catch (IOException e) {
453             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
454                     "Error during zone watering execution");
455         }
456     }
457
458     public void runProgram(String deviceId, String program) {
459         String dateTime = format.format(new Date());
460         try {
461             ping();
462             Session localSession = session;
463             if (localSession != null && localSession.isOpen() && localSession.getRemote() != null) {
464                 localSession.getRemote().sendString("{\"event\":\"change_mode\",\"mode\":\"manual\",\"program\":\""
465                         + program + "\",\"device_id\":\"" + deviceId + "\",\"timestamp\":\"" + dateTime + "\"}");
466             }
467         } catch (IOException e) {
468             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
469                     "Error during program watering execution");
470         }
471     }
472
473     public void enableProgram(OrbitBhyveProgram program, boolean enable) {
474         try {
475             String payLoad = "{\"sprinkler_timer_program\":{\"id\":\"" + program.getId() + "\",\"device_id\":\""
476                     + program.getDeviceId() + "\",\"program\":\"" + program.getProgram() + "\",\"enabled\":" + enable
477                     + "}}";
478             logger.debug("updating program {} with data {}", program.getProgram(), payLoad);
479             ContentResponse response = sendRequestBuilder(BHYVE_PROGRAMS + "/" + program.getId(), HttpMethod.PUT)
480                     .content(new StringContentProvider(payLoad), "application/json; charset=utf-8").send();
481             if (response.getStatus() == 200) {
482                 if (logger.isTraceEnabled()) {
483                     logger.trace("Enable programs response: {}", response.getContentAsString());
484                 }
485                 return;
486             } else {
487                 logger.debug("Returned status: {}", response.getStatus());
488                 updateStatus(ThingStatus.OFFLINE);
489             }
490         } catch (TimeoutException | ExecutionException e) {
491             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Error during updating programs");
492         } catch (InterruptedException e) {
493             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Error during updating programs");
494             Thread.currentThread().interrupt();
495         }
496     }
497
498     public void setRainDelay(String deviceId, int delay) {
499         String dateTime = format.format(new Date());
500         try {
501             ping();
502             Session localSession = session;
503             if (localSession != null && localSession.isOpen() && localSession.getRemote() != null) {
504                 localSession.getRemote().sendString("{\"event\":\"rain_delay\",\"device_id\":\"" + deviceId
505                         + "\",\"delay\":" + delay + ",\"timestamp\":\"" + dateTime + "\"}");
506             }
507         } catch (IOException e) {
508             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Error during rain delay setting");
509         }
510     }
511
512     public void stopWatering(String deviceId) {
513         String dateTime = format.format(new Date());
514         try {
515             ping();
516             Session localSession = session;
517             if (localSession != null && localSession.isOpen() && localSession.getRemote() != null) {
518                 localSession.getRemote().sendString("{\"event\":\"change_mode\",\"device_id\":\"" + deviceId
519                         + "\",\"timestamp\":\"" + dateTime + "\",\"mode\":\"manual\",\"stations\":[]}");
520             }
521         } catch (IOException e) {
522             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Error during watering stopping");
523         }
524     }
525
526     public List<OrbitBhyveProgram> getPrograms() {
527         try {
528             ContentResponse response = sendRequestBuilder(BHYVE_PROGRAMS, HttpMethod.GET).send();
529             if (response.getStatus() == 200) {
530                 if (logger.isTraceEnabled()) {
531                     logger.trace("Programs response: {}", response.getContentAsString());
532                 }
533                 OrbitBhyveProgram[] devices = gson.fromJson(response.getContentAsString(), OrbitBhyveProgram[].class);
534                 return Arrays.asList(devices);
535             } else {
536                 logger.debug("Returned status: {}", response.getStatus());
537                 updateStatus(ThingStatus.OFFLINE);
538             }
539         } catch (TimeoutException | ExecutionException e) {
540             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Error during getting programs");
541         } catch (InterruptedException e) {
542             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Error during getting programs");
543             Thread.currentThread().interrupt();
544         }
545         return new ArrayList<>();
546     }
547
548     public void changeRunMode(String deviceId, String mode) {
549         String dateTime = format.format(new Date());
550         try {
551             ping();
552             Session localSession = session;
553             if (localSession != null && localSession.isOpen() && localSession.getRemote() != null) {
554                 localSession.getRemote().sendString("{\"event\":\"change_mode\",\"mode\":\"" + mode
555                         + "\",\"device_id\":\"" + deviceId + "\",\"timestamp\":\"" + dateTime + "\"}");
556             }
557         } catch (IOException e) {
558             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Error during setting run mode");
559         }
560     }
561
562     public void setSmartWatering(String deviceId, boolean enable) {
563         OrbitBhyveDevice device = getDevice(deviceId);
564         if (device != null && device.getId().equals(deviceId)) {
565             device.setWaterSenseMode(enable ? "auto" : "off");
566             updateDevice(deviceId, gson.toJson(device));
567         }
568     }
569
570     private void updateDevice(String deviceId, String deviceString) {
571         String payload = "{\"device\":" + deviceString + "}";
572         logger.trace("New String: {}", payload);
573         try {
574             ContentResponse response = sendRequestBuilder(BHYVE_DEVICES + "/" + deviceId, HttpMethod.PUT)
575                     .content(new StringContentProvider(payload), "application/json;charset=UTF-8").send();
576             if (logger.isTraceEnabled()) {
577                 logger.trace("Device update response: {}", response.getContentAsString());
578             }
579             if (response.getStatus() != 200) {
580                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
581                         "Update device response status: " + response.getStatus());
582             }
583         } catch (TimeoutException | ExecutionException e) {
584             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Error during updating device");
585         } catch (InterruptedException e) {
586             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Error during updating device");
587             Thread.currentThread().interrupt();
588         }
589     }
590 }