]> git.basschouten.com Git - openhab-addons.git/blob
03be05685a88cea4ec6ffdc3299e03a58a93871a
[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 = Collections.singleton(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) {
161                         int newBrightness = ((PercentType) command).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 = "<BinaryState></BinaryState>" + "<Duration></Duration>" + "<EndAction></EndAction>"
221                                 + "<brightness></brightness>" + "<fader>" + commandString + ":-1:1:0:0</fader>"
222                                 + "<UDN></UDN>";
223                         setBinaryState(action, argument, value);
224                     }
225                     break;
226                 case CHANNEL_FADER_ENABLED:
227                     argument = "Fader";
228                     if (command.equals(OnOffType.ON)) {
229                         value = "<BinaryState></BinaryState>" + "<Duration></Duration>" + "<EndAction></EndAction>"
230                                 + "<brightness></brightness>" + "<fader>600:-1:1:0:0</fader>" + "<UDN></UDN>";
231                     } else if (command.equals(OnOffType.OFF)) {
232                         value = "<BinaryState></BinaryState>" + "<Duration></Duration>" + "<EndAction></EndAction>"
233                                 + "<brightness></brightness>" + "<fader>600:-1:0:0:0</fader>" + "<UDN></UDN>";
234                     }
235                     setBinaryState(action, argument, value);
236                     break;
237                 case CHANNEL_TIMER_START:
238                     argument = "Fader";
239                     long ts = System.currentTimeMillis() / 1000;
240                     timeStamp = String.valueOf(ts);
241                     logger.info("timestamp '{}' created", timeStamp);
242                     String faderSeconds = null;
243                     String faderEnabled = null;
244                     String fader = this.stateMap.get("fader");
245                     if (fader != null) {
246                         String[] splitFader = fader.split(":");
247                         if (splitFader[0] != null) {
248                             faderSeconds = splitFader[0];
249                         }
250                         if (splitFader[0] != null) {
251                             faderEnabled = splitFader[2];
252                         }
253                     }
254                     if (faderSeconds != null && faderEnabled != null) {
255                         if (OnOffType.ON.equals(command)) {
256                             value = "<BinaryState></BinaryState>" + "<Duration></Duration>" + "<EndAction></EndAction>"
257                                     + "<brightness></brightness>" + "<fader>" + faderSeconds + ":" + timeStamp + ":"
258                                     + faderEnabled + ":0:0</fader>" + "<UDN></UDN>";
259                             updateState(CHANNEL_STATE, OnOffType.ON);
260                         } else if (OnOffType.OFF.equals(command)) {
261                             value = "<BinaryState></BinaryState>" + "<Duration></Duration>" + "<EndAction></EndAction>"
262                                     + "<brightness></brightness>" + "<fader>" + faderSeconds + ":-1:" + faderEnabled
263                                     + ":0:0</fader>" + "<UDN></UDN>";
264                         }
265                     }
266                     setBinaryState(action, argument, value);
267                     break;
268                 case CHANNEL_NIGHT_MODE:
269                     action = "ConfigureNightMode";
270                     argument = "NightModeConfiguration";
271                     String nightModeBrightness = String.valueOf(currentNightModeBrightness);
272                     if (OnOffType.ON.equals(command)) {
273                         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;"
274                                 + nightModeBrightness + "&lt;/nightModeBrightness&gt; \\n";
275                     } else if (OnOffType.OFF.equals(command)) {
276                         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;"
277                                 + nightModeBrightness + "&lt;/nightModeBrightness&gt; \\n";
278                     }
279                     setBinaryState(action, argument, value);
280                     break;
281                 case CHANNEL_NIGHT_MODE_BRIGHTNESS:
282                     action = "ConfigureNightMode";
283                     argument = "NightModeConfiguration";
284                     if (command instanceof PercentType) {
285                         int newBrightness = ((PercentType) command).intValue();
286                         String newNightModeBrightness = String.valueOf(newBrightness);
287                         value = "&lt;startTime&gt;0&lt;/startTime&gt; \\n&lt;nightMode&gt;" + currentNightModeState
288                                 + "&lt;/nightMode&gt; \\n&lt;endTime&gt;23400&lt;/endTime&gt; \\n&lt;nightModeBrightness&gt;"
289                                 + newNightModeBrightness + "&lt;/nightModeBrightness&gt; \\n";
290                         currentNightModeBrightness = newBrightness;
291                     } else if (command instanceof IncreaseDecreaseType) {
292                         int newBrightness;
293                         String newNightModeBrightness = null;
294                         switch (command.toString()) {
295                             case "INCREASE":
296                                 newBrightness = currentNightModeBrightness + DIM_STEPSIZE;
297                                 if (newBrightness > 100) {
298                                     newBrightness = 100;
299                                 }
300                                 newNightModeBrightness = String.valueOf(newBrightness);
301                                 currentBrightness = newBrightness;
302                                 break;
303                             case "DECREASE":
304                                 newBrightness = currentNightModeBrightness - DIM_STEPSIZE;
305                                 if (newBrightness < 0) {
306                                     newBrightness = 0;
307                                 }
308                                 newNightModeBrightness = String.valueOf(newBrightness);
309                                 currentNightModeBrightness = newBrightness;
310                                 break;
311                         }
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                     }
316                     setBinaryState(action, argument, value);
317                     break;
318             }
319         }
320     }
321
322     @Override
323     public void onValueReceived(@Nullable String variable, @Nullable String value, @Nullable String service) {
324         logger.debug("Received pair '{}':'{}' (service '{}') for thing '{}'",
325                 new Object[] { variable, value, service, this.getThing().getUID() });
326         updateStatus(ThingStatus.ONLINE);
327         if (variable != null && value != null) {
328             String oldBinaryState = this.stateMap.get("BinaryState");
329             this.stateMap.put(variable, value);
330             switch (variable) {
331                 case "BinaryState":
332                     if (oldBinaryState == null || !oldBinaryState.equals(value)) {
333                         State state = "0".equals(value) ? OnOffType.OFF : OnOffType.ON;
334                         logger.debug("State '{}' for device '{}' received", state, getThing().getUID());
335                         updateState(CHANNEL_BRIGHTNESS, state);
336                         if (state.equals(OnOffType.OFF)) {
337                             updateState(CHANNEL_TIMER_START, OnOffType.OFF);
338                         }
339                     }
340                     break;
341                 case "brightness":
342                     logger.debug("brightness '{}' for device '{}' received", value, getThing().getUID());
343                     int newBrightnessValue = Integer.valueOf(value);
344                     State newBrightnessState = new PercentType(newBrightnessValue);
345                     String binaryState = this.stateMap.get("BinaryState");
346                     if (binaryState != null) {
347                         if ("1".equals(binaryState)) {
348                             updateState(CHANNEL_BRIGHTNESS, newBrightnessState);
349                         }
350                     }
351                     currentBrightness = newBrightnessValue;
352                     break;
353                 case "fader":
354                     logger.debug("fader '{}' for device '{}' received", value, getThing().getUID());
355                     String[] splitFader = value.split(":");
356                     if (splitFader[0] != null) {
357                         int faderSeconds = Integer.valueOf(splitFader[0]);
358                         State faderMinutes = new DecimalType(faderSeconds / 60);
359                         logger.debug("faderTime '{} minutes' for device '{}' received", faderMinutes,
360                                 getThing().getUID());
361                         updateState(CHANNEL_FADER_COUNT_DOWN_TIME, faderMinutes);
362                     }
363                     if (splitFader[1] != null) {
364                         State isTimerRunning = splitFader[1].equals("-1") ? OnOffType.OFF : OnOffType.ON;
365                         logger.debug("isTimerRunning '{}' for device '{}' received", isTimerRunning,
366                                 getThing().getUID());
367                         updateState(CHANNEL_TIMER_START, isTimerRunning);
368                         if (isTimerRunning.equals(OnOffType.ON)) {
369                             updateState(CHANNEL_STATE, OnOffType.ON);
370                         }
371                     }
372                     if (splitFader[2] != null) {
373                         State isFaderEnabled = splitFader[1].equals("0") ? OnOffType.OFF : OnOffType.ON;
374                         logger.debug("isFaderEnabled '{}' for device '{}' received", isFaderEnabled,
375                                 getThing().getUID());
376                         updateState(CHANNEL_FADER_ENABLED, isFaderEnabled);
377                     }
378                     break;
379                 case "nightMode":
380                     State nightModeState = "0".equals(value) ? OnOffType.OFF : OnOffType.ON;
381                     currentNightModeState = value;
382                     logger.debug("nightModeState '{}' for device '{}' received", nightModeState, getThing().getUID());
383                     updateState(CHANNEL_NIGHT_MODE, nightModeState);
384                     break;
385                 case "startTime":
386                     State startTimeState = getDateTimeState(value);
387                     logger.debug("startTimeState '{}' for device '{}' received", startTimeState, getThing().getUID());
388                     if (startTimeState != null) {
389                         updateState(CHANNEL_START_TIME, startTimeState);
390                     }
391                     break;
392                 case "endTime":
393                     State endTimeState = getDateTimeState(value);
394                     logger.debug("endTimeState '{}' for device '{}' received", endTimeState, getThing().getUID());
395                     if (endTimeState != null) {
396                         updateState(CHANNEL_END_TIME, endTimeState);
397                     }
398                     break;
399                 case "nightModeBrightness":
400                     int nightModeBrightnessValue = Integer.valueOf(value);
401                     currentNightModeBrightness = nightModeBrightnessValue;
402                     State nightModeBrightnessState = new PercentType(nightModeBrightnessValue);
403                     logger.debug("nightModeBrightnessState '{}' for device '{}' received", nightModeBrightnessState,
404                             getThing().getUID());
405                     updateState(CHANNEL_NIGHT_MODE_BRIGHTNESS, nightModeBrightnessState);
406                     break;
407             }
408         }
409     }
410
411     /**
412      * The {@link updateWemoState} polls the actual state of a WeMo device and
413      * calls {@link onValueReceived} to update the statemap and channels..
414      *
415      */
416     protected void updateWemoState() {
417         String wemoURL = getWemoURL(BASICACTION);
418         if (wemoURL == null) {
419             logger.debug("Failed to get actual state for device '{}': URL cannot be created", getThing().getUID());
420             return;
421         }
422         String action = "GetBinaryState";
423         String variable = null;
424         String actionService = BASICACTION;
425         String value = null;
426         String soapHeader = "\"urn:Belkin:service:" + actionService + ":1#" + action + "\"";
427         String content = createStateRequestContent(action, actionService);
428         try {
429             String wemoCallResponse = wemoHttpCaller.executeCall(wemoURL, soapHeader, content);
430             value = substringBetween(wemoCallResponse, "<BinaryState>", "</BinaryState>");
431             variable = "BinaryState";
432             this.onValueReceived(variable, value, actionService + "1");
433             value = substringBetween(wemoCallResponse, "<brightness>", "</brightness>");
434             variable = "brightness";
435             this.onValueReceived(variable, value, actionService + "1");
436             value = substringBetween(wemoCallResponse, "<fader>", "</fader>");
437             variable = "fader";
438             this.onValueReceived(variable, value, actionService + "1");
439             updateStatus(ThingStatus.ONLINE);
440         } catch (Exception e) {
441             logger.debug("Failed to get actual state for device '{}': {}", getThing().getUID(), e.getMessage());
442             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
443         }
444         action = "GetNightModeConfiguration";
445         variable = null;
446         value = null;
447         soapHeader = "\"urn:Belkin:service:" + actionService + ":1#" + action + "\"";
448         content = createStateRequestContent(action, actionService);
449         try {
450             String wemoCallResponse = wemoHttpCaller.executeCall(wemoURL, soapHeader, content);
451             value = substringBetween(wemoCallResponse, "<startTime>", "</startTime>");
452             variable = "startTime";
453             this.onValueReceived(variable, value, actionService + "1");
454             value = substringBetween(wemoCallResponse, "<endTime>", "</endTime>");
455             variable = "endTime";
456             this.onValueReceived(variable, value, actionService + "1");
457             value = substringBetween(wemoCallResponse, "<nightMode>", "</nightMode>");
458             variable = "nightMode";
459             this.onValueReceived(variable, value, actionService + "1");
460             value = substringBetween(wemoCallResponse, "<nightModeBrightness>", "</nightModeBrightness>");
461             variable = "nightModeBrightness";
462             this.onValueReceived(variable, value, actionService + "1");
463             updateStatus(ThingStatus.ONLINE);
464         } catch (Exception e) {
465             logger.debug("Failed to get actual NightMode state for device '{}': {}", getThing().getUID(),
466                     e.getMessage());
467             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
468         }
469     }
470
471     public @Nullable State getDateTimeState(String attributeValue) {
472         long value = 0;
473         try {
474             value = Long.parseLong(attributeValue);
475         } catch (NumberFormatException e) {
476             logger.warn("Unable to parse attributeValue '{}' for device '{}'; expected long", attributeValue,
477                     getThing().getUID());
478             return null;
479         }
480         ZonedDateTime zoned = ZonedDateTime.ofInstant(Instant.ofEpochSecond(value), TimeZone.getDefault().toZoneId());
481         State dateTimeState = new DateTimeType(zoned);
482         return dateTimeState;
483     }
484
485     public void setBinaryState(String action, String argument, String value) {
486         String wemoURL = getWemoURL(BASICACTION);
487         if (wemoURL == null) {
488             logger.debug("Failed to set binary state for device '{}': URL cannot be created", getThing().getUID());
489             return;
490         }
491         try {
492             String soapHeader = "\"urn:Belkin:service:basicevent:1#SetBinaryState\"";
493             String content = "<?xml version=\"1.0\"?>"
494                     + "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
495                     + "<s:Body>" + "<u:" + action + " xmlns:u=\"urn:Belkin:service:basicevent:1\">" + "<" + argument
496                     + ">" + value + "</" + argument + ">" + "</u:" + action + ">" + "</s:Body>" + "</s:Envelope>";
497
498             wemoHttpCaller.executeCall(wemoURL, soapHeader, content);
499             updateStatus(ThingStatus.ONLINE);
500         } catch (Exception e) {
501             logger.debug("Failed to set binaryState '{}' for device '{}': {}", value, getThing().getUID(),
502                     e.getMessage());
503             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
504         }
505     }
506
507     public void setTimerStart(String action, String argument, String value) {
508         String wemoURL = getWemoURL(BASICACTION);
509         if (wemoURL == null) {
510             logger.warn("Failed to set timerStart for device '{}': URL cannot be created", getThing().getUID());
511             return;
512         }
513         try {
514             String soapHeader = "\"urn:Belkin:service:basicevent:1#SetBinaryState\"";
515             String content = "<?xml version=\"1.0\"?>"
516                     + "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
517                     + "<s:Body>" + "<u:SetBinaryState xmlns:u=\"urn:Belkin:service:basicevent:1\">" + value
518                     + "</u:SetBinaryState>" + "</s:Body>" + "</s:Envelope>";
519             wemoHttpCaller.executeCall(wemoURL, soapHeader, content);
520             updateStatus(ThingStatus.ONLINE);
521         } catch (Exception e) {
522             logger.debug("Failed to set timerStart '{}' for device '{}': {}", value, getThing().getUID(),
523                     e.getMessage());
524             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
525         }
526     }
527 }