]> git.basschouten.com Git - openhab-addons.git/blob
8c66336888d0bdffe984c047c9cd1c68ae893304
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2022 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.util.concurrent.ScheduledFuture;
19 import java.util.concurrent.TimeUnit;
20
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;
35
36 /**
37  * The {@link WemoHandler} is responsible for handling commands, which are
38  * sent to one of the channels and to update their states.
39  *
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
45  */
46 @NonNullByDefault
47 public abstract class WemoHandler extends WemoBaseThingHandler {
48
49     private final Logger logger = LoggerFactory.getLogger(WemoHandler.class);
50
51     private final Object jobLock = new Object();
52
53     private @Nullable ScheduledFuture<?> pollingJob;
54
55     public WemoHandler(Thing thing, UpnpIOService upnpIOService, WemoHttpCall wemoHttpCaller) {
56         super(thing, upnpIOService, wemoHttpCaller);
57
58         logger.debug("Creating a WemoHandler for thing '{}'", getThing().getUID());
59     }
60
61     @Override
62     public void initialize() {
63         super.initialize();
64         Configuration configuration = getConfig();
65
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);
71             }
72             host = getHost();
73             pollingJob = scheduler.scheduleWithFixedDelay(this::poll, 0, DEFAULT_REFRESH_INTERVAL_SECONDS,
74                     TimeUnit.SECONDS);
75             updateStatus(ThingStatus.ONLINE);
76         } else {
77             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
78                     "@text/config-status.error.missing-udn");
79             logger.debug("Cannot initalize WemoHandler. UDN not set.");
80         }
81     }
82
83     @Override
84     public void dispose() {
85         logger.debug("WemoHandler disposed for thing {}", getThing().getUID());
86
87         ScheduledFuture<?> job = this.pollingJob;
88         if (job != null) {
89             job.cancel(true);
90         }
91         this.pollingJob = null;
92         super.dispose();
93     }
94
95     private void poll() {
96         synchronized (jobLock) {
97             if (pollingJob == null) {
98                 return;
99             }
100             try {
101                 logger.debug("Polling job");
102                 host = getHost();
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() + "\"]");
109                     return;
110                 }
111                 updateWemoState();
112             } catch (Exception e) {
113                 logger.debug("Exception during poll: {}", e.getMessage(), e);
114             }
115         }
116     }
117
118     @Override
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");
126             return;
127         }
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");
134             return;
135         }
136         if (command instanceof RefreshType) {
137             try {
138                 updateWemoState();
139             } catch (Exception e) {
140                 logger.debug("Exception during poll", e);
141             }
142         } else if (CHANNEL_STATE.equals(channelUID.getId())) {
143             if (command instanceof OnOffType) {
144                 try {
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(),
152                             e.getMessage());
153                     updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
154                 }
155             }
156         }
157     }
158
159     /**
160      * The {@link updateWemoState} polls the actual state of a WeMo device and
161      * calls {@link onValueReceived} to update the statemap and channels..
162      *
163      */
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");
171             return;
172         }
173         String action = "GetBinaryState";
174         String variable = "BinaryState";
175         String value = null;
176         if ("insight".equals(getThing().getThingTypeUID().getId())) {
177             action = "GetInsightParams";
178             variable = "InsightParams";
179             actionService = INSIGHTACTION;
180         }
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");
186             return;
187         }
188         String soapHeader = "\"urn:Belkin:service:" + actionService + ":1#" + action + "\"";
189         String content = createStateRequestContent(action, actionService);
190         try {
191             String wemoCallResponse = wemoHttpCaller.executeCall(wemoURL, soapHeader, content);
192             if ("InsightParams".equals(variable)) {
193                 value = substringBetween(wemoCallResponse, "<InsightParams>", "</InsightParams>");
194             } else {
195                 value = substringBetween(wemoCallResponse, "<BinaryState>", "</BinaryState>");
196             }
197             if (value.length() != 0) {
198                 logger.trace("New state '{}' for device '{}' received", value, getThing().getUID());
199                 this.onValueReceived(variable, value, actionService + "1");
200             }
201         } catch (Exception e) {
202             logger.warn("Failed to get actual state for device '{}': {}", getThing().getUID(), e.getMessage());
203         }
204     }
205 }