]> git.basschouten.com Git - openhab-addons.git/blob
d32c4f3b850d81c8ac9a810ee785d6335c3abe14
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.wemo.internal.handler;
14
15 import static org.openhab.binding.wemo.internal.WemoBindingConstants.*;
16 import static org.openhab.binding.wemo.internal.WemoUtil.*;
17
18 import java.time.Instant;
19 import java.time.ZonedDateTime;
20 import java.util.Collections;
21 import java.util.HashMap;
22 import java.util.Map;
23 import java.util.Set;
24 import java.util.TimeZone;
25 import java.util.concurrent.ScheduledFuture;
26 import java.util.concurrent.TimeUnit;
27
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;
48
49 /**
50  * The {@link WemoDimmerHandler} is responsible for handling commands, which are
51  * sent to one of the channels and to update their states.
52  *
53  * @author Hans-Jörg Merk - Initial contribution
54  */
55 @NonNullByDefault
56 public class WemoDimmerHandler extends WemoBaseThingHandler {
57
58     private final Logger logger = LoggerFactory.getLogger(WemoDimmerHandler.class);
59
60     public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Set.of(THING_TYPE_DIMMER);
61
62     private final Object jobLock = new Object();
63
64     private final Map<String, String> stateMap = Collections.synchronizedMap(new HashMap<>());
65
66     private @Nullable ScheduledFuture<?> pollingJob;
67
68     private int currentBrightness;
69     private int currentNightModeBrightness;
70     private @Nullable String currentNightModeState;
71     /**
72      * Set dimming stepsize to 5%
73      */
74     private static final int DIM_STEPSIZE = 5;
75
76     public WemoDimmerHandler(Thing thing, UpnpIOService upnpIOService, WemoHttpCall wemoHttpCaller) {
77         super(thing, upnpIOService, wemoHttpCaller);
78
79         logger.debug("Creating a WemoDimmerHandler for thing '{}'", getThing().getUID());
80     }
81
82     @Override
83     public void initialize() {
84         super.initialize();
85         Configuration configuration = getConfig();
86
87         if (configuration.get(UDN) != null) {
88             logger.debug("Initializing WemoDimmerHandler for UDN '{}'", configuration.get(UDN));
89             addSubscription(BASICEVENT);
90             pollingJob = scheduler.scheduleWithFixedDelay(this::poll, 0, DEFAULT_REFRESH_INTERVAL_SECONDS,
91                     TimeUnit.SECONDS);
92             updateStatus(ThingStatus.UNKNOWN);
93         } else {
94             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
95                     "@text/config-status.error.missing-udn");
96         }
97     }
98
99     @Override
100     public void dispose() {
101         logger.debug("WeMoDimmerHandler disposed.");
102
103         ScheduledFuture<?> job = this.pollingJob;
104         if (job != null && !job.isCancelled()) {
105             job.cancel(true);
106         }
107         this.pollingJob = null;
108         super.dispose();
109     }
110
111     private void poll() {
112         synchronized (jobLock) {
113             if (pollingJob == null) {
114                 return;
115             }
116             try {
117                 logger.debug("Polling job");
118                 // Check if the Wemo device is set in the UPnP service registry
119                 if (!isUpnpDeviceRegistered()) {
120                     logger.debug("UPnP device {} not yet registered", getUDN());
121                     updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE,
122                             "@text/config-status.pending.device-not-registered [\"" + getUDN() + "\"]");
123                     return;
124                 }
125                 updateWemoState();
126             } catch (Exception e) {
127                 logger.debug("Exception during poll: {}", e.getMessage(), e);
128             }
129         }
130     }
131
132     @Override
133     public void handleCommand(ChannelUID channelUID, Command command) {
134         logger.trace("Command '{}' received for channel '{}'", command, channelUID);
135         if (command instanceof RefreshType) {
136             try {
137                 updateWemoState();
138             } catch (Exception e) {
139                 logger.debug("Exception during poll", e);
140             }
141         } else {
142             String action = "SetBinaryState";
143             String argument = "BinaryState";
144             String value = "0";
145             String timeStamp = null;
146             switch (channelUID.getId()) {
147                 case CHANNEL_BRIGHTNESS:
148                     String binaryState = this.stateMap.get("BinaryState");
149                     if (command instanceof OnOffType) {
150                         value = command.equals(OnOffType.OFF) ? "0" : "1";
151                         setBinaryState(action, argument, value);
152                         if (command.equals(OnOffType.OFF)) {
153                             State brightnessState = new PercentType("0");
154                             updateState(CHANNEL_BRIGHTNESS, brightnessState);
155                             updateState(CHANNEL_TIMER_START, OnOffType.OFF);
156                         } else {
157                             State brightnessState = new PercentType(currentBrightness);
158                             updateState(CHANNEL_BRIGHTNESS, brightnessState);
159                         }
160                     } else if (command instanceof PercentType percentCommand) {
161                         int newBrightness = percentCommand.intValue();
162                         value = String.valueOf(newBrightness);
163                         currentBrightness = newBrightness;
164                         argument = "brightness";
165                         if ("0".equals(value)) {
166                             value = "1";
167                             argument = "brightness";
168                             setBinaryState(action, argument, "1");
169                             value = "0";
170                             argument = "BinaryState";
171                             setBinaryState(action, argument, "0");
172                         } else if ("0".equals(binaryState)) {
173                             argument = "BinaryState";
174                             setBinaryState(action, argument, "1");
175                         }
176                         argument = "brightness";
177                         setBinaryState(action, argument, value);
178                     } else if (command instanceof IncreaseDecreaseType) {
179                         int newBrightness;
180                         switch (command.toString()) {
181                             case "INCREASE":
182                                 newBrightness = currentBrightness + DIM_STEPSIZE;
183                                 if (newBrightness > 100) {
184                                     newBrightness = 100;
185                                 }
186                                 value = String.valueOf(newBrightness);
187                                 currentBrightness = newBrightness;
188                                 break;
189                             case "DECREASE":
190                                 newBrightness = currentBrightness - DIM_STEPSIZE;
191                                 if (newBrightness < 0) {
192                                     newBrightness = 0;
193                                 }
194                                 value = String.valueOf(newBrightness);
195                                 currentBrightness = newBrightness;
196                                 break;
197                         }
198                         argument = "brightness";
199                         if ("0".equals(value)) {
200                             value = "1";
201                             argument = "brightness";
202                             setBinaryState(action, argument, "1");
203                             value = "0";
204                             argument = "BinaryState";
205                             setBinaryState(action, argument, "0");
206                         } else if ("0".equals(binaryState)) {
207                             argument = "BinaryState";
208                             setBinaryState(action, argument, "1");
209                         }
210                         argument = "brightness";
211                         setBinaryState(action, argument, value);
212                     }
213                     break;
214                 case CHANNEL_FADER_COUNT_DOWN_TIME:
215                     argument = "Fader";
216                     if (command instanceof DecimalType) {
217                         int commandValue = Integer.valueOf(String.valueOf(command));
218                         commandValue = commandValue * 60;
219                         String commandString = String.valueOf(commandValue);
220                         value = """
221                                 <BinaryState></BinaryState>\
222                                 <Duration></Duration>\
223                                 <EndAction></EndAction>\
224                                 <brightness></brightness>\
225                                 <fader>\
226                                 """ + commandString + ":-1:1:0:0</fader><UDN></UDN>";
227                         setBinaryState(action, argument, value);
228                     }
229                     break;
230                 case CHANNEL_FADER_ENABLED:
231                     argument = "Fader";
232                     if (command.equals(OnOffType.ON)) {
233                         value = """
234                                 <BinaryState></BinaryState>\
235                                 <Duration></Duration>\
236                                 <EndAction></EndAction>\
237                                 <brightness></brightness>\
238                                 <fader>600:-1:1:0:0</fader>\
239                                 <UDN></UDN>\
240                                 """;
241                     } else if (command.equals(OnOffType.OFF)) {
242                         value = """
243                                 <BinaryState></BinaryState>\
244                                 <Duration></Duration>\
245                                 <EndAction></EndAction>\
246                                 <brightness></brightness>\
247                                 <fader>600:-1:0:0:0</fader>\
248                                 <UDN></UDN>\
249                                 """;
250                     }
251                     setBinaryState(action, argument, value);
252                     break;
253                 case CHANNEL_TIMER_START:
254                     argument = "Fader";
255                     long ts = System.currentTimeMillis() / 1000;
256                     timeStamp = String.valueOf(ts);
257                     logger.info("timestamp '{}' created", timeStamp);
258                     String faderSeconds = null;
259                     String faderEnabled = null;
260                     String fader = this.stateMap.get("fader");
261                     if (fader != null) {
262                         String[] splitFader = fader.split(":");
263                         if (splitFader[0] != null) {
264                             faderSeconds = splitFader[0];
265                         }
266                         if (splitFader[0] != null) {
267                             faderEnabled = splitFader[2];
268                         }
269                     }
270                     if (faderSeconds != null && faderEnabled != null) {
271                         if (OnOffType.ON.equals(command)) {
272                             value = """
273                                     <BinaryState></BinaryState>\
274                                     <Duration></Duration>\
275                                     <EndAction></EndAction>\
276                                     <brightness></brightness>\
277                                     <fader>\
278                                     """ + faderSeconds + ":" + timeStamp + ":" + faderEnabled + ":0:0</fader>"
279                                     + "<UDN></UDN>";
280                             updateState(CHANNEL_STATE, OnOffType.ON);
281                         } else if (OnOffType.OFF.equals(command)) {
282                             value = """
283                                     <BinaryState></BinaryState>\
284                                     <Duration></Duration>\
285                                     <EndAction></EndAction>\
286                                     <brightness></brightness>\
287                                     <fader>\
288                                     """ + faderSeconds + ":-1:" + faderEnabled + ":0:0</fader>" + "<UDN></UDN>";
289                         }
290                     }
291                     setBinaryState(action, argument, value);
292                     break;
293                 case CHANNEL_NIGHT_MODE:
294                     action = "ConfigureNightMode";
295                     argument = "NightModeConfiguration";
296                     String nightModeBrightness = String.valueOf(currentNightModeBrightness);
297                     if (OnOffType.ON.equals(command)) {
298                         value = "&lt;startTime&gt;0&lt;/startTime&gt; \\n&lt;nightMode&gt;1&lt;/nightMode&gt; \\n&lt;endTime&gt;23400&lt;/endTime&gt; \\n&lt;nightModeBrightness&gt;"
299                                 + nightModeBrightness + "&lt;/nightModeBrightness&gt; \\n";
300                     } else if (OnOffType.OFF.equals(command)) {
301                         value = "&lt;startTime&gt;0&lt;/startTime&gt; \\n&lt;nightMode&gt;0&lt;/nightMode&gt; \\n&lt;endTime&gt;23400&lt;/endTime&gt; \\n&lt;nightModeBrightness&gt;"
302                                 + nightModeBrightness + "&lt;/nightModeBrightness&gt; \\n";
303                     }
304                     setBinaryState(action, argument, value);
305                     break;
306                 case CHANNEL_NIGHT_MODE_BRIGHTNESS:
307                     action = "ConfigureNightMode";
308                     argument = "NightModeConfiguration";
309                     if (command instanceof PercentType percentCommand) {
310                         int newBrightness = percentCommand.intValue();
311                         String newNightModeBrightness = String.valueOf(newBrightness);
312                         value = "&lt;startTime&gt;0&lt;/startTime&gt; \\n&lt;nightMode&gt;" + currentNightModeState
313                                 + "&lt;/nightMode&gt; \\n&lt;endTime&gt;23400&lt;/endTime&gt; \\n&lt;nightModeBrightness&gt;"
314                                 + newNightModeBrightness + "&lt;/nightModeBrightness&gt; \\n";
315                         currentNightModeBrightness = newBrightness;
316                     } else if (command instanceof IncreaseDecreaseType) {
317                         int newBrightness;
318                         String newNightModeBrightness = null;
319                         switch (command.toString()) {
320                             case "INCREASE":
321                                 newBrightness = currentNightModeBrightness + DIM_STEPSIZE;
322                                 if (newBrightness > 100) {
323                                     newBrightness = 100;
324                                 }
325                                 newNightModeBrightness = String.valueOf(newBrightness);
326                                 currentBrightness = newBrightness;
327                                 break;
328                             case "DECREASE":
329                                 newBrightness = currentNightModeBrightness - DIM_STEPSIZE;
330                                 if (newBrightness < 0) {
331                                     newBrightness = 0;
332                                 }
333                                 newNightModeBrightness = String.valueOf(newBrightness);
334                                 currentNightModeBrightness = newBrightness;
335                                 break;
336                         }
337                         value = "&lt;startTime&gt;0&lt;/startTime&gt; \\n&lt;nightMode&gt;" + currentNightModeState
338                                 + "&lt;/nightMode&gt; \\n&lt;endTime&gt;23400&lt;/endTime&gt; \\n&lt;nightModeBrightness&gt;"
339                                 + newNightModeBrightness + "&lt;/nightModeBrightness&gt; \\n";
340                     }
341                     setBinaryState(action, argument, value);
342                     break;
343             }
344         }
345     }
346
347     @Override
348     public void onValueReceived(@Nullable String variable, @Nullable String value, @Nullable String service) {
349         logger.debug("Received pair '{}':'{}' (service '{}') for thing '{}'",
350                 new Object[] { variable, value, service, this.getThing().getUID() });
351         updateStatus(ThingStatus.ONLINE);
352         if (variable != null && value != null) {
353             String oldBinaryState = this.stateMap.get("BinaryState");
354             this.stateMap.put(variable, value);
355             switch (variable) {
356                 case "BinaryState":
357                     if (oldBinaryState == null || !oldBinaryState.equals(value)) {
358                         State state = OnOffType.from(!"0".equals(value));
359                         logger.debug("State '{}' for device '{}' received", state, getThing().getUID());
360                         updateState(CHANNEL_BRIGHTNESS, state);
361                         if (state.equals(OnOffType.OFF)) {
362                             updateState(CHANNEL_TIMER_START, OnOffType.OFF);
363                         }
364                     }
365                     break;
366                 case "brightness":
367                     logger.debug("brightness '{}' for device '{}' received", value, getThing().getUID());
368                     int newBrightnessValue = Integer.valueOf(value);
369                     State newBrightnessState = new PercentType(newBrightnessValue);
370                     String binaryState = this.stateMap.get("BinaryState");
371                     if (binaryState != null) {
372                         if ("1".equals(binaryState)) {
373                             updateState(CHANNEL_BRIGHTNESS, newBrightnessState);
374                         }
375                     }
376                     currentBrightness = newBrightnessValue;
377                     break;
378                 case "fader":
379                     logger.debug("fader '{}' for device '{}' received", value, getThing().getUID());
380                     String[] splitFader = value.split(":");
381                     if (splitFader[0] != null) {
382                         int faderSeconds = Integer.valueOf(splitFader[0]);
383                         State faderMinutes = new DecimalType(faderSeconds / 60);
384                         logger.debug("faderTime '{} minutes' for device '{}' received", faderMinutes,
385                                 getThing().getUID());
386                         updateState(CHANNEL_FADER_COUNT_DOWN_TIME, faderMinutes);
387                     }
388                     if (splitFader[1] != null) {
389                         State isTimerRunning = OnOffType.from(!"-1".equals(splitFader[1]));
390                         logger.debug("isTimerRunning '{}' for device '{}' received", isTimerRunning,
391                                 getThing().getUID());
392                         updateState(CHANNEL_TIMER_START, isTimerRunning);
393                         if (isTimerRunning.equals(OnOffType.ON)) {
394                             updateState(CHANNEL_STATE, OnOffType.ON);
395                         }
396                     }
397                     if (splitFader[2] != null) {
398                         State isFaderEnabled = OnOffType.from(!"0".equals(splitFader[1]));
399                         logger.debug("isFaderEnabled '{}' for device '{}' received", isFaderEnabled,
400                                 getThing().getUID());
401                         updateState(CHANNEL_FADER_ENABLED, isFaderEnabled);
402                     }
403                     break;
404                 case "nightMode":
405                     State nightModeState = OnOffType.from(!"0".equals(value));
406                     currentNightModeState = value;
407                     logger.debug("nightModeState '{}' for device '{}' received", nightModeState, getThing().getUID());
408                     updateState(CHANNEL_NIGHT_MODE, nightModeState);
409                     break;
410                 case "startTime":
411                     State startTimeState = getDateTimeState(value);
412                     logger.debug("startTimeState '{}' for device '{}' received", startTimeState, getThing().getUID());
413                     if (startTimeState != null) {
414                         updateState(CHANNEL_START_TIME, startTimeState);
415                     }
416                     break;
417                 case "endTime":
418                     State endTimeState = getDateTimeState(value);
419                     logger.debug("endTimeState '{}' for device '{}' received", endTimeState, getThing().getUID());
420                     if (endTimeState != null) {
421                         updateState(CHANNEL_END_TIME, endTimeState);
422                     }
423                     break;
424                 case "nightModeBrightness":
425                     int nightModeBrightnessValue = Integer.valueOf(value);
426                     currentNightModeBrightness = nightModeBrightnessValue;
427                     State nightModeBrightnessState = new PercentType(nightModeBrightnessValue);
428                     logger.debug("nightModeBrightnessState '{}' for device '{}' received", nightModeBrightnessState,
429                             getThing().getUID());
430                     updateState(CHANNEL_NIGHT_MODE_BRIGHTNESS, nightModeBrightnessState);
431                     break;
432             }
433         }
434     }
435
436     /**
437      * The {@link updateWemoState} polls the actual state of a WeMo device and
438      * calls {@link onValueReceived} to update the statemap and channels..
439      *
440      */
441     protected void updateWemoState() {
442         String wemoURL = getWemoURL(BASICACTION);
443         if (wemoURL == null) {
444             logger.debug("Failed to get actual state for device '{}': URL cannot be created", getThing().getUID());
445             return;
446         }
447         String action = "GetBinaryState";
448         String variable = null;
449         String actionService = BASICACTION;
450         String value = null;
451         String soapHeader = "\"urn:Belkin:service:" + actionService + ":1#" + action + "\"";
452         String content = createStateRequestContent(action, actionService);
453         try {
454             String wemoCallResponse = wemoHttpCaller.executeCall(wemoURL, soapHeader, content);
455             value = substringBetween(wemoCallResponse, "<BinaryState>", "</BinaryState>");
456             variable = "BinaryState";
457             this.onValueReceived(variable, value, actionService + "1");
458             value = substringBetween(wemoCallResponse, "<brightness>", "</brightness>");
459             variable = "brightness";
460             this.onValueReceived(variable, value, actionService + "1");
461             value = substringBetween(wemoCallResponse, "<fader>", "</fader>");
462             variable = "fader";
463             this.onValueReceived(variable, value, actionService + "1");
464             updateStatus(ThingStatus.ONLINE);
465         } catch (Exception e) {
466             logger.debug("Failed to get actual state for device '{}': {}", getThing().getUID(), e.getMessage());
467             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
468         }
469         action = "GetNightModeConfiguration";
470         variable = null;
471         value = null;
472         soapHeader = "\"urn:Belkin:service:" + actionService + ":1#" + action + "\"";
473         content = createStateRequestContent(action, actionService);
474         try {
475             String wemoCallResponse = wemoHttpCaller.executeCall(wemoURL, soapHeader, content);
476             value = substringBetween(wemoCallResponse, "<startTime>", "</startTime>");
477             variable = "startTime";
478             this.onValueReceived(variable, value, actionService + "1");
479             value = substringBetween(wemoCallResponse, "<endTime>", "</endTime>");
480             variable = "endTime";
481             this.onValueReceived(variable, value, actionService + "1");
482             value = substringBetween(wemoCallResponse, "<nightMode>", "</nightMode>");
483             variable = "nightMode";
484             this.onValueReceived(variable, value, actionService + "1");
485             value = substringBetween(wemoCallResponse, "<nightModeBrightness>", "</nightModeBrightness>");
486             variable = "nightModeBrightness";
487             this.onValueReceived(variable, value, actionService + "1");
488             updateStatus(ThingStatus.ONLINE);
489         } catch (Exception e) {
490             logger.debug("Failed to get actual NightMode state for device '{}': {}", getThing().getUID(),
491                     e.getMessage());
492             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
493         }
494     }
495
496     public @Nullable State getDateTimeState(String attributeValue) {
497         long value = 0;
498         try {
499             value = Long.parseLong(attributeValue);
500         } catch (NumberFormatException e) {
501             logger.warn("Unable to parse attributeValue '{}' for device '{}'; expected long", attributeValue,
502                     getThing().getUID());
503             return null;
504         }
505         ZonedDateTime zoned = ZonedDateTime.ofInstant(Instant.ofEpochSecond(value), TimeZone.getDefault().toZoneId());
506         return new DateTimeType(zoned);
507     }
508
509     public void setBinaryState(String action, String argument, String value) {
510         String wemoURL = getWemoURL(BASICACTION);
511         if (wemoURL == null) {
512             logger.debug("Failed to set binary state for device '{}': URL cannot be created", getThing().getUID());
513             return;
514         }
515         try {
516             String soapHeader = "\"urn:Belkin:service:basicevent:1#SetBinaryState\"";
517             String content = """
518                     <?xml version="1.0"?>\
519                     <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">\
520                     <s:Body>\
521                     <u:\
522                     """
523                     + action + " xmlns:u=\"urn:Belkin:service:basicevent:1\">" + "<" + argument + ">" + value + "</"
524                     + argument + ">" + "</u:" + action + ">" + "</s:Body>" + "</s:Envelope>";
525
526             wemoHttpCaller.executeCall(wemoURL, soapHeader, content);
527             updateStatus(ThingStatus.ONLINE);
528         } catch (Exception e) {
529             logger.debug("Failed to set binaryState '{}' for device '{}': {}", value, getThing().getUID(),
530                     e.getMessage());
531             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
532         }
533     }
534
535     public void setTimerStart(String action, String argument, String value) {
536         String wemoURL = getWemoURL(BASICACTION);
537         if (wemoURL == null) {
538             logger.warn("Failed to set timerStart for device '{}': URL cannot be created", getThing().getUID());
539             return;
540         }
541         try {
542             String soapHeader = "\"urn:Belkin:service:basicevent:1#SetBinaryState\"";
543             String content = """
544                     <?xml version="1.0"?>\
545                     <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">\
546                     <s:Body>\
547                     <u:SetBinaryState xmlns:u="urn:Belkin:service:basicevent:1">\
548                     """
549                     + value + "</u:SetBinaryState>" + "</s:Body>" + "</s:Envelope>";
550             wemoHttpCaller.executeCall(wemoURL, soapHeader, content);
551             updateStatus(ThingStatus.ONLINE);
552         } catch (Exception e) {
553             logger.debug("Failed to set timerStart '{}' for device '{}': {}", value, getThing().getUID(),
554                     e.getMessage());
555             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
556         }
557     }
558 }