2 * Copyright (c) 2010-2022 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.wemo.internal.handler;
15 import static org.openhab.binding.wemo.internal.WemoBindingConstants.*;
16 import static org.openhab.binding.wemo.internal.WemoUtil.*;
18 import java.util.concurrent.ScheduledFuture;
19 import java.util.concurrent.TimeUnit;
21 import org.eclipse.jdt.annotation.NonNullByDefault;
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.openhab.binding.wemo.internal.http.WemoHttpCall;
24 import org.openhab.core.config.core.Configuration;
25 import org.openhab.core.io.transport.upnp.UpnpIOService;
26 import org.openhab.core.library.types.OnOffType;
27 import org.openhab.core.thing.ChannelUID;
28 import org.openhab.core.thing.Thing;
29 import org.openhab.core.thing.ThingStatus;
30 import org.openhab.core.thing.ThingStatusDetail;
31 import org.openhab.core.types.Command;
32 import org.openhab.core.types.RefreshType;
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
37 * The {@link WemoHandler} is responsible for handling commands, which are
38 * sent to one of the channels and to update their states.
40 * @author Hans-Jörg Merk - Initial contribution
41 * @author Kai Kreuzer - some refactoring for performance and simplification
42 * @author Stefan Bußweiler - Added new thing status handling
43 * @author Erdoan Hadzhiyusein - Adapted the class to work with the new DateTimeType
44 * @author Mihir Patil - Added standby switch
47 public abstract class WemoHandler extends WemoBaseThingHandler {
49 private final Logger logger = LoggerFactory.getLogger(WemoHandler.class);
51 private final Object jobLock = new Object();
53 private @Nullable ScheduledFuture<?> pollingJob;
55 public WemoHandler(Thing thing, UpnpIOService upnpIOService, WemoHttpCall wemoHttpCaller) {
56 super(thing, upnpIOService, wemoHttpCaller);
58 logger.debug("Creating a WemoHandler for thing '{}'", getThing().getUID());
62 public void initialize() {
64 Configuration configuration = getConfig();
66 if (configuration.get(UDN) != null) {
67 logger.debug("Initializing WemoHandler for UDN '{}'", configuration.get(UDN));
68 addSubscription(BASICEVENT);
69 if (THING_TYPE_INSIGHT.equals(thing.getThingTypeUID())) {
70 addSubscription(INSIGHTEVENT);
73 pollingJob = scheduler.scheduleWithFixedDelay(this::poll, 0, DEFAULT_REFRESH_INTERVAL_SECONDS,
75 updateStatus(ThingStatus.ONLINE);
77 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
78 "@text/config-status.error.missing-udn");
79 logger.debug("Cannot initalize WemoHandler. UDN not set.");
84 public void dispose() {
85 logger.debug("WemoHandler disposed for thing {}", getThing().getUID());
87 ScheduledFuture<?> job = this.pollingJob;
91 this.pollingJob = null;
96 synchronized (jobLock) {
97 if (pollingJob == null) {
101 logger.debug("Polling job");
103 // Check if the Wemo device is set in the UPnP service registry
104 // If not, set the thing state to ONLINE/CONFIG-PENDING and wait for the next poll
105 if (!isUpnpDeviceRegistered()) {
106 logger.debug("UPnP device {} not yet registered", getUDN());
107 updateStatus(ThingStatus.ONLINE, ThingStatusDetail.CONFIGURATION_PENDING,
108 "@text/config-status.pending.device-not-registered [\"" + getUDN() + "\"]");
112 } catch (Exception e) {
113 logger.debug("Exception during poll: {}", e.getMessage(), e);
119 public void handleCommand(ChannelUID channelUID, Command command) {
120 String localHost = getHost();
121 if (localHost.isEmpty()) {
122 logger.warn("Failed to send command '{}' for device '{}': IP address missing", command,
123 getThing().getUID());
124 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
125 "@text/config-status.error.missing-ip");
128 String wemoURL = getWemoURL(localHost, BASICACTION);
129 if (wemoURL == null) {
130 logger.debug("Failed to send command '{}' for device '{}': URL cannot be created", command,
131 getThing().getUID());
132 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
133 "@text/config-status.error.missing-url");
136 if (command instanceof RefreshType) {
139 } catch (Exception e) {
140 logger.debug("Exception during poll", e);
142 } else if (CHANNEL_STATE.equals(channelUID.getId())) {
143 if (command instanceof OnOffType) {
145 boolean binaryState = OnOffType.ON.equals(command) ? true : false;
146 String soapHeader = "\"urn:Belkin:service:basicevent:1#SetBinaryState\"";
147 String content = createBinaryStateContent(binaryState);
148 wemoHttpCaller.executeCall(wemoURL, soapHeader, content);
149 updateStatus(ThingStatus.ONLINE);
150 } catch (Exception e) {
151 logger.warn("Failed to send command '{}' for device '{}': {}", command, getThing().getUID(),
153 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
160 * The {@link updateWemoState} polls the actual state of a WeMo device and
161 * calls {@link onValueReceived} to update the statemap and channels..
164 protected void updateWemoState() {
165 String actionService = BASICACTION;
166 String localhost = getHost();
167 if (localhost.isEmpty()) {
168 logger.warn("Failed to get actual state for device '{}': IP address missing", getThing().getUID());
169 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
170 "@text/config-status.error.missing-ip");
173 String action = "GetBinaryState";
174 String variable = "BinaryState";
176 if ("insight".equals(getThing().getThingTypeUID().getId())) {
177 action = "GetInsightParams";
178 variable = "InsightParams";
179 actionService = INSIGHTACTION;
181 String wemoURL = getWemoURL(localhost, actionService);
182 if (wemoURL == null) {
183 logger.debug("Failed to get actual state for device '{}': URL cannot be created", getThing().getUID());
184 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
185 "@text/config-status.error.missing-url");
188 String soapHeader = "\"urn:Belkin:service:" + actionService + ":1#" + action + "\"";
189 String content = createStateRequestContent(action, actionService);
191 String wemoCallResponse = wemoHttpCaller.executeCall(wemoURL, soapHeader, content);
192 if ("InsightParams".equals(variable)) {
193 value = substringBetween(wemoCallResponse, "<InsightParams>", "</InsightParams>");
195 value = substringBetween(wemoCallResponse, "<BinaryState>", "</BinaryState>");
197 if (value.length() != 0) {
198 logger.trace("New state '{}' for device '{}' received", value, getThing().getUID());
199 this.onValueReceived(variable, value, actionService + "1");
201 } catch (Exception e) {
202 logger.warn("Failed to get actual state for device '{}': {}", getThing().getUID(), e.getMessage());