2 * Copyright (c) 2010-2021 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.wemo.internal.handler;
15 import static org.openhab.binding.wemo.internal.WemoBindingConstants.*;
16 import static org.openhab.binding.wemo.internal.WemoUtil.*;
18 import java.math.BigDecimal;
20 import java.time.Instant;
21 import java.time.ZonedDateTime;
22 import java.util.Collections;
23 import java.util.HashMap;
26 import java.util.TimeZone;
27 import java.util.concurrent.ScheduledFuture;
28 import java.util.concurrent.TimeUnit;
30 import org.eclipse.jdt.annotation.NonNullByDefault;
31 import org.eclipse.jdt.annotation.Nullable;
32 import org.openhab.binding.wemo.internal.http.WemoHttpCall;
33 import org.openhab.core.config.core.Configuration;
34 import org.openhab.core.io.transport.upnp.UpnpIOParticipant;
35 import org.openhab.core.io.transport.upnp.UpnpIOService;
36 import org.openhab.core.library.types.DateTimeType;
37 import org.openhab.core.library.types.DecimalType;
38 import org.openhab.core.library.types.IncreaseDecreaseType;
39 import org.openhab.core.library.types.OnOffType;
40 import org.openhab.core.library.types.PercentType;
41 import org.openhab.core.thing.ChannelUID;
42 import org.openhab.core.thing.Thing;
43 import org.openhab.core.thing.ThingStatus;
44 import org.openhab.core.thing.ThingStatusDetail;
45 import org.openhab.core.thing.ThingTypeUID;
46 import org.openhab.core.types.Command;
47 import org.openhab.core.types.RefreshType;
48 import org.openhab.core.types.State;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
53 * The {@link WemoDimmerHandler} is responsible for handling commands, which are
54 * sent to one of the channels and to update their states.
56 * @author Hans-Jörg Merk - Initial contribution
59 public class WemoDimmerHandler extends AbstractWemoHandler implements UpnpIOParticipant {
61 private final Logger logger = LoggerFactory.getLogger(WemoDimmerHandler.class);
63 public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_DIMMER);
65 private Map<String, Boolean> subscriptionState = new HashMap<>();
66 private Map<String, String> stateMap = Collections.synchronizedMap(new HashMap<>());
68 private UpnpIOService service;
69 private WemoHttpCall wemoCall;
71 private int currentBrightness;
72 private int currentNightModeBrightness;
73 private @Nullable String currentNightModeState;
75 * Set dimming stepsize to 5%
77 private static final int DIM_STEPSIZE = 5;
79 private @Nullable ScheduledFuture<?> refreshJob;
80 private Runnable refreshRunnable = new Runnable() {
85 if (!isUpnpDeviceRegistered()) {
86 logger.debug("WeMo UPnP device {} not yet registered", getUDN());
90 } catch (Exception e) {
91 logger.debug("Exception during poll : {}", e.getMessage(), e);
96 public WemoDimmerHandler(Thing thing, UpnpIOService upnpIOService, WemoHttpCall wemoHttpCaller) {
97 super(thing, wemoHttpCaller);
99 this.service = upnpIOService;
100 this.wemoCall = wemoHttpCaller;
102 logger.debug("Creating a WemoDimmerHandler for thing '{}'", getThing().getUID());
106 public void initialize() {
107 Configuration configuration = getConfig();
108 if (configuration.get("udn") != null) {
109 logger.debug("Initializing WemoDimmerHandler for UDN '{}'", configuration.get("udn"));
110 service.registerParticipant(this);
114 logger.debug("Cannot initalize WemoDimmerHandler. UDN not set.");
119 public void dispose() {
120 logger.debug("WeMoDimmerHandler disposed.");
122 ScheduledFuture<?> job = refreshJob;
123 if (job != null && !job.isCancelled()) {
128 removeSubscription();
132 public void handleCommand(ChannelUID channelUID, Command command) {
133 logger.trace("Command '{}' received for channel '{}'", command, channelUID);
134 if (command instanceof RefreshType) {
137 } catch (Exception e) {
138 logger.debug("Exception during poll", e);
141 String action = "SetBinaryState";
142 String argument = "BinaryState";
144 String timeStamp = null;
145 switch (channelUID.getId()) {
146 case CHANNEL_BRIGHTNESS:
147 String binaryState = this.stateMap.get("BinaryState");
148 if (command instanceof OnOffType) {
149 value = command.equals(OnOffType.OFF) ? "0" : "1";
150 setBinaryState(action, argument, value);
151 if (command.equals(OnOffType.OFF)) {
152 State brightnessState = new PercentType("0");
153 updateState(CHANNEL_BRIGHTNESS, brightnessState);
154 updateState(CHANNEL_TIMERSTART, OnOffType.OFF);
156 State brightnessState = new PercentType(currentBrightness);
157 updateState(CHANNEL_BRIGHTNESS, brightnessState);
159 } else if (command instanceof PercentType) {
160 int newBrightness = ((PercentType) command).intValue();
161 value = String.valueOf(newBrightness);
162 currentBrightness = newBrightness;
163 argument = "brightness";
164 if (value.equals("0")) {
166 argument = "brightness";
167 setBinaryState(action, argument, "1");
169 argument = "BinaryState";
170 setBinaryState(action, argument, "0");
171 } else if ("0".equals(binaryState)) {
172 argument = "BinaryState";
173 setBinaryState(action, argument, "1");
175 argument = "brightness";
176 setBinaryState(action, argument, value);
177 } else if (command instanceof IncreaseDecreaseType) {
179 switch (command.toString()) {
181 newBrightness = currentBrightness + DIM_STEPSIZE;
182 if (newBrightness > 100) {
185 value = String.valueOf(newBrightness);
186 currentBrightness = newBrightness;
189 newBrightness = currentBrightness - DIM_STEPSIZE;
190 if (newBrightness < 0) {
193 value = String.valueOf(newBrightness);
194 currentBrightness = newBrightness;
197 argument = "brightness";
198 if (value.equals("0")) {
200 argument = "brightness";
201 setBinaryState(action, argument, "1");
203 argument = "BinaryState";
204 setBinaryState(action, argument, "0");
205 } else if ("0".equals(binaryState)) {
206 argument = "BinaryState";
207 setBinaryState(action, argument, "1");
209 argument = "brightness";
210 setBinaryState(action, argument, value);
213 case CHANNEL_FADERCOUNTDOWNTIME:
215 if (command instanceof DecimalType) {
216 int commandValue = Integer.valueOf(String.valueOf(command));
217 commandValue = commandValue * 60;
218 String commandString = String.valueOf(commandValue);
219 value = "<BinaryState></BinaryState>" + "<Duration></Duration>" + "<EndAction></EndAction>"
220 + "<brightness></brightness>" + "<fader>" + commandString + ":-1:1:0:0</fader>"
222 setBinaryState(action, argument, value);
225 case CHANNEL_FADERENABLED:
227 if (command.equals(OnOffType.ON)) {
228 value = "<BinaryState></BinaryState>" + "<Duration></Duration>" + "<EndAction></EndAction>"
229 + "<brightness></brightness>" + "<fader>600:-1:1:0:0</fader>" + "<UDN></UDN>";
230 } else if (command.equals(OnOffType.OFF)) {
231 value = "<BinaryState></BinaryState>" + "<Duration></Duration>" + "<EndAction></EndAction>"
232 + "<brightness></brightness>" + "<fader>600:-1:0:0:0</fader>" + "<UDN></UDN>";
234 setBinaryState(action, argument, value);
236 case CHANNEL_TIMERSTART:
238 long ts = System.currentTimeMillis() / 1000;
239 timeStamp = String.valueOf(ts);
240 logger.info("timestamp '{}' created", timeStamp);
241 String faderSeconds = null;
242 String faderEnabled = null;
243 String fader = this.stateMap.get("fader");
245 String[] splitFader = fader.split(":");
246 if (splitFader[0] != null) {
247 faderSeconds = splitFader[0];
249 if (splitFader[0] != null) {
250 faderEnabled = splitFader[2];
253 if (faderSeconds != null && faderEnabled != null) {
254 if (command.equals(OnOffType.ON)) {
255 value = "<BinaryState></BinaryState>" + "<Duration></Duration>" + "<EndAction></EndAction>"
256 + "<brightness></brightness>" + "<fader>" + faderSeconds + ":" + timeStamp + ":"
257 + faderEnabled + ":0:0</fader>" + "<UDN></UDN>";
258 updateState(CHANNEL_STATE, OnOffType.ON);
259 } else if (command.equals(OnOffType.OFF)) {
260 value = "<BinaryState></BinaryState>" + "<Duration></Duration>" + "<EndAction></EndAction>"
261 + "<brightness></brightness>" + "<fader>" + faderSeconds + ":-1:" + faderEnabled
262 + ":0:0</fader>" + "<UDN></UDN>";
265 setBinaryState(action, argument, value);
267 case CHANNEL_NIGHTMODE:
268 action = "ConfigureNightMode";
269 argument = "NightModeConfiguration";
270 String nightModeBrightness = String.valueOf(currentNightModeBrightness);
271 if (command.equals(OnOffType.ON)) {
272 value = "<startTime>0</startTime> \\n<nightMode>1</nightMode> \\n<endTime>23400</endTime> \\n<nightModeBrightness>"
273 + nightModeBrightness + "</nightModeBrightness> \\n";
274 } else if (command.equals(OnOffType.OFF)) {
275 value = "<startTime>0</startTime> \\n<nightMode>0</nightMode> \\n<endTime>23400</endTime> \\n<nightModeBrightness>"
276 + nightModeBrightness + "</nightModeBrightness> \\n";
278 setBinaryState(action, argument, value);
280 case CHANNEL_NIGHTMODEBRIGHTNESS:
281 action = "ConfigureNightMode";
282 argument = "NightModeConfiguration";
283 if (command instanceof PercentType) {
284 int newBrightness = ((PercentType) command).intValue();
285 String newNightModeBrightness = String.valueOf(newBrightness);
286 value = "<startTime>0</startTime> \\n<nightMode>" + currentNightModeState
287 + "</nightMode> \\n<endTime>23400</endTime> \\n<nightModeBrightness>"
288 + newNightModeBrightness + "</nightModeBrightness> \\n";
289 currentNightModeBrightness = newBrightness;
290 } else if (command instanceof IncreaseDecreaseType) {
292 String newNightModeBrightness = null;
293 switch (command.toString()) {
295 newBrightness = currentNightModeBrightness + DIM_STEPSIZE;
296 if (newBrightness > 100) {
299 newNightModeBrightness = String.valueOf(newBrightness);
300 currentBrightness = newBrightness;
303 newBrightness = currentNightModeBrightness - DIM_STEPSIZE;
304 if (newBrightness < 0) {
307 newNightModeBrightness = String.valueOf(newBrightness);
308 currentNightModeBrightness = newBrightness;
311 value = "<startTime>0</startTime> \\n<nightMode>" + currentNightModeState
312 + "</nightMode> \\n<endTime>23400</endTime> \\n<nightModeBrightness>"
313 + newNightModeBrightness + "</nightModeBrightness> \\n";
315 setBinaryState(action, argument, value);
322 public void onServiceSubscribed(@Nullable String service, boolean succeeded) {
323 if (service != null) {
324 logger.debug("WeMo {}: Subscription to service {} {}", getUDN(), service,
325 succeeded ? "succeeded" : "failed");
326 subscriptionState.put(service, succeeded);
331 public void onValueReceived(@Nullable String variable, @Nullable String value, @Nullable String service) {
332 logger.debug("Received pair '{}':'{}' (service '{}') for thing '{}'",
333 new Object[] { variable, value, service, this.getThing().getUID() });
334 updateStatus(ThingStatus.ONLINE);
335 if (variable != null && value != null) {
336 this.stateMap.put(variable, value);
338 if (variable != null && value != null) {
341 State state = value.equals("0") ? OnOffType.OFF : OnOffType.ON;
342 logger.debug("State '{}' for device '{}' received", state, getThing().getUID());
343 updateState(CHANNEL_BRIGHTNESS, state);
344 if (state.equals(OnOffType.OFF)) {
345 updateState(CHANNEL_TIMERSTART, OnOffType.OFF);
349 logger.debug("brightness '{}' for device '{}' received", value, getThing().getUID());
350 int newBrightnessValue = Integer.valueOf(value);
351 State newBrightnessState = new PercentType(newBrightnessValue);
352 String binaryState = this.stateMap.get("BinaryState");
353 if (binaryState != null) {
354 if (binaryState.equals("1")) {
355 updateState(CHANNEL_BRIGHTNESS, newBrightnessState);
358 currentBrightness = newBrightnessValue;
361 logger.debug("fader '{}' for device '{}' received", value, getThing().getUID());
362 String[] splitFader = value.split(":");
363 if (splitFader[0] != null) {
364 int faderSeconds = Integer.valueOf(splitFader[0]);
365 State faderMinutes = new DecimalType(faderSeconds / 60);
366 logger.debug("faderTime '{} minutes' for device '{}' received", faderMinutes,
367 getThing().getUID());
368 updateState(CHANNEL_FADERCOUNTDOWNTIME, faderMinutes);
370 if (splitFader[1] != null) {
371 State isTimerRunning = splitFader[1].equals("-1") ? OnOffType.OFF : OnOffType.ON;
372 logger.debug("isTimerRunning '{}' for device '{}' received", isTimerRunning,
373 getThing().getUID());
374 updateState(CHANNEL_TIMERSTART, isTimerRunning);
375 if (isTimerRunning.equals(OnOffType.ON)) {
376 updateState(CHANNEL_STATE, OnOffType.ON);
379 if (splitFader[2] != null) {
380 State isFaderEnabled = splitFader[1].equals("0") ? OnOffType.OFF : OnOffType.ON;
381 logger.debug("isFaderEnabled '{}' for device '{}' received", isFaderEnabled,
382 getThing().getUID());
383 updateState(CHANNEL_FADERENABLED, isFaderEnabled);
387 State nightModeState = value.equals("0") ? OnOffType.OFF : OnOffType.ON;
388 currentNightModeState = value;
389 logger.debug("nightModeState '{}' for device '{}' received", nightModeState, getThing().getUID());
390 updateState(CHANNEL_NIGHTMODE, nightModeState);
393 State startTimeState = getDateTimeState(value);
394 logger.debug("startTimeState '{}' for device '{}' received", startTimeState, getThing().getUID());
395 if (startTimeState != null) {
396 updateState(CHANNEL_STARTTIME, startTimeState);
400 State endTimeState = getDateTimeState(value);
401 logger.debug("endTimeState '{}' for device '{}' received", endTimeState, getThing().getUID());
402 if (endTimeState != null) {
403 updateState(CHANNEL_ENDTIME, endTimeState);
406 case "nightModeBrightness":
407 int nightModeBrightnessValue = Integer.valueOf(value);
408 currentNightModeBrightness = nightModeBrightnessValue;
409 State nightModeBrightnessState = new PercentType(nightModeBrightnessValue);
410 logger.debug("nightModeBrightnessState '{}' for device '{}' received", nightModeBrightnessState,
411 getThing().getUID());
412 updateState(CHANNEL_NIGHTMODEBRIGHTNESS, nightModeBrightnessState);
419 private synchronized void onSubscription() {
420 if (service.isRegistered(this)) {
421 logger.debug("Checking WeMo GENA subscription for '{}'", this);
422 String subscription = "basicevent1";
423 if (subscriptionState.get(subscription) == null) {
424 logger.debug("Setting up GENA subscription {}: Subscribing to service {}...", getUDN(), subscription);
425 service.addSubscription(this, subscription, SUBSCRIPTION_DURATION_SECONDS);
426 subscriptionState.put(subscription, true);
429 logger.debug("Setting up WeMo GENA subscription for '{}' FAILED - service.isRegistered(this) is FALSE",
434 private synchronized void removeSubscription() {
435 logger.debug("Removing WeMo GENA subscription for '{}'", this);
436 if (service.isRegistered(this)) {
437 String subscription = "basicevent1";
438 if (subscriptionState.get(subscription) != null) {
439 logger.debug("WeMo {}: Unsubscribing from service {}...", getUDN(), subscription);
440 service.removeSubscription(this, subscription);
442 subscriptionState = new HashMap<>();
443 service.unregisterParticipant(this);
447 private synchronized void onUpdate() {
448 ScheduledFuture<?> job = refreshJob;
449 if (job == null || job.isCancelled()) {
450 Configuration config = getThing().getConfiguration();
451 int refreshInterval = DEFAULT_REFRESH_INTERVALL_SECONDS;
452 Object refreshConfig = config.get("refresh");
453 if (refreshConfig != null) {
454 refreshInterval = ((BigDecimal) refreshConfig).intValue();
456 refreshJob = scheduler.scheduleWithFixedDelay(refreshRunnable, 10, refreshInterval, TimeUnit.SECONDS);
460 private boolean isUpnpDeviceRegistered() {
461 return service.isRegistered(this);
465 public String getUDN() {
466 return (String) this.getThing().getConfiguration().get(UDN);
470 * The {@link updateWemoState} polls the actual state of a WeMo device and
471 * calls {@link onValueReceived} to update the statemap and channels..
474 protected void updateWemoState() {
475 String action = "GetBinaryState";
476 String variable = null;
477 String actionService = "basicevent";
479 String soapHeader = "\"urn:Belkin:service:" + actionService + ":1#" + action + "\"";
480 String content = "<?xml version=\"1.0\"?>"
481 + "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
482 + "<s:Body>" + "<u:" + action + " xmlns:u=\"urn:Belkin:service:" + actionService + ":1\">" + "</u:"
483 + action + ">" + "</s:Body>" + "</s:Envelope>";
485 URL descriptorURL = service.getDescriptorURL(this);
486 String wemoURL = getWemoURL(descriptorURL, "basicevent");
488 if (wemoURL != null) {
489 String wemoCallResponse = wemoCall.executeCall(wemoURL, soapHeader, content);
490 if (wemoCallResponse != null) {
491 logger.trace("State response '{}' for device '{}' received", wemoCallResponse, getThing().getUID());
492 value = substringBetween(wemoCallResponse, "<BinaryState>", "</BinaryState>");
493 variable = "BinaryState";
494 logger.trace("New state '{}' for device '{}' received", value, getThing().getUID());
495 this.onValueReceived(variable, value, actionService + "1");
496 value = substringBetween(wemoCallResponse, "<brightness>", "</brightness>");
497 variable = "brightness";
498 logger.trace("New brightness '{}' for device '{}' received", value, getThing().getUID());
499 this.onValueReceived(variable, value, actionService + "1");
500 value = substringBetween(wemoCallResponse, "<fader>", "</fader>");
502 logger.trace("New fader value '{}' for device '{}' received", value, getThing().getUID());
503 this.onValueReceived(variable, value, actionService + "1");
506 } catch (Exception e) {
507 logger.debug("Failed to get actual state for device '{}': {}", getThing().getUID(), e.getMessage());
508 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
510 updateStatus(ThingStatus.ONLINE);
511 action = "GetNightModeConfiguration";
514 soapHeader = "\"urn:Belkin:service:" + actionService + ":1#" + action + "\"";
515 content = "<?xml version=\"1.0\"?>"
516 + "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
517 + "<s:Body>" + "<u:" + action + " xmlns:u=\"urn:Belkin:service:" + actionService + ":1\">" + "</u:"
518 + action + ">" + "</s:Body>" + "</s:Envelope>";
520 URL descriptorURL = service.getDescriptorURL(this);
521 String wemoURL = getWemoURL(descriptorURL, "basicevent");
523 if (wemoURL != null) {
524 String wemoCallResponse = wemoCall.executeCall(wemoURL, soapHeader, content);
525 if (wemoCallResponse != null) {
526 logger.trace("GetNightModeConfiguration response '{}' for device '{}' received", wemoCallResponse,
527 getThing().getUID());
528 value = substringBetween(wemoCallResponse, "<startTime>", "</startTime>");
529 variable = "startTime";
530 logger.trace("New startTime '{}' for device '{}' received", value, getThing().getUID());
531 this.onValueReceived(variable, value, actionService + "1");
532 value = substringBetween(wemoCallResponse, "<endTime>", "</endTime>");
533 variable = "endTime";
534 logger.trace("New endTime '{}' for device '{}' received", value, getThing().getUID());
535 this.onValueReceived(variable, value, actionService + "1");
536 value = substringBetween(wemoCallResponse, "<nightMode>", "</nightMode>");
537 variable = "nightMode";
538 logger.trace("New nightMode state '{}' for device '{}' received", value, getThing().getUID());
539 this.onValueReceived(variable, value, actionService + "1");
540 value = substringBetween(wemoCallResponse, "<nightModeBrightness>", "</nightModeBrightness>");
541 variable = "nightModeBrightness";
542 logger.trace("New nightModeBrightness '{}' for device '{}' received", value, getThing().getUID());
543 this.onValueReceived(variable, value, actionService + "1");
546 } catch (Exception e) {
547 logger.debug("Failed to get actual NightMode state for device '{}': {}", getThing().getUID(),
549 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
551 updateStatus(ThingStatus.ONLINE);
554 public @Nullable State getDateTimeState(String attributeValue) {
557 value = Long.parseLong(attributeValue);
558 } catch (NumberFormatException e) {
559 logger.error("Unable to parse attributeValue '{}' for device '{}'; expected long", attributeValue,
560 getThing().getUID());
563 ZonedDateTime zoned = ZonedDateTime.ofInstant(Instant.ofEpochSecond(value), TimeZone.getDefault().toZoneId());
564 State dateTimeState = new DateTimeType(zoned);
565 logger.trace("New attribute brewed '{}' received", dateTimeState);
566 return dateTimeState;
569 public void setBinaryState(String action, String argument, String value) {
571 String soapHeader = "\"urn:Belkin:service:basicevent:1#SetBinaryState\"";
572 String content = "<?xml version=\"1.0\"?>"
573 + "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
574 + "<s:Body>" + "<u:" + action + " xmlns:u=\"urn:Belkin:service:basicevent:1\">" + "<" + argument
575 + ">" + value + "</" + argument + ">" + "</u:" + action + ">" + "</s:Body>" + "</s:Envelope>";
577 URL descriptorURL = service.getDescriptorURL(this);
578 String wemoURL = getWemoURL(descriptorURL, "basicevent");
580 if (wemoURL != null) {
581 wemoCall.executeCall(wemoURL, soapHeader, content);
583 } catch (Exception e) {
584 logger.debug("Failed to set binaryState '{}' for device '{}': {}", value, getThing().getUID(),
586 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
590 public void setTimerStart(String action, String argument, String value) {
592 String soapHeader = "\"urn:Belkin:service:basicevent:1#SetBinaryState\"";
593 String content = "<?xml version=\"1.0\"?>"
594 + "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
595 + "<s:Body>" + "<u:SetBinaryState xmlns:u=\"urn:Belkin:service:basicevent:1\">" + value
596 + "</u:SetBinaryState>" + "</s:Body>" + "</s:Envelope>";
598 URL descriptorURL = service.getDescriptorURL(this);
599 String wemoURL = getWemoURL(descriptorURL, "basicevent");
601 if (wemoURL != null) {
602 wemoCall.executeCall(wemoURL, soapHeader, content);
604 } catch (Exception e) {
605 logger.debug("Failed to set binaryState '{}' for device '{}': {}", value, getThing().getUID(),
607 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
612 public void onStatusChanged(boolean status) {