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.*;
19 import java.time.Instant;
20 import java.time.ZonedDateTime;
21 import java.util.Collections;
22 import java.util.HashMap;
25 import java.util.TimeZone;
26 import java.util.concurrent.ScheduledFuture;
27 import java.util.concurrent.TimeUnit;
29 import org.eclipse.jdt.annotation.NonNullByDefault;
30 import org.eclipse.jdt.annotation.Nullable;
31 import org.openhab.binding.wemo.internal.http.WemoHttpCall;
32 import org.openhab.core.config.core.Configuration;
33 import org.openhab.core.io.transport.upnp.UpnpIOParticipant;
34 import org.openhab.core.io.transport.upnp.UpnpIOService;
35 import org.openhab.core.library.types.DateTimeType;
36 import org.openhab.core.library.types.DecimalType;
37 import org.openhab.core.library.types.IncreaseDecreaseType;
38 import org.openhab.core.library.types.OnOffType;
39 import org.openhab.core.library.types.PercentType;
40 import org.openhab.core.thing.ChannelUID;
41 import org.openhab.core.thing.Thing;
42 import org.openhab.core.thing.ThingStatus;
43 import org.openhab.core.thing.ThingStatusDetail;
44 import org.openhab.core.thing.ThingTypeUID;
45 import org.openhab.core.types.Command;
46 import org.openhab.core.types.RefreshType;
47 import org.openhab.core.types.State;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
52 * The {@link WemoDimmerHandler} is responsible for handling commands, which are
53 * sent to one of the channels and to update their states.
55 * @author Hans-Jörg Merk - Initial contribution
58 public class WemoDimmerHandler extends AbstractWemoHandler implements UpnpIOParticipant {
60 private final Logger logger = LoggerFactory.getLogger(WemoDimmerHandler.class);
62 public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_DIMMER);
64 private final Object upnpLock = new Object();
65 private final Object jobLock = new Object();
67 private final Map<String, String> stateMap = Collections.synchronizedMap(new HashMap<>());
69 private @Nullable UpnpIOService service;
71 private WemoHttpCall wemoCall;
73 private String host = "";
75 private Map<String, Boolean> subscriptionState = new HashMap<>();
77 private @Nullable ScheduledFuture<?> pollingJob;
79 private int currentBrightness;
80 private int currentNightModeBrightness;
81 private @Nullable String currentNightModeState;
83 * Set dimming stepsize to 5%
85 private static final int DIM_STEPSIZE = 5;
87 public WemoDimmerHandler(Thing thing, UpnpIOService upnpIOService, WemoHttpCall wemoHttpCaller) {
88 super(thing, wemoHttpCaller);
90 this.service = upnpIOService;
91 this.wemoCall = wemoHttpCaller;
93 logger.debug("Creating a WemoDimmerHandler for thing '{}'", getThing().getUID());
97 public void initialize() {
98 Configuration configuration = getConfig();
100 if (configuration.get(UDN) != null) {
101 logger.debug("Initializing WemoDimmerHandler for UDN '{}'", configuration.get(UDN));
102 UpnpIOService localService = service;
103 if (localService != null) {
104 localService.registerParticipant(this);
107 pollingJob = scheduler.scheduleWithFixedDelay(this::poll, 0, DEFAULT_REFRESH_INTERVALL_SECONDS,
109 updateStatus(ThingStatus.ONLINE);
111 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
112 "@text/config-status.error.missing-udn");
113 logger.debug("Cannot initalize WemoDimmerHandler. UDN not set.");
118 public void dispose() {
119 logger.debug("WeMoDimmerHandler disposed.");
121 ScheduledFuture<?> job = this.pollingJob;
122 if (job != null && !job.isCancelled()) {
125 this.pollingJob = null;
126 removeSubscription();
129 private void poll() {
130 synchronized (jobLock) {
131 if (pollingJob == null) {
135 logger.debug("Polling job");
137 // Check if the Wemo device is set in the UPnP service registry
138 // If not, set the thing state to ONLINE/CONFIG-PENDING and wait for the next poll
139 if (!isUpnpDeviceRegistered()) {
140 logger.debug("UPnP device {} not yet registered", getUDN());
141 updateStatus(ThingStatus.ONLINE, ThingStatusDetail.CONFIGURATION_PENDING,
142 "@text/config-status.pending.device-not-registered [\"" + getUDN() + "\"]");
143 synchronized (upnpLock) {
144 subscriptionState = new HashMap<>();
148 updateStatus(ThingStatus.ONLINE);
151 } catch (Exception e) {
152 logger.debug("Exception during poll: {}", e.getMessage(), e);
158 public void handleCommand(ChannelUID channelUID, Command command) {
159 logger.trace("Command '{}' received for channel '{}'", command, channelUID);
160 if (command instanceof RefreshType) {
163 } catch (Exception e) {
164 logger.debug("Exception during poll", e);
167 String action = "SetBinaryState";
168 String argument = "BinaryState";
170 String timeStamp = null;
171 switch (channelUID.getId()) {
172 case CHANNEL_BRIGHTNESS:
173 String binaryState = this.stateMap.get("BinaryState");
174 if (command instanceof OnOffType) {
175 value = command.equals(OnOffType.OFF) ? "0" : "1";
176 setBinaryState(action, argument, value);
177 if (command.equals(OnOffType.OFF)) {
178 State brightnessState = new PercentType("0");
179 updateState(CHANNEL_BRIGHTNESS, brightnessState);
180 updateState(CHANNEL_TIMERSTART, OnOffType.OFF);
182 State brightnessState = new PercentType(currentBrightness);
183 updateState(CHANNEL_BRIGHTNESS, brightnessState);
185 } else if (command instanceof PercentType) {
186 int newBrightness = ((PercentType) command).intValue();
187 value = String.valueOf(newBrightness);
188 currentBrightness = newBrightness;
189 argument = "brightness";
190 if ("0".equals(value)) {
192 argument = "brightness";
193 setBinaryState(action, argument, "1");
195 argument = "BinaryState";
196 setBinaryState(action, argument, "0");
197 } else if ("0".equals(binaryState)) {
198 argument = "BinaryState";
199 setBinaryState(action, argument, "1");
201 argument = "brightness";
202 setBinaryState(action, argument, value);
203 } else if (command instanceof IncreaseDecreaseType) {
205 switch (command.toString()) {
207 newBrightness = currentBrightness + DIM_STEPSIZE;
208 if (newBrightness > 100) {
211 value = String.valueOf(newBrightness);
212 currentBrightness = newBrightness;
215 newBrightness = currentBrightness - DIM_STEPSIZE;
216 if (newBrightness < 0) {
219 value = String.valueOf(newBrightness);
220 currentBrightness = newBrightness;
223 argument = "brightness";
224 if ("0".equals(value)) {
226 argument = "brightness";
227 setBinaryState(action, argument, "1");
229 argument = "BinaryState";
230 setBinaryState(action, argument, "0");
231 } else if ("0".equals(binaryState)) {
232 argument = "BinaryState";
233 setBinaryState(action, argument, "1");
235 argument = "brightness";
236 setBinaryState(action, argument, value);
239 case CHANNEL_FADERCOUNTDOWNTIME:
241 if (command instanceof DecimalType) {
242 int commandValue = Integer.valueOf(String.valueOf(command));
243 commandValue = commandValue * 60;
244 String commandString = String.valueOf(commandValue);
245 value = "<BinaryState></BinaryState>" + "<Duration></Duration>" + "<EndAction></EndAction>"
246 + "<brightness></brightness>" + "<fader>" + commandString + ":-1:1:0:0</fader>"
248 setBinaryState(action, argument, value);
251 case CHANNEL_FADERENABLED:
253 if (command.equals(OnOffType.ON)) {
254 value = "<BinaryState></BinaryState>" + "<Duration></Duration>" + "<EndAction></EndAction>"
255 + "<brightness></brightness>" + "<fader>600:-1:1:0:0</fader>" + "<UDN></UDN>";
256 } else if (command.equals(OnOffType.OFF)) {
257 value = "<BinaryState></BinaryState>" + "<Duration></Duration>" + "<EndAction></EndAction>"
258 + "<brightness></brightness>" + "<fader>600:-1:0:0:0</fader>" + "<UDN></UDN>";
260 setBinaryState(action, argument, value);
262 case CHANNEL_TIMERSTART:
264 long ts = System.currentTimeMillis() / 1000;
265 timeStamp = String.valueOf(ts);
266 logger.info("timestamp '{}' created", timeStamp);
267 String faderSeconds = null;
268 String faderEnabled = null;
269 String fader = this.stateMap.get("fader");
271 String[] splitFader = fader.split(":");
272 if (splitFader[0] != null) {
273 faderSeconds = splitFader[0];
275 if (splitFader[0] != null) {
276 faderEnabled = splitFader[2];
279 if (faderSeconds != null && faderEnabled != null) {
280 if (OnOffType.ON.equals(command)) {
281 value = "<BinaryState></BinaryState>" + "<Duration></Duration>" + "<EndAction></EndAction>"
282 + "<brightness></brightness>" + "<fader>" + faderSeconds + ":" + timeStamp + ":"
283 + faderEnabled + ":0:0</fader>" + "<UDN></UDN>";
284 updateState(CHANNEL_STATE, OnOffType.ON);
285 } else if (OnOffType.OFF.equals(command)) {
286 value = "<BinaryState></BinaryState>" + "<Duration></Duration>" + "<EndAction></EndAction>"
287 + "<brightness></brightness>" + "<fader>" + faderSeconds + ":-1:" + faderEnabled
288 + ":0:0</fader>" + "<UDN></UDN>";
291 setBinaryState(action, argument, value);
293 case CHANNEL_NIGHTMODE:
294 action = "ConfigureNightMode";
295 argument = "NightModeConfiguration";
296 String nightModeBrightness = String.valueOf(currentNightModeBrightness);
297 if (OnOffType.ON.equals(command)) {
298 value = "<startTime>0</startTime> \\n<nightMode>1</nightMode> \\n<endTime>23400</endTime> \\n<nightModeBrightness>"
299 + nightModeBrightness + "</nightModeBrightness> \\n";
300 } else if (OnOffType.OFF.equals(command)) {
301 value = "<startTime>0</startTime> \\n<nightMode>0</nightMode> \\n<endTime>23400</endTime> \\n<nightModeBrightness>"
302 + nightModeBrightness + "</nightModeBrightness> \\n";
304 setBinaryState(action, argument, value);
306 case CHANNEL_NIGHTMODEBRIGHTNESS:
307 action = "ConfigureNightMode";
308 argument = "NightModeConfiguration";
309 if (command instanceof PercentType) {
310 int newBrightness = ((PercentType) command).intValue();
311 String newNightModeBrightness = String.valueOf(newBrightness);
312 value = "<startTime>0</startTime> \\n<nightMode>" + currentNightModeState
313 + "</nightMode> \\n<endTime>23400</endTime> \\n<nightModeBrightness>"
314 + newNightModeBrightness + "</nightModeBrightness> \\n";
315 currentNightModeBrightness = newBrightness;
316 } else if (command instanceof IncreaseDecreaseType) {
318 String newNightModeBrightness = null;
319 switch (command.toString()) {
321 newBrightness = currentNightModeBrightness + DIM_STEPSIZE;
322 if (newBrightness > 100) {
325 newNightModeBrightness = String.valueOf(newBrightness);
326 currentBrightness = newBrightness;
329 newBrightness = currentNightModeBrightness - DIM_STEPSIZE;
330 if (newBrightness < 0) {
333 newNightModeBrightness = String.valueOf(newBrightness);
334 currentNightModeBrightness = newBrightness;
337 value = "<startTime>0</startTime> \\n<nightMode>" + currentNightModeState
338 + "</nightMode> \\n<endTime>23400</endTime> \\n<nightModeBrightness>"
339 + newNightModeBrightness + "</nightModeBrightness> \\n";
341 setBinaryState(action, argument, value);
348 public void onServiceSubscribed(@Nullable String service, boolean succeeded) {
349 if (service != null) {
350 logger.debug("WeMo {}: Subscription to service {} {}", getUDN(), service,
351 succeeded ? "succeeded" : "failed");
352 subscriptionState.put(service, succeeded);
357 public void onValueReceived(@Nullable String variable, @Nullable String value, @Nullable String service) {
358 logger.debug("Received pair '{}':'{}' (service '{}') for thing '{}'",
359 new Object[] { variable, value, service, this.getThing().getUID() });
360 updateStatus(ThingStatus.ONLINE);
361 if (variable != null && value != null) {
362 String oldBinaryState = this.stateMap.get("BinaryState");
363 this.stateMap.put(variable, value);
366 if (oldBinaryState == null || !oldBinaryState.equals(value)) {
367 State state = "0".equals(value) ? OnOffType.OFF : OnOffType.ON;
368 logger.debug("State '{}' for device '{}' received", state, getThing().getUID());
369 updateState(CHANNEL_BRIGHTNESS, state);
370 if (state.equals(OnOffType.OFF)) {
371 updateState(CHANNEL_TIMERSTART, OnOffType.OFF);
376 logger.debug("brightness '{}' for device '{}' received", value, getThing().getUID());
377 int newBrightnessValue = Integer.valueOf(value);
378 State newBrightnessState = new PercentType(newBrightnessValue);
379 String binaryState = this.stateMap.get("BinaryState");
380 if (binaryState != null) {
381 if ("1".equals(binaryState)) {
382 updateState(CHANNEL_BRIGHTNESS, newBrightnessState);
385 currentBrightness = newBrightnessValue;
388 logger.debug("fader '{}' for device '{}' received", value, getThing().getUID());
389 String[] splitFader = value.split(":");
390 if (splitFader[0] != null) {
391 int faderSeconds = Integer.valueOf(splitFader[0]);
392 State faderMinutes = new DecimalType(faderSeconds / 60);
393 logger.debug("faderTime '{} minutes' for device '{}' received", faderMinutes,
394 getThing().getUID());
395 updateState(CHANNEL_FADERCOUNTDOWNTIME, faderMinutes);
397 if (splitFader[1] != null) {
398 State isTimerRunning = splitFader[1].equals("-1") ? OnOffType.OFF : OnOffType.ON;
399 logger.debug("isTimerRunning '{}' for device '{}' received", isTimerRunning,
400 getThing().getUID());
401 updateState(CHANNEL_TIMERSTART, isTimerRunning);
402 if (isTimerRunning.equals(OnOffType.ON)) {
403 updateState(CHANNEL_STATE, OnOffType.ON);
406 if (splitFader[2] != null) {
407 State isFaderEnabled = splitFader[1].equals("0") ? OnOffType.OFF : OnOffType.ON;
408 logger.debug("isFaderEnabled '{}' for device '{}' received", isFaderEnabled,
409 getThing().getUID());
410 updateState(CHANNEL_FADERENABLED, isFaderEnabled);
414 State nightModeState = "0".equals(value) ? OnOffType.OFF : OnOffType.ON;
415 currentNightModeState = value;
416 logger.debug("nightModeState '{}' for device '{}' received", nightModeState, getThing().getUID());
417 updateState(CHANNEL_NIGHTMODE, nightModeState);
420 State startTimeState = getDateTimeState(value);
421 logger.debug("startTimeState '{}' for device '{}' received", startTimeState, getThing().getUID());
422 if (startTimeState != null) {
423 updateState(CHANNEL_STARTTIME, startTimeState);
427 State endTimeState = getDateTimeState(value);
428 logger.debug("endTimeState '{}' for device '{}' received", endTimeState, getThing().getUID());
429 if (endTimeState != null) {
430 updateState(CHANNEL_ENDTIME, endTimeState);
433 case "nightModeBrightness":
434 int nightModeBrightnessValue = Integer.valueOf(value);
435 currentNightModeBrightness = nightModeBrightnessValue;
436 State nightModeBrightnessState = new PercentType(nightModeBrightnessValue);
437 logger.debug("nightModeBrightnessState '{}' for device '{}' received", nightModeBrightnessState,
438 getThing().getUID());
439 updateState(CHANNEL_NIGHTMODEBRIGHTNESS, nightModeBrightnessState);
445 private synchronized void addSubscription() {
446 UpnpIOService localService = service;
447 if (localService != null) {
448 if (localService.isRegistered(this)) {
449 logger.debug("Checking WeMo GENA subscription for '{}'", getThing().getUID());
450 String subscription = BASICEVENT;
451 if (subscriptionState.get(subscription) == null) {
452 logger.debug("Setting up GENA subscription {}: Subscribing to service {}...", getUDN(),
454 localService.addSubscription(this, subscription, SUBSCRIPTION_DURATION_SECONDS);
455 subscriptionState.put(subscription, true);
458 logger.debug("Setting up WeMo GENA subscription for '{}' FAILED - service.isRegistered(this) is FALSE",
459 getThing().getUID());
464 private synchronized void removeSubscription() {
465 logger.debug("Removing WeMo GENA subscription for '{}'", getThing().getUID());
466 UpnpIOService localService = service;
467 if (localService != null) {
468 if (localService.isRegistered(this)) {
469 String subscription = BASICEVENT;
470 if (subscriptionState.get(subscription) != null) {
471 logger.debug("WeMo {}: Unsubscribing from service {}...", getUDN(), subscription);
472 localService.removeSubscription(this, subscription);
474 subscriptionState = new HashMap<>();
475 localService.unregisterParticipant(this);
480 private boolean isUpnpDeviceRegistered() {
481 UpnpIOService localService = service;
482 if (localService != null) {
483 return localService.isRegistered(this);
489 public String getUDN() {
490 return (String) this.getThing().getConfiguration().get(UDN);
494 * The {@link updateWemoState} polls the actual state of a WeMo device and
495 * calls {@link onValueReceived} to update the statemap and channels..
498 protected void updateWemoState() {
499 String localHost = getHost();
500 if (localHost.isEmpty()) {
501 logger.error("Failed to get actual state for device '{}': IP address missing", getThing().getUID());
502 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
503 "@text/config-status.error.missing-ip");
506 String wemoURL = getWemoURL(localHost, BASICACTION);
507 if (wemoURL == null) {
508 logger.error("Failed to get actual state for device '{}': URL cannot be created", getThing().getUID());
509 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
510 "@text/config-status.error.missing-url");
513 String action = "GetBinaryState";
514 String variable = null;
515 String actionService = BASICACTION;
517 String soapHeader = "\"urn:Belkin:service:" + actionService + ":1#" + action + "\"";
518 String content = createStateRequestContent(action, actionService);
520 String wemoCallResponse = wemoCall.executeCall(wemoURL, soapHeader, content);
521 if (wemoCallResponse != null) {
522 if (logger.isTraceEnabled()) {
523 logger.trace("wemoCall to URL '{}' for device '{}'", wemoURL, getThing().getUID());
524 logger.trace("wemoCall with soapHeader '{}' for device '{}'", soapHeader, getThing().getUID());
525 logger.trace("wemoCall with content '{}' for device '{}'", content, getThing().getUID());
526 logger.trace("wemoCall with response '{}' for device '{}'", wemoCallResponse, getThing().getUID());
528 value = substringBetween(wemoCallResponse, "<BinaryState>", "</BinaryState>");
529 variable = "BinaryState";
530 this.onValueReceived(variable, value, actionService + "1");
531 value = substringBetween(wemoCallResponse, "<brightness>", "</brightness>");
532 variable = "brightness";
533 this.onValueReceived(variable, value, actionService + "1");
534 value = substringBetween(wemoCallResponse, "<fader>", "</fader>");
536 this.onValueReceived(variable, value, actionService + "1");
537 updateStatus(ThingStatus.ONLINE);
539 } catch (Exception e) {
540 logger.debug("Failed to get actual state for device '{}': {}", getThing().getUID(), e.getMessage());
541 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
543 action = "GetNightModeConfiguration";
546 soapHeader = "\"urn:Belkin:service:" + actionService + ":1#" + action + "\"";
547 content = createStateRequestContent(action, actionService);
549 String wemoCallResponse = wemoCall.executeCall(wemoURL, soapHeader, content);
550 if (wemoCallResponse != null) {
551 if (logger.isTraceEnabled()) {
552 logger.trace("wemoCall to URL '{}' for device '{}'", wemoURL, getThing().getUID());
553 logger.trace("wemoCall with soapHeader '{}' for device '{}'", soapHeader, getThing().getUID());
554 logger.trace("wemoCall with content '{}' for device '{}'", content, getThing().getUID());
555 logger.trace("wemoCall with response '{}' for device '{}'", wemoCallResponse, getThing().getUID());
557 value = substringBetween(wemoCallResponse, "<startTime>", "</startTime>");
558 variable = "startTime";
559 this.onValueReceived(variable, value, actionService + "1");
560 value = substringBetween(wemoCallResponse, "<endTime>", "</endTime>");
561 variable = "endTime";
562 this.onValueReceived(variable, value, actionService + "1");
563 value = substringBetween(wemoCallResponse, "<nightMode>", "</nightMode>");
564 variable = "nightMode";
565 this.onValueReceived(variable, value, actionService + "1");
566 value = substringBetween(wemoCallResponse, "<nightModeBrightness>", "</nightModeBrightness>");
567 variable = "nightModeBrightness";
568 this.onValueReceived(variable, value, actionService + "1");
569 updateStatus(ThingStatus.ONLINE);
572 } catch (Exception e) {
573 logger.debug("Failed to get actual NightMode state for device '{}': {}", getThing().getUID(),
575 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
579 public @Nullable State getDateTimeState(String attributeValue) {
582 value = Long.parseLong(attributeValue);
583 } catch (NumberFormatException e) {
584 logger.error("Unable to parse attributeValue '{}' for device '{}'; expected long", attributeValue,
585 getThing().getUID());
588 ZonedDateTime zoned = ZonedDateTime.ofInstant(Instant.ofEpochSecond(value), TimeZone.getDefault().toZoneId());
589 State dateTimeState = new DateTimeType(zoned);
590 logger.trace("New attribute brewed '{}' received", dateTimeState);
591 return dateTimeState;
594 public void setBinaryState(String action, String argument, String value) {
595 String localHost = getHost();
596 if (localHost.isEmpty()) {
597 logger.error("Failed to set binary state for device '{}': IP address missing", getThing().getUID());
598 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
599 "@text/config-status.error.missing-ip");
602 String wemoURL = getWemoURL(localHost, BASICACTION);
603 if (wemoURL == null) {
604 logger.error("Failed to set binary state for device '{}': URL cannot be created", getThing().getUID());
605 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
606 "@text/config-status.error.missing-url");
610 String soapHeader = "\"urn:Belkin:service:basicevent:1#SetBinaryState\"";
611 String content = "<?xml version=\"1.0\"?>"
612 + "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
613 + "<s:Body>" + "<u:" + action + " xmlns:u=\"urn:Belkin:service:basicevent:1\">" + "<" + argument
614 + ">" + value + "</" + argument + ">" + "</u:" + action + ">" + "</s:Body>" + "</s:Envelope>";
616 String wemoCallResponse = wemoCall.executeCall(wemoURL, soapHeader, content);
617 if (wemoCallResponse != null && logger.isTraceEnabled()) {
618 logger.trace("wemoCall to URL '{}' for device '{}'", wemoURL, getThing().getUID());
619 logger.trace("wemoCall with soapHeader '{}' for device '{}'", soapHeader, getThing().getUID());
620 logger.trace("wemoCall with content '{}' for device '{}'", content, getThing().getUID());
621 logger.trace("wemoCall with response '{}' for device '{}'", wemoCallResponse, getThing().getUID());
623 } catch (Exception e) {
624 logger.debug("Failed to set binaryState '{}' for device '{}': {}", value, getThing().getUID(),
626 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
630 public void setTimerStart(String action, String argument, String value) {
631 String localHost = getHost();
632 if (localHost.isEmpty()) {
633 logger.error("Failed to set timerStart for device '{}': IP address missing", getThing().getUID());
634 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
635 "@text/config-status.error.missing-ip");
638 String wemoURL = getWemoURL(localHost, BASICACTION);
639 if (wemoURL == null) {
640 logger.error("Failed to set timerStart for device '{}': URL cannot be created", getThing().getUID());
641 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
642 "@text/config-status.error.missing-url");
646 String soapHeader = "\"urn:Belkin:service:basicevent:1#SetBinaryState\"";
647 String content = "<?xml version=\"1.0\"?>"
648 + "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
649 + "<s:Body>" + "<u:SetBinaryState xmlns:u=\"urn:Belkin:service:basicevent:1\">" + value
650 + "</u:SetBinaryState>" + "</s:Body>" + "</s:Envelope>";
651 String wemoCallResponse = wemoCall.executeCall(wemoURL, soapHeader, content);
652 if (wemoCallResponse != null && logger.isTraceEnabled()) {
653 logger.trace("wemoCall to URL '{}' for device '{}'", wemoURL, getThing().getUID());
654 logger.trace("wemoCall with soapHeader '{}' for device '{}'", soapHeader, getThing().getUID());
655 logger.trace("wemoCall with content '{}' for device '{}'", content, getThing().getUID());
656 logger.trace("wemoCall with response '{}' for device '{}'", wemoCallResponse, getThing().getUID());
658 } catch (Exception e) {
659 logger.debug("Failed to set timerStart '{}' for device '{}': {}", value, getThing().getUID(),
661 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
665 public String getHost() {
666 String localHost = host;
667 if (!localHost.isEmpty()) {
670 UpnpIOService localService = service;
671 if (localService != null) {
672 URL descriptorURL = localService.getDescriptorURL(this);
673 if (descriptorURL != null) {
674 return descriptorURL.getHost();
681 public void onStatusChanged(boolean status) {