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