2 * Copyright (c) 2010-2022 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 String oldBinaryState = this.stateMap.get("BinaryState");
337 this.stateMap.put(variable, value);
340 if (oldBinaryState == null || !oldBinaryState.equals(value)) {
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);
350 logger.debug("brightness '{}' for device '{}' received", value, getThing().getUID());
351 int newBrightnessValue = Integer.valueOf(value);
352 State newBrightnessState = new PercentType(newBrightnessValue);
353 String binaryState = this.stateMap.get("BinaryState");
354 if (binaryState != null) {
355 if (binaryState.equals("1")) {
356 updateState(CHANNEL_BRIGHTNESS, newBrightnessState);
359 currentBrightness = newBrightnessValue;
362 logger.debug("fader '{}' for device '{}' received", value, getThing().getUID());
363 String[] splitFader = value.split(":");
364 if (splitFader[0] != null) {
365 int faderSeconds = Integer.valueOf(splitFader[0]);
366 State faderMinutes = new DecimalType(faderSeconds / 60);
367 logger.debug("faderTime '{} minutes' for device '{}' received", faderMinutes,
368 getThing().getUID());
369 updateState(CHANNEL_FADERCOUNTDOWNTIME, faderMinutes);
371 if (splitFader[1] != null) {
372 State isTimerRunning = splitFader[1].equals("-1") ? OnOffType.OFF : OnOffType.ON;
373 logger.debug("isTimerRunning '{}' for device '{}' received", isTimerRunning,
374 getThing().getUID());
375 updateState(CHANNEL_TIMERSTART, isTimerRunning);
376 if (isTimerRunning.equals(OnOffType.ON)) {
377 updateState(CHANNEL_STATE, OnOffType.ON);
380 if (splitFader[2] != null) {
381 State isFaderEnabled = splitFader[1].equals("0") ? OnOffType.OFF : OnOffType.ON;
382 logger.debug("isFaderEnabled '{}' for device '{}' received", isFaderEnabled,
383 getThing().getUID());
384 updateState(CHANNEL_FADERENABLED, isFaderEnabled);
388 State nightModeState = value.equals("0") ? OnOffType.OFF : OnOffType.ON;
389 currentNightModeState = value;
390 logger.debug("nightModeState '{}' for device '{}' received", nightModeState, getThing().getUID());
391 updateState(CHANNEL_NIGHTMODE, nightModeState);
394 State startTimeState = getDateTimeState(value);
395 logger.debug("startTimeState '{}' for device '{}' received", startTimeState, getThing().getUID());
396 if (startTimeState != null) {
397 updateState(CHANNEL_STARTTIME, startTimeState);
401 State endTimeState = getDateTimeState(value);
402 logger.debug("endTimeState '{}' for device '{}' received", endTimeState, getThing().getUID());
403 if (endTimeState != null) {
404 updateState(CHANNEL_ENDTIME, endTimeState);
407 case "nightModeBrightness":
408 int nightModeBrightnessValue = Integer.valueOf(value);
409 currentNightModeBrightness = nightModeBrightnessValue;
410 State nightModeBrightnessState = new PercentType(nightModeBrightnessValue);
411 logger.debug("nightModeBrightnessState '{}' for device '{}' received", nightModeBrightnessState,
412 getThing().getUID());
413 updateState(CHANNEL_NIGHTMODEBRIGHTNESS, nightModeBrightnessState);
420 private synchronized void onSubscription() {
421 if (service.isRegistered(this)) {
422 logger.debug("Checking WeMo GENA subscription for '{}'", this);
423 String subscription = "basicevent1";
424 if (subscriptionState.get(subscription) == null) {
425 logger.debug("Setting up GENA subscription {}: Subscribing to service {}...", getUDN(), subscription);
426 service.addSubscription(this, subscription, SUBSCRIPTION_DURATION_SECONDS);
427 subscriptionState.put(subscription, true);
430 logger.debug("Setting up WeMo GENA subscription for '{}' FAILED - service.isRegistered(this) is FALSE",
435 private synchronized void removeSubscription() {
436 logger.debug("Removing WeMo GENA subscription for '{}'", this);
437 if (service.isRegistered(this)) {
438 String subscription = "basicevent1";
439 if (subscriptionState.get(subscription) != null) {
440 logger.debug("WeMo {}: Unsubscribing from service {}...", getUDN(), subscription);
441 service.removeSubscription(this, subscription);
443 subscriptionState = new HashMap<>();
444 service.unregisterParticipant(this);
448 private synchronized void onUpdate() {
449 ScheduledFuture<?> job = refreshJob;
450 if (job == null || job.isCancelled()) {
451 Configuration config = getThing().getConfiguration();
452 int refreshInterval = DEFAULT_REFRESH_INTERVALL_SECONDS;
453 Object refreshConfig = config.get("refresh");
454 if (refreshConfig != null) {
455 refreshInterval = ((BigDecimal) refreshConfig).intValue();
457 refreshJob = scheduler.scheduleWithFixedDelay(refreshRunnable, 10, refreshInterval, TimeUnit.SECONDS);
461 private boolean isUpnpDeviceRegistered() {
462 return service.isRegistered(this);
466 public String getUDN() {
467 return (String) this.getThing().getConfiguration().get(UDN);
471 * The {@link updateWemoState} polls the actual state of a WeMo device and
472 * calls {@link onValueReceived} to update the statemap and channels..
475 protected void updateWemoState() {
476 String action = "GetBinaryState";
477 String variable = null;
478 String actionService = "basicevent";
480 String soapHeader = "\"urn:Belkin:service:" + actionService + ":1#" + action + "\"";
481 String content = "<?xml version=\"1.0\"?>"
482 + "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
483 + "<s:Body>" + "<u:" + action + " xmlns:u=\"urn:Belkin:service:" + actionService + ":1\">" + "</u:"
484 + action + ">" + "</s:Body>" + "</s:Envelope>";
486 URL descriptorURL = service.getDescriptorURL(this);
487 String wemoURL = getWemoURL(descriptorURL, "basicevent");
489 if (wemoURL != null) {
490 String wemoCallResponse = wemoCall.executeCall(wemoURL, soapHeader, content);
491 if (wemoCallResponse != null) {
492 logger.trace("State response '{}' for device '{}' received", wemoCallResponse, getThing().getUID());
493 value = substringBetween(wemoCallResponse, "<BinaryState>", "</BinaryState>");
494 variable = "BinaryState";
495 logger.trace("New state '{}' for device '{}' received", value, getThing().getUID());
496 this.onValueReceived(variable, value, actionService + "1");
497 value = substringBetween(wemoCallResponse, "<brightness>", "</brightness>");
498 variable = "brightness";
499 logger.trace("New brightness '{}' for device '{}' received", value, getThing().getUID());
500 this.onValueReceived(variable, value, actionService + "1");
501 value = substringBetween(wemoCallResponse, "<fader>", "</fader>");
503 logger.trace("New fader value '{}' for device '{}' received", value, getThing().getUID());
504 this.onValueReceived(variable, value, actionService + "1");
507 } catch (Exception e) {
508 logger.debug("Failed to get actual state for device '{}': {}", getThing().getUID(), e.getMessage());
509 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
511 updateStatus(ThingStatus.ONLINE);
512 action = "GetNightModeConfiguration";
515 soapHeader = "\"urn:Belkin:service:" + actionService + ":1#" + action + "\"";
516 content = "<?xml version=\"1.0\"?>"
517 + "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
518 + "<s:Body>" + "<u:" + action + " xmlns:u=\"urn:Belkin:service:" + actionService + ":1\">" + "</u:"
519 + action + ">" + "</s:Body>" + "</s:Envelope>";
521 URL descriptorURL = service.getDescriptorURL(this);
522 String wemoURL = getWemoURL(descriptorURL, "basicevent");
524 if (wemoURL != null) {
525 String wemoCallResponse = wemoCall.executeCall(wemoURL, soapHeader, content);
526 if (wemoCallResponse != null) {
527 logger.trace("GetNightModeConfiguration response '{}' for device '{}' received", wemoCallResponse,
528 getThing().getUID());
529 value = substringBetween(wemoCallResponse, "<startTime>", "</startTime>");
530 variable = "startTime";
531 logger.trace("New startTime '{}' for device '{}' received", value, getThing().getUID());
532 this.onValueReceived(variable, value, actionService + "1");
533 value = substringBetween(wemoCallResponse, "<endTime>", "</endTime>");
534 variable = "endTime";
535 logger.trace("New endTime '{}' for device '{}' received", value, getThing().getUID());
536 this.onValueReceived(variable, value, actionService + "1");
537 value = substringBetween(wemoCallResponse, "<nightMode>", "</nightMode>");
538 variable = "nightMode";
539 logger.trace("New nightMode state '{}' for device '{}' received", value, getThing().getUID());
540 this.onValueReceived(variable, value, actionService + "1");
541 value = substringBetween(wemoCallResponse, "<nightModeBrightness>", "</nightModeBrightness>");
542 variable = "nightModeBrightness";
543 logger.trace("New nightModeBrightness '{}' for device '{}' received", value, getThing().getUID());
544 this.onValueReceived(variable, value, actionService + "1");
547 } catch (Exception e) {
548 logger.debug("Failed to get actual NightMode state for device '{}': {}", getThing().getUID(),
550 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
552 updateStatus(ThingStatus.ONLINE);
555 public @Nullable State getDateTimeState(String attributeValue) {
558 value = Long.parseLong(attributeValue);
559 } catch (NumberFormatException e) {
560 logger.error("Unable to parse attributeValue '{}' for device '{}'; expected long", attributeValue,
561 getThing().getUID());
564 ZonedDateTime zoned = ZonedDateTime.ofInstant(Instant.ofEpochSecond(value), TimeZone.getDefault().toZoneId());
565 State dateTimeState = new DateTimeType(zoned);
566 logger.trace("New attribute brewed '{}' received", dateTimeState);
567 return dateTimeState;
570 public void setBinaryState(String action, String argument, String value) {
572 String soapHeader = "\"urn:Belkin:service:basicevent:1#SetBinaryState\"";
573 String content = "<?xml version=\"1.0\"?>"
574 + "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
575 + "<s:Body>" + "<u:" + action + " xmlns:u=\"urn:Belkin:service:basicevent:1\">" + "<" + argument
576 + ">" + value + "</" + argument + ">" + "</u:" + action + ">" + "</s:Body>" + "</s:Envelope>";
578 URL descriptorURL = service.getDescriptorURL(this);
579 String wemoURL = getWemoURL(descriptorURL, "basicevent");
581 if (wemoURL != null) {
582 wemoCall.executeCall(wemoURL, soapHeader, content);
584 } catch (Exception e) {
585 logger.debug("Failed to set binaryState '{}' for device '{}': {}", value, getThing().getUID(),
587 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
591 public void setTimerStart(String action, String argument, String value) {
593 String soapHeader = "\"urn:Belkin:service:basicevent:1#SetBinaryState\"";
594 String content = "<?xml version=\"1.0\"?>"
595 + "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
596 + "<s:Body>" + "<u:SetBinaryState xmlns:u=\"urn:Belkin:service:basicevent:1\">" + value
597 + "</u:SetBinaryState>" + "</s:Body>" + "</s:Envelope>";
599 URL descriptorURL = service.getDescriptorURL(this);
600 String wemoURL = getWemoURL(descriptorURL, "basicevent");
602 if (wemoURL != null) {
603 wemoCall.executeCall(wemoURL, soapHeader, content);
605 } catch (Exception e) {
606 logger.debug("Failed to set binaryState '{}' for device '{}': {}", value, getThing().getUID(),
608 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
613 public void onStatusChanged(boolean status) {