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.time.Instant;
19 import java.time.ZonedDateTime;
20 import java.util.Collections;
21 import java.util.HashMap;
24 import java.util.TimeZone;
25 import java.util.concurrent.ScheduledFuture;
26 import java.util.concurrent.TimeUnit;
28 import org.eclipse.jdt.annotation.NonNullByDefault;
29 import org.eclipse.jdt.annotation.Nullable;
30 import org.openhab.binding.wemo.internal.http.WemoHttpCall;
31 import org.openhab.core.config.core.Configuration;
32 import org.openhab.core.io.transport.upnp.UpnpIOService;
33 import org.openhab.core.library.types.DateTimeType;
34 import org.openhab.core.library.types.DecimalType;
35 import org.openhab.core.library.types.IncreaseDecreaseType;
36 import org.openhab.core.library.types.OnOffType;
37 import org.openhab.core.library.types.PercentType;
38 import org.openhab.core.thing.ChannelUID;
39 import org.openhab.core.thing.Thing;
40 import org.openhab.core.thing.ThingStatus;
41 import org.openhab.core.thing.ThingStatusDetail;
42 import org.openhab.core.thing.ThingTypeUID;
43 import org.openhab.core.types.Command;
44 import org.openhab.core.types.RefreshType;
45 import org.openhab.core.types.State;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
50 * The {@link WemoDimmerHandler} is responsible for handling commands, which are
51 * sent to one of the channels and to update their states.
53 * @author Hans-Jörg Merk - Initial contribution
56 public class WemoDimmerHandler extends WemoBaseThingHandler {
58 private final Logger logger = LoggerFactory.getLogger(WemoDimmerHandler.class);
60 public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_DIMMER);
62 private final Object upnpLock = new Object();
63 private final Object jobLock = new Object();
65 private final Map<String, String> stateMap = Collections.synchronizedMap(new HashMap<>());
67 private Map<String, Boolean> subscriptionState = new HashMap<>();
69 private @Nullable ScheduledFuture<?> pollingJob;
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 public WemoDimmerHandler(Thing thing, UpnpIOService upnpIOService, WemoHttpCall wemoHttpCaller) {
80 super(thing, upnpIOService, wemoHttpCaller);
82 logger.debug("Creating a WemoDimmerHandler for thing '{}'", getThing().getUID());
86 public void initialize() {
87 Configuration configuration = getConfig();
89 if (configuration.get(UDN) != null) {
90 logger.debug("Initializing WemoDimmerHandler for UDN '{}'", configuration.get(UDN));
91 UpnpIOService localService = service;
92 if (localService != null) {
93 localService.registerParticipant(this);
96 pollingJob = scheduler.scheduleWithFixedDelay(this::poll, 0, DEFAULT_REFRESH_INTERVAL_SECONDS,
98 updateStatus(ThingStatus.ONLINE);
100 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
101 "@text/config-status.error.missing-udn");
102 logger.debug("Cannot initalize WemoDimmerHandler. UDN not set.");
107 public void dispose() {
108 logger.debug("WeMoDimmerHandler disposed.");
110 ScheduledFuture<?> job = this.pollingJob;
111 if (job != null && !job.isCancelled()) {
114 this.pollingJob = null;
115 removeSubscription();
118 private void poll() {
119 synchronized (jobLock) {
120 if (pollingJob == null) {
124 logger.debug("Polling job");
126 // Check if the Wemo device is set in the UPnP service registry
127 // If not, set the thing state to ONLINE/CONFIG-PENDING and wait for the next poll
128 if (!isUpnpDeviceRegistered()) {
129 logger.debug("UPnP device {} not yet registered", getUDN());
130 updateStatus(ThingStatus.ONLINE, ThingStatusDetail.CONFIGURATION_PENDING,
131 "@text/config-status.pending.device-not-registered [\"" + getUDN() + "\"]");
132 synchronized (upnpLock) {
133 subscriptionState = new HashMap<>();
137 updateStatus(ThingStatus.ONLINE);
140 } catch (Exception e) {
141 logger.debug("Exception during poll: {}", e.getMessage(), e);
147 public void handleCommand(ChannelUID channelUID, Command command) {
148 logger.trace("Command '{}' received for channel '{}'", command, channelUID);
149 if (command instanceof RefreshType) {
152 } catch (Exception e) {
153 logger.debug("Exception during poll", e);
156 String action = "SetBinaryState";
157 String argument = "BinaryState";
159 String timeStamp = null;
160 switch (channelUID.getId()) {
161 case CHANNEL_BRIGHTNESS:
162 String binaryState = this.stateMap.get("BinaryState");
163 if (command instanceof OnOffType) {
164 value = command.equals(OnOffType.OFF) ? "0" : "1";
165 setBinaryState(action, argument, value);
166 if (command.equals(OnOffType.OFF)) {
167 State brightnessState = new PercentType("0");
168 updateState(CHANNEL_BRIGHTNESS, brightnessState);
169 updateState(CHANNEL_TIMERSTART, OnOffType.OFF);
171 State brightnessState = new PercentType(currentBrightness);
172 updateState(CHANNEL_BRIGHTNESS, brightnessState);
174 } else if (command instanceof PercentType) {
175 int newBrightness = ((PercentType) command).intValue();
176 value = String.valueOf(newBrightness);
177 currentBrightness = newBrightness;
178 argument = "brightness";
179 if ("0".equals(value)) {
181 argument = "brightness";
182 setBinaryState(action, argument, "1");
184 argument = "BinaryState";
185 setBinaryState(action, argument, "0");
186 } else if ("0".equals(binaryState)) {
187 argument = "BinaryState";
188 setBinaryState(action, argument, "1");
190 argument = "brightness";
191 setBinaryState(action, argument, value);
192 } else if (command instanceof IncreaseDecreaseType) {
194 switch (command.toString()) {
196 newBrightness = currentBrightness + DIM_STEPSIZE;
197 if (newBrightness > 100) {
200 value = String.valueOf(newBrightness);
201 currentBrightness = newBrightness;
204 newBrightness = currentBrightness - DIM_STEPSIZE;
205 if (newBrightness < 0) {
208 value = String.valueOf(newBrightness);
209 currentBrightness = newBrightness;
212 argument = "brightness";
213 if ("0".equals(value)) {
215 argument = "brightness";
216 setBinaryState(action, argument, "1");
218 argument = "BinaryState";
219 setBinaryState(action, argument, "0");
220 } else if ("0".equals(binaryState)) {
221 argument = "BinaryState";
222 setBinaryState(action, argument, "1");
224 argument = "brightness";
225 setBinaryState(action, argument, value);
228 case CHANNEL_FADERCOUNTDOWNTIME:
230 if (command instanceof DecimalType) {
231 int commandValue = Integer.valueOf(String.valueOf(command));
232 commandValue = commandValue * 60;
233 String commandString = String.valueOf(commandValue);
234 value = "<BinaryState></BinaryState>" + "<Duration></Duration>" + "<EndAction></EndAction>"
235 + "<brightness></brightness>" + "<fader>" + commandString + ":-1:1:0:0</fader>"
237 setBinaryState(action, argument, value);
240 case CHANNEL_FADERENABLED:
242 if (command.equals(OnOffType.ON)) {
243 value = "<BinaryState></BinaryState>" + "<Duration></Duration>" + "<EndAction></EndAction>"
244 + "<brightness></brightness>" + "<fader>600:-1:1:0:0</fader>" + "<UDN></UDN>";
245 } else if (command.equals(OnOffType.OFF)) {
246 value = "<BinaryState></BinaryState>" + "<Duration></Duration>" + "<EndAction></EndAction>"
247 + "<brightness></brightness>" + "<fader>600:-1:0:0:0</fader>" + "<UDN></UDN>";
249 setBinaryState(action, argument, value);
251 case CHANNEL_TIMERSTART:
253 long ts = System.currentTimeMillis() / 1000;
254 timeStamp = String.valueOf(ts);
255 logger.info("timestamp '{}' created", timeStamp);
256 String faderSeconds = null;
257 String faderEnabled = null;
258 String fader = this.stateMap.get("fader");
260 String[] splitFader = fader.split(":");
261 if (splitFader[0] != null) {
262 faderSeconds = splitFader[0];
264 if (splitFader[0] != null) {
265 faderEnabled = splitFader[2];
268 if (faderSeconds != null && faderEnabled != null) {
269 if (OnOffType.ON.equals(command)) {
270 value = "<BinaryState></BinaryState>" + "<Duration></Duration>" + "<EndAction></EndAction>"
271 + "<brightness></brightness>" + "<fader>" + faderSeconds + ":" + timeStamp + ":"
272 + faderEnabled + ":0:0</fader>" + "<UDN></UDN>";
273 updateState(CHANNEL_STATE, OnOffType.ON);
274 } else if (OnOffType.OFF.equals(command)) {
275 value = "<BinaryState></BinaryState>" + "<Duration></Duration>" + "<EndAction></EndAction>"
276 + "<brightness></brightness>" + "<fader>" + faderSeconds + ":-1:" + faderEnabled
277 + ":0:0</fader>" + "<UDN></UDN>";
280 setBinaryState(action, argument, value);
282 case CHANNEL_NIGHTMODE:
283 action = "ConfigureNightMode";
284 argument = "NightModeConfiguration";
285 String nightModeBrightness = String.valueOf(currentNightModeBrightness);
286 if (OnOffType.ON.equals(command)) {
287 value = "<startTime>0</startTime> \\n<nightMode>1</nightMode> \\n<endTime>23400</endTime> \\n<nightModeBrightness>"
288 + nightModeBrightness + "</nightModeBrightness> \\n";
289 } else if (OnOffType.OFF.equals(command)) {
290 value = "<startTime>0</startTime> \\n<nightMode>0</nightMode> \\n<endTime>23400</endTime> \\n<nightModeBrightness>"
291 + nightModeBrightness + "</nightModeBrightness> \\n";
293 setBinaryState(action, argument, value);
295 case CHANNEL_NIGHTMODEBRIGHTNESS:
296 action = "ConfigureNightMode";
297 argument = "NightModeConfiguration";
298 if (command instanceof PercentType) {
299 int newBrightness = ((PercentType) command).intValue();
300 String newNightModeBrightness = String.valueOf(newBrightness);
301 value = "<startTime>0</startTime> \\n<nightMode>" + currentNightModeState
302 + "</nightMode> \\n<endTime>23400</endTime> \\n<nightModeBrightness>"
303 + newNightModeBrightness + "</nightModeBrightness> \\n";
304 currentNightModeBrightness = newBrightness;
305 } else if (command instanceof IncreaseDecreaseType) {
307 String newNightModeBrightness = null;
308 switch (command.toString()) {
310 newBrightness = currentNightModeBrightness + DIM_STEPSIZE;
311 if (newBrightness > 100) {
314 newNightModeBrightness = String.valueOf(newBrightness);
315 currentBrightness = newBrightness;
318 newBrightness = currentNightModeBrightness - DIM_STEPSIZE;
319 if (newBrightness < 0) {
322 newNightModeBrightness = String.valueOf(newBrightness);
323 currentNightModeBrightness = newBrightness;
326 value = "<startTime>0</startTime> \\n<nightMode>" + currentNightModeState
327 + "</nightMode> \\n<endTime>23400</endTime> \\n<nightModeBrightness>"
328 + newNightModeBrightness + "</nightModeBrightness> \\n";
330 setBinaryState(action, argument, value);
337 public void onServiceSubscribed(@Nullable String service, boolean succeeded) {
338 if (service != null) {
339 logger.debug("WeMo {}: Subscription to service {} {}", getUDN(), service,
340 succeeded ? "succeeded" : "failed");
341 subscriptionState.put(service, succeeded);
346 public void onValueReceived(@Nullable String variable, @Nullable String value, @Nullable String service) {
347 logger.debug("Received pair '{}':'{}' (service '{}') for thing '{}'",
348 new Object[] { variable, value, service, this.getThing().getUID() });
349 updateStatus(ThingStatus.ONLINE);
350 if (variable != null && value != null) {
351 String oldBinaryState = this.stateMap.get("BinaryState");
352 this.stateMap.put(variable, value);
355 if (oldBinaryState == null || !oldBinaryState.equals(value)) {
356 State state = "0".equals(value) ? OnOffType.OFF : OnOffType.ON;
357 logger.debug("State '{}' for device '{}' received", state, getThing().getUID());
358 updateState(CHANNEL_BRIGHTNESS, state);
359 if (state.equals(OnOffType.OFF)) {
360 updateState(CHANNEL_TIMERSTART, OnOffType.OFF);
365 logger.debug("brightness '{}' for device '{}' received", value, getThing().getUID());
366 int newBrightnessValue = Integer.valueOf(value);
367 State newBrightnessState = new PercentType(newBrightnessValue);
368 String binaryState = this.stateMap.get("BinaryState");
369 if (binaryState != null) {
370 if ("1".equals(binaryState)) {
371 updateState(CHANNEL_BRIGHTNESS, newBrightnessState);
374 currentBrightness = newBrightnessValue;
377 logger.debug("fader '{}' for device '{}' received", value, getThing().getUID());
378 String[] splitFader = value.split(":");
379 if (splitFader[0] != null) {
380 int faderSeconds = Integer.valueOf(splitFader[0]);
381 State faderMinutes = new DecimalType(faderSeconds / 60);
382 logger.debug("faderTime '{} minutes' for device '{}' received", faderMinutes,
383 getThing().getUID());
384 updateState(CHANNEL_FADERCOUNTDOWNTIME, faderMinutes);
386 if (splitFader[1] != null) {
387 State isTimerRunning = splitFader[1].equals("-1") ? OnOffType.OFF : OnOffType.ON;
388 logger.debug("isTimerRunning '{}' for device '{}' received", isTimerRunning,
389 getThing().getUID());
390 updateState(CHANNEL_TIMERSTART, isTimerRunning);
391 if (isTimerRunning.equals(OnOffType.ON)) {
392 updateState(CHANNEL_STATE, OnOffType.ON);
395 if (splitFader[2] != null) {
396 State isFaderEnabled = splitFader[1].equals("0") ? OnOffType.OFF : OnOffType.ON;
397 logger.debug("isFaderEnabled '{}' for device '{}' received", isFaderEnabled,
398 getThing().getUID());
399 updateState(CHANNEL_FADERENABLED, isFaderEnabled);
403 State nightModeState = "0".equals(value) ? OnOffType.OFF : OnOffType.ON;
404 currentNightModeState = value;
405 logger.debug("nightModeState '{}' for device '{}' received", nightModeState, getThing().getUID());
406 updateState(CHANNEL_NIGHTMODE, nightModeState);
409 State startTimeState = getDateTimeState(value);
410 logger.debug("startTimeState '{}' for device '{}' received", startTimeState, getThing().getUID());
411 if (startTimeState != null) {
412 updateState(CHANNEL_STARTTIME, startTimeState);
416 State endTimeState = getDateTimeState(value);
417 logger.debug("endTimeState '{}' for device '{}' received", endTimeState, getThing().getUID());
418 if (endTimeState != null) {
419 updateState(CHANNEL_ENDTIME, endTimeState);
422 case "nightModeBrightness":
423 int nightModeBrightnessValue = Integer.valueOf(value);
424 currentNightModeBrightness = nightModeBrightnessValue;
425 State nightModeBrightnessState = new PercentType(nightModeBrightnessValue);
426 logger.debug("nightModeBrightnessState '{}' for device '{}' received", nightModeBrightnessState,
427 getThing().getUID());
428 updateState(CHANNEL_NIGHTMODEBRIGHTNESS, nightModeBrightnessState);
434 private synchronized void addSubscription() {
435 UpnpIOService localService = service;
436 if (localService != null) {
437 if (localService.isRegistered(this)) {
438 logger.debug("Checking WeMo GENA subscription for '{}'", getThing().getUID());
439 String subscription = BASICEVENT;
440 if (subscriptionState.get(subscription) == null) {
441 logger.debug("Setting up GENA subscription {}: Subscribing to service {}...", getUDN(),
443 localService.addSubscription(this, subscription, SUBSCRIPTION_DURATION_SECONDS);
444 subscriptionState.put(subscription, true);
447 logger.debug("Setting up WeMo GENA subscription for '{}' FAILED - service.isRegistered(this) is FALSE",
448 getThing().getUID());
453 private synchronized void removeSubscription() {
454 logger.debug("Removing WeMo GENA subscription for '{}'", getThing().getUID());
455 UpnpIOService localService = service;
456 if (localService != null) {
457 if (localService.isRegistered(this)) {
458 String subscription = BASICEVENT;
459 if (subscriptionState.get(subscription) != null) {
460 logger.debug("WeMo {}: Unsubscribing from service {}...", getUDN(), subscription);
461 localService.removeSubscription(this, subscription);
463 subscriptionState = new HashMap<>();
464 localService.unregisterParticipant(this);
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 localHost = getHost();
476 if (localHost.isEmpty()) {
477 logger.error("Failed to get actual state for device '{}': IP address missing", getThing().getUID());
478 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
479 "@text/config-status.error.missing-ip");
482 String wemoURL = getWemoURL(localHost, BASICACTION);
483 if (wemoURL == null) {
484 logger.error("Failed to get actual state for device '{}': URL cannot be created", getThing().getUID());
485 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
486 "@text/config-status.error.missing-url");
489 String action = "GetBinaryState";
490 String variable = null;
491 String actionService = BASICACTION;
493 String soapHeader = "\"urn:Belkin:service:" + actionService + ":1#" + action + "\"";
494 String content = createStateRequestContent(action, actionService);
496 String wemoCallResponse = wemoHttpCaller.executeCall(wemoURL, soapHeader, content);
497 if (wemoCallResponse != null) {
498 if (logger.isTraceEnabled()) {
499 logger.trace("wemoCall to URL '{}' for device '{}'", wemoURL, getThing().getUID());
500 logger.trace("wemoCall with soapHeader '{}' for device '{}'", soapHeader, getThing().getUID());
501 logger.trace("wemoCall with content '{}' for device '{}'", content, getThing().getUID());
502 logger.trace("wemoCall with response '{}' for device '{}'", wemoCallResponse, getThing().getUID());
504 value = substringBetween(wemoCallResponse, "<BinaryState>", "</BinaryState>");
505 variable = "BinaryState";
506 this.onValueReceived(variable, value, actionService + "1");
507 value = substringBetween(wemoCallResponse, "<brightness>", "</brightness>");
508 variable = "brightness";
509 this.onValueReceived(variable, value, actionService + "1");
510 value = substringBetween(wemoCallResponse, "<fader>", "</fader>");
512 this.onValueReceived(variable, value, actionService + "1");
513 updateStatus(ThingStatus.ONLINE);
515 } catch (Exception e) {
516 logger.debug("Failed to get actual state for device '{}': {}", getThing().getUID(), e.getMessage());
517 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
519 action = "GetNightModeConfiguration";
522 soapHeader = "\"urn:Belkin:service:" + actionService + ":1#" + action + "\"";
523 content = createStateRequestContent(action, actionService);
525 String wemoCallResponse = wemoHttpCaller.executeCall(wemoURL, soapHeader, content);
526 if (wemoCallResponse != null) {
527 if (logger.isTraceEnabled()) {
528 logger.trace("wemoCall to URL '{}' for device '{}'", wemoURL, getThing().getUID());
529 logger.trace("wemoCall with soapHeader '{}' for device '{}'", soapHeader, getThing().getUID());
530 logger.trace("wemoCall with content '{}' for device '{}'", content, getThing().getUID());
531 logger.trace("wemoCall with response '{}' for device '{}'", wemoCallResponse, getThing().getUID());
533 value = substringBetween(wemoCallResponse, "<startTime>", "</startTime>");
534 variable = "startTime";
535 this.onValueReceived(variable, value, actionService + "1");
536 value = substringBetween(wemoCallResponse, "<endTime>", "</endTime>");
537 variable = "endTime";
538 this.onValueReceived(variable, value, actionService + "1");
539 value = substringBetween(wemoCallResponse, "<nightMode>", "</nightMode>");
540 variable = "nightMode";
541 this.onValueReceived(variable, value, actionService + "1");
542 value = substringBetween(wemoCallResponse, "<nightModeBrightness>", "</nightModeBrightness>");
543 variable = "nightModeBrightness";
544 this.onValueReceived(variable, value, actionService + "1");
545 updateStatus(ThingStatus.ONLINE);
548 } catch (Exception e) {
549 logger.debug("Failed to get actual NightMode state for device '{}': {}", getThing().getUID(),
551 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
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) {
571 String localHost = getHost();
572 if (localHost.isEmpty()) {
573 logger.error("Failed to set binary state for device '{}': IP address missing", getThing().getUID());
574 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
575 "@text/config-status.error.missing-ip");
578 String wemoURL = getWemoURL(localHost, BASICACTION);
579 if (wemoURL == null) {
580 logger.error("Failed to set binary state for device '{}': URL cannot be created", getThing().getUID());
581 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
582 "@text/config-status.error.missing-url");
586 String soapHeader = "\"urn:Belkin:service:basicevent:1#SetBinaryState\"";
587 String content = "<?xml version=\"1.0\"?>"
588 + "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
589 + "<s:Body>" + "<u:" + action + " xmlns:u=\"urn:Belkin:service:basicevent:1\">" + "<" + argument
590 + ">" + value + "</" + argument + ">" + "</u:" + action + ">" + "</s:Body>" + "</s:Envelope>";
592 String wemoCallResponse = wemoHttpCaller.executeCall(wemoURL, soapHeader, content);
593 if (wemoCallResponse != null && logger.isTraceEnabled()) {
594 logger.trace("wemoCall to URL '{}' for device '{}'", wemoURL, getThing().getUID());
595 logger.trace("wemoCall with soapHeader '{}' for device '{}'", soapHeader, getThing().getUID());
596 logger.trace("wemoCall with content '{}' for device '{}'", content, getThing().getUID());
597 logger.trace("wemoCall with response '{}' for device '{}'", wemoCallResponse, getThing().getUID());
599 } catch (Exception e) {
600 logger.debug("Failed to set binaryState '{}' for device '{}': {}", value, getThing().getUID(),
602 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
606 public void setTimerStart(String action, String argument, String value) {
607 String localHost = getHost();
608 if (localHost.isEmpty()) {
609 logger.error("Failed to set timerStart for device '{}': IP address missing", getThing().getUID());
610 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
611 "@text/config-status.error.missing-ip");
614 String wemoURL = getWemoURL(localHost, BASICACTION);
615 if (wemoURL == null) {
616 logger.error("Failed to set timerStart for device '{}': URL cannot be created", getThing().getUID());
617 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
618 "@text/config-status.error.missing-url");
622 String soapHeader = "\"urn:Belkin:service:basicevent:1#SetBinaryState\"";
623 String content = "<?xml version=\"1.0\"?>"
624 + "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
625 + "<s:Body>" + "<u:SetBinaryState xmlns:u=\"urn:Belkin:service:basicevent:1\">" + value
626 + "</u:SetBinaryState>" + "</s:Body>" + "</s:Envelope>";
627 String wemoCallResponse = wemoHttpCaller.executeCall(wemoURL, soapHeader, content);
628 if (wemoCallResponse != null && logger.isTraceEnabled()) {
629 logger.trace("wemoCall to URL '{}' for device '{}'", wemoURL, getThing().getUID());
630 logger.trace("wemoCall with soapHeader '{}' for device '{}'", soapHeader, getThing().getUID());
631 logger.trace("wemoCall with content '{}' for device '{}'", content, getThing().getUID());
632 logger.trace("wemoCall with response '{}' for device '{}'", wemoCallResponse, getThing().getUID());
634 } catch (Exception e) {
635 logger.debug("Failed to set timerStart '{}' for device '{}': {}", value, getThing().getUID(),
637 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());