2 * Copyright (c) 2010-2024 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.orbitbhyve.internal.handler;
15 import static org.openhab.binding.orbitbhyve.internal.OrbitBhyveBindingConstants.*;
17 import java.io.IOException;
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;
27 import java.util.concurrent.ExecutionException;
28 import java.util.concurrent.Future;
29 import java.util.concurrent.ScheduledFuture;
30 import java.util.concurrent.TimeUnit;
31 import java.util.concurrent.TimeoutException;
33 import org.eclipse.jdt.annotation.NonNullByDefault;
34 import org.eclipse.jdt.annotation.Nullable;
35 import org.eclipse.jetty.client.HttpClient;
36 import org.eclipse.jetty.client.api.ContentResponse;
37 import org.eclipse.jetty.client.api.Request;
38 import org.eclipse.jetty.client.util.StringContentProvider;
39 import org.eclipse.jetty.http.HttpMethod;
40 import org.eclipse.jetty.websocket.api.Session;
41 import org.eclipse.jetty.websocket.api.WebSocketException;
42 import org.eclipse.jetty.websocket.client.WebSocketClient;
43 import org.openhab.binding.orbitbhyve.internal.OrbitBhyveConfiguration;
44 import org.openhab.binding.orbitbhyve.internal.discovery.OrbitBhyveDiscoveryService;
45 import org.openhab.binding.orbitbhyve.internal.model.OrbitBhyveDevice;
46 import org.openhab.binding.orbitbhyve.internal.model.OrbitBhyveProgram;
47 import org.openhab.binding.orbitbhyve.internal.model.OrbitBhyveSessionResponse;
48 import org.openhab.binding.orbitbhyve.internal.model.OrbitBhyveSocketEvent;
49 import org.openhab.binding.orbitbhyve.internal.net.OrbitBhyveSocket;
50 import org.openhab.core.config.core.status.ConfigStatusMessage;
51 import org.openhab.core.library.types.OnOffType;
52 import org.openhab.core.library.types.StringType;
53 import org.openhab.core.thing.Bridge;
54 import org.openhab.core.thing.Channel;
55 import org.openhab.core.thing.ChannelUID;
56 import org.openhab.core.thing.Thing;
57 import org.openhab.core.thing.ThingStatus;
58 import org.openhab.core.thing.ThingStatusDetail;
59 import org.openhab.core.thing.binding.ConfigStatusBridgeHandler;
60 import org.openhab.core.thing.binding.ThingHandler;
61 import org.openhab.core.thing.binding.ThingHandlerService;
62 import org.openhab.core.types.Command;
63 import org.slf4j.Logger;
64 import org.slf4j.LoggerFactory;
66 import com.google.gson.Gson;
67 import com.google.gson.JsonSyntaxException;
70 * The {@link OrbitBhyveBridgeHandler} is responsible for handling commands, which are
71 * sent to one of the channels.
73 * @author Ondrej Pecta - Initial contribution
76 public class OrbitBhyveBridgeHandler extends ConfigStatusBridgeHandler {
78 private final Logger logger = LoggerFactory.getLogger(OrbitBhyveBridgeHandler.class);
80 private final HttpClient httpClient;
82 private final WebSocketClient webSocketClient;
84 private @Nullable ScheduledFuture<?> future = null;
86 private @Nullable Session session;
88 private @Nullable String sessionToken = null;
90 private OrbitBhyveConfiguration config = new OrbitBhyveConfiguration();
92 private final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
95 private final Gson gson = new Gson();
97 public OrbitBhyveBridgeHandler(Bridge thing, HttpClient httpClient, WebSocketClient webSocketClient) {
99 this.httpClient = httpClient;
100 this.webSocketClient = webSocketClient;
104 public Collection<ConfigStatusMessage> getConfigStatus() {
105 return Collections.emptyList();
109 public void handleCommand(ChannelUID channelUID, Command command) {
113 public Collection<Class<? extends ThingHandlerService>> getServices() {
114 return Set.of(OrbitBhyveDiscoveryService.class);
118 public void initialize() {
119 config = getConfigAs(OrbitBhyveConfiguration.class);
120 httpClient.setFollowRedirects(false);
122 scheduler.execute(() -> {
124 future = scheduler.scheduleWithFixedDelay(this::ping, 0, config.refresh, TimeUnit.SECONDS);
126 logger.debug("Finished initializing!");
130 public void dispose() {
131 ScheduledFuture<?> localFuture = future;
132 if (localFuture != null) {
133 localFuture.cancel(true);
139 private boolean login() {
141 String urlParameters = "{\"session\":{\"email\":\"" + config.email + "\",\"password\":\"" + config.password
143 ContentResponse response = httpClient.newRequest(BHYVE_SESSION).method(HttpMethod.POST).agent(AGENT)
144 .content(new StringContentProvider(urlParameters), "application/json; charset=utf-8")
145 .timeout(BHYVE_TIMEOUT, TimeUnit.SECONDS).send();
146 if (response.getStatus() == 200) {
147 if (logger.isTraceEnabled()) {
148 logger.trace("response: {}", response.getContentAsString());
150 OrbitBhyveSessionResponse session = gson.fromJson(response.getContentAsString(),
151 OrbitBhyveSessionResponse.class);
152 sessionToken = session.getOrbitSessionToken();
153 logger.debug("token: {}", sessionToken);
154 initializeWebSocketSession();
156 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
157 "Login response status:" + response.getStatus());
160 } catch (TimeoutException | ExecutionException e) {
161 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Exception during login");
163 } catch (InterruptedException e) {
164 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Exception during login");
165 Thread.currentThread().interrupt();
168 updateStatus(ThingStatus.ONLINE);
172 private synchronized void ping() {
173 if (ThingStatus.OFFLINE == thing.getStatus()) {
177 if (ThingStatus.ONLINE == thing.getStatus()) {
178 Session localSession = session;
179 if (localSession == null || !localSession.isOpen()) {
180 initializeWebSocketSession();
182 localSession = session;
183 if (localSession != null && localSession.isOpen() && localSession.getRemote() != null) {
185 logger.debug("Sending ping");
186 localSession.getRemote().sendString("{\"event\":\"ping\"}");
188 } catch (IOException e) {
189 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
190 "Error sending ping (IOException on web socket)");
191 } catch (WebSocketException e) {
192 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
193 String.format("Error sending ping (WebSocketException: %s)", e.getMessage()));
196 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Web socket creation error");
201 public List<OrbitBhyveDevice> getDevices() {
203 ContentResponse response = sendRequestBuilder(BHYVE_DEVICES, HttpMethod.GET).send();
204 if (response.getStatus() == 200) {
205 if (logger.isTraceEnabled()) {
206 logger.trace("Devices response: {}", response.getContentAsString());
208 OrbitBhyveDevice[] devices = gson.fromJson(response.getContentAsString(), OrbitBhyveDevice[].class);
209 return Arrays.asList(devices);
211 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
212 "Get devices returned response status: " + response.getStatus());
214 } catch (JsonSyntaxException e) {
215 logger.debug("Exception parsing devices json: {}", e.getMessage(), e);
216 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Error parsing devices json");
217 } catch (TimeoutException | ExecutionException e) {
218 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Error during getting devices");
219 } catch (InterruptedException e) {
220 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Error during getting devices");
221 Thread.currentThread().interrupt();
223 return new ArrayList<>();
226 Request sendRequestBuilder(String uri, HttpMethod method) {
227 return httpClient.newRequest(uri).method(method).agent(AGENT).header("Orbit-Session-Token", sessionToken)
228 .timeout(BHYVE_TIMEOUT, TimeUnit.SECONDS);
231 public @Nullable OrbitBhyveDevice getDevice(String deviceId) {
233 ContentResponse response = sendRequestBuilder(BHYVE_DEVICES + "/" + deviceId, HttpMethod.GET).send();
234 if (response.getStatus() == 200) {
235 if (logger.isTraceEnabled()) {
236 logger.trace("Device response: {}", response.getContentAsString());
238 return gson.fromJson(response.getContentAsString(), OrbitBhyveDevice.class);
240 logger.debug("Returned status: {}", response.getStatus());
241 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
242 "Returned status: " + response.getStatus());
244 } catch (JsonSyntaxException e) {
245 logger.debug("Exception parsing device json: {}", e.getMessage(), e);
246 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Error parsing device json");
247 } catch (TimeoutException | ExecutionException e) {
248 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
249 "Error during getting device info: " + deviceId);
250 } catch (InterruptedException e) {
251 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
252 "Error during getting device info: " + deviceId);
253 Thread.currentThread().interrupt();
258 public synchronized void processStatusResponse(String content) {
259 updateStatus(ThingStatus.ONLINE);
260 logger.trace("Got message: {}", content);
261 OrbitBhyveSocketEvent event = gson.fromJson(content, OrbitBhyveSocketEvent.class);
267 private void processEvent(OrbitBhyveSocketEvent event) {
268 switch (event.getEvent()) {
269 case "watering_in_progress_notification":
270 disableZones(event.getDeviceId());
271 Channel channel = getThingChannel(event.getDeviceId(), event.getStation());
272 if (channel != null) {
273 logger.debug("Watering zone: {}", event.getStation());
274 updateState(channel.getUID(), OnOffType.ON);
275 String program = event.getProgram().getAsString();
276 if (!program.isEmpty() && !"manual".equals(program)) {
277 channel = getThingChannel(event.getDeviceId(), "program_" + program);
278 if (channel != null) {
279 updateState(channel.getUID(), OnOffType.ON);
284 case "watering_complete":
285 logger.debug("Watering complete");
286 disableZones(event.getDeviceId());
287 disablePrograms(event.getDeviceId());
288 updateDeviceStatus(event.getDeviceId());
291 logger.debug("Updating mode to: {}", event.getMode());
292 Channel ch = getThingChannel(event.getDeviceId(), CHANNEL_MODE);
294 updateState(ch.getUID(), new StringType(event.getMode()));
296 ch = getThingChannel(event.getDeviceId(), CHANNEL_CONTROL);
298 updateState(ch.getUID(), OnOffType.from(!"off".equals(event.getMode())));
300 updateDeviceStatus(event.getDeviceId());
303 updateDeviceStatus(event.getDeviceId());
305 case "skip_active_station":
306 disableZones(event.getDeviceId());
308 case "program_changed":
309 OrbitBhyveProgram program = gson.fromJson(event.getProgram(), OrbitBhyveProgram.class);
310 if (program != null) {
311 updateDeviceProgramStatus(program);
312 updateDeviceStatus(program.getDeviceId());
316 logger.debug("Received event: {}", event.getEvent());
320 private void updateAllStatuses() {
321 List<OrbitBhyveDevice> devices = getDevices();
322 for (Thing th : getThing().getThings()) {
323 if (th.isEnabled()) {
324 String deviceId = th.getUID().getId();
325 ThingHandler handler = th.getHandler();
326 if (handler instanceof OrbitBhyveSprinklerHandler sprinklerHandler) {
327 for (OrbitBhyveDevice device : devices) {
328 if (deviceId.equals(th.getUID().getId())) {
329 updateDeviceStatus(device, sprinklerHandler);
337 private void updateDeviceStatus(@Nullable OrbitBhyveDevice device, @Nullable OrbitBhyveSprinklerHandler handler) {
338 if (device != null && handler != null) {
339 handler.setDeviceOnline(device.isConnected());
340 handler.updateDeviceStatus(device.getStatus());
341 handler.updateSmartWatering(device.getWaterSenseMode());
346 private void updateDeviceStatus(String deviceId) {
347 for (Thing th : getThing().getThings()) {
348 if (deviceId.equals(th.getUID().getId())) {
349 ThingHandler handler = th.getHandler();
350 if (handler instanceof OrbitBhyveSprinklerHandler sprinklerHandler) {
351 OrbitBhyveDevice device = getDevice(deviceId);
352 updateDeviceStatus(device, sprinklerHandler);
358 private void updateDeviceProgramStatus(OrbitBhyveProgram program) {
359 for (Thing th : getThing().getThings()) {
360 if (program.getDeviceId().equals(th.getUID().getId())) {
361 ThingHandler handler = th.getHandler();
362 if (handler instanceof OrbitBhyveSprinklerHandler sprinklerHandler) {
363 sprinklerHandler.updateProgram(program);
369 private void disableZones(String deviceId) {
370 disableChannel(deviceId, "zone_");
373 private void disablePrograms(String deviceId) {
374 disableChannel(deviceId, "program_");
377 private void disableChannel(String deviceId, String name) {
378 for (Thing th : getThing().getThings()) {
379 if (deviceId.equals(th.getUID().getId())) {
380 for (Channel ch : th.getChannels()) {
381 if (ch.getUID().getId().startsWith(name)) {
382 updateState(ch.getUID(), OnOffType.OFF);
390 private @Nullable Channel getThingChannel(String deviceId, int station) {
391 for (Thing th : getThing().getThings()) {
392 if (deviceId.equals(th.getUID().getId())) {
393 return th.getChannel("zone_" + station);
396 logger.debug("Cannot find zone: {} for device: {}", station, deviceId);
400 private @Nullable Channel getThingChannel(String deviceId, String name) {
401 for (Thing th : getThing().getThings()) {
402 if (deviceId.equals(th.getUID().getId())) {
403 return th.getChannel(name);
406 logger.debug("Cannot find channel: {} for device: {}", name, deviceId);
410 private @Nullable Session createSession() {
411 String url = BHYVE_WS_URL;
412 URI uri = URI.create(url);
415 // The socket that receives events
416 OrbitBhyveSocket socket = new OrbitBhyveSocket(this);
418 Future<Session> fut = webSocketClient.connect(socket, uri);
421 } catch (IOException e) {
422 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Cannot connect websocket client");
423 } catch (InterruptedException e) {
424 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Cannot create websocket session");
425 Thread.currentThread().interrupt();
426 } catch (ExecutionException e) {
427 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Cannot create websocket session");
432 private synchronized void initializeWebSocketSession() {
433 logger.debug("Initializing WebSocket session");
435 session = createSession();
436 Session localSession = session;
437 if (localSession != null) {
438 logger.debug("WebSocket connected!");
440 String msg = "{\"event\":\"app_connection\",\"orbit_session_token\":\"" + sessionToken + "\"}";
441 logger.trace("sending message:\n {}", msg);
442 localSession.getRemote().sendString(msg);
443 } catch (IOException e) {
444 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
445 "Error sending hello string (IOException on web socket)");
446 } catch (WebSocketException e) {
447 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
448 String.format("Error sending hello string (WebSocketException: %s)", e.getMessage()));
453 private void closeSession() {
454 Session localSession = session;
455 if (localSession != null && localSession.isOpen()) {
456 localSession.close();
460 public void runZone(String deviceId, String zone, int time) {
461 String dateTime = format.format(new Date());
464 Session localSession = session;
465 if (localSession != null && localSession.isOpen() && localSession.getRemote() != null) {
466 localSession.getRemote()
467 .sendString("{\"event\":\"change_mode\",\"device_id\":\"" + deviceId + "\",\"timestamp\":\""
468 + dateTime + "\",\"mode\":\"manual\",\"stations\":[{\"station\":" + zone
469 + ",\"run_time\":" + time + "}]}");
471 } catch (IOException e) {
472 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
473 "Error during zone watering execution");
477 public void runProgram(String deviceId, String program) {
478 String dateTime = format.format(new Date());
481 Session localSession = session;
482 if (localSession != null && localSession.isOpen() && localSession.getRemote() != null) {
483 localSession.getRemote().sendString("{\"event\":\"change_mode\",\"mode\":\"manual\",\"program\":\""
484 + program + "\",\"device_id\":\"" + deviceId + "\",\"timestamp\":\"" + dateTime + "\"}");
486 } catch (IOException e) {
487 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
488 "Error sending program watering execution (IOException on web socket)");
489 } catch (WebSocketException e) {
490 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
491 String.format("Error sending program watering execution (WebSocketException: %s)", e.getMessage()));
495 public void enableProgram(OrbitBhyveProgram program, boolean enable) {
497 String payLoad = "{\"sprinkler_timer_program\":{\"id\":\"" + program.getId() + "\",\"device_id\":\""
498 + program.getDeviceId() + "\",\"program\":\"" + program.getProgram() + "\",\"enabled\":" + enable
500 logger.debug("updating program {} with data {}", program.getProgram(), payLoad);
501 ContentResponse response = sendRequestBuilder(BHYVE_PROGRAMS + "/" + program.getId(), HttpMethod.PUT)
502 .content(new StringContentProvider(payLoad), "application/json; charset=utf-8").send();
503 if (response.getStatus() == 200) {
504 if (logger.isTraceEnabled()) {
505 logger.trace("Enable programs response: {}", response.getContentAsString());
509 logger.debug("Returned status: {}", response.getStatus());
510 updateStatus(ThingStatus.OFFLINE);
512 } catch (TimeoutException | ExecutionException e) {
513 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Error during updating programs");
514 } catch (InterruptedException e) {
515 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Error during updating programs");
516 Thread.currentThread().interrupt();
520 public void setRainDelay(String deviceId, int delay) {
521 String dateTime = format.format(new Date());
524 Session localSession = session;
525 if (localSession != null && localSession.isOpen() && localSession.getRemote() != null) {
526 localSession.getRemote().sendString("{\"event\":\"rain_delay\",\"device_id\":\"" + deviceId
527 + "\",\"delay\":" + delay + ",\"timestamp\":\"" + dateTime + "\"}");
529 } catch (IOException e) {
530 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
531 "Error setting rain delay (IOException on web socket)");
532 } catch (WebSocketException e) {
533 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
534 String.format("Error setting rain delay (WebSocketException: %s)", e.getMessage()));
538 public void stopWatering(String deviceId) {
539 String dateTime = format.format(new Date());
542 Session localSession = session;
543 if (localSession != null && localSession.isOpen() && localSession.getRemote() != null) {
544 localSession.getRemote().sendString("{\"event\":\"change_mode\",\"device_id\":\"" + deviceId
545 + "\",\"timestamp\":\"" + dateTime + "\",\"mode\":\"manual\",\"stations\":[]}");
547 } catch (IOException e) {
548 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
549 "Error sending stop watering (IOException on web socket)");
550 } catch (WebSocketException e) {
551 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
552 String.format("Error sending stop watering (WebSocketException: %s)", e.getMessage()));
556 public List<OrbitBhyveProgram> getPrograms() {
558 ContentResponse response = sendRequestBuilder(BHYVE_PROGRAMS, HttpMethod.GET).send();
559 if (response.getStatus() == 200) {
560 if (logger.isTraceEnabled()) {
561 logger.trace("Programs response: {}", response.getContentAsString());
563 OrbitBhyveProgram[] devices = gson.fromJson(response.getContentAsString(), OrbitBhyveProgram[].class);
564 return Arrays.asList(devices);
566 logger.debug("Returned status: {}", response.getStatus());
567 updateStatus(ThingStatus.OFFLINE);
569 } catch (TimeoutException | ExecutionException e) {
570 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Error during getting programs");
571 } catch (InterruptedException e) {
572 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Error during getting programs");
573 Thread.currentThread().interrupt();
575 return new ArrayList<>();
578 public void changeRunMode(String deviceId, String mode) {
579 String dateTime = format.format(new Date());
582 Session localSession = session;
583 if (localSession != null && localSession.isOpen() && localSession.getRemote() != null) {
584 localSession.getRemote().sendString("{\"event\":\"change_mode\",\"mode\":\"" + mode
585 + "\",\"device_id\":\"" + deviceId + "\",\"timestamp\":\"" + dateTime + "\"}");
587 } catch (IOException e) {
588 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
589 "Error setting run mode (IOException on web socket)");
590 } catch (WebSocketException e) {
591 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
592 String.format("Error setting run mode (WebSocketException: %s)", e.getMessage()));
596 public void setSmartWatering(String deviceId, boolean enable) {
597 OrbitBhyveDevice device = getDevice(deviceId);
598 if (device != null && device.getId().equals(deviceId)) {
599 device.setWaterSenseMode(enable ? "auto" : "off");
600 updateDevice(deviceId, gson.toJson(device));
604 private void updateDevice(String deviceId, String deviceString) {
605 String payload = "{\"device\":" + deviceString + "}";
606 logger.trace("New String: {}", payload);
608 ContentResponse response = sendRequestBuilder(BHYVE_DEVICES + "/" + deviceId, HttpMethod.PUT)
609 .content(new StringContentProvider(payload), "application/json;charset=UTF-8").send();
610 if (logger.isTraceEnabled()) {
611 logger.trace("Device update response: {}", response.getContentAsString());
613 if (response.getStatus() != 200) {
614 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
615 "Update device response status: " + response.getStatus());
617 } catch (TimeoutException | ExecutionException e) {
618 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Error during updating device");
619 } catch (InterruptedException e) {
620 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Error during updating device");
621 Thread.currentThread().interrupt();