]> git.basschouten.com Git - openhab-addons.git/blob
4aa9eb7c7612ec2d51b65e9a8a264866f048dcf3
[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.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.io.transport.upnp.UpnpIOService;
25 import org.openhab.core.library.types.OnOffType;
26 import org.openhab.core.thing.ChannelUID;
27 import org.openhab.core.thing.Thing;
28 import org.openhab.core.thing.ThingStatus;
29 import org.openhab.core.thing.ThingStatusDetail;
30 import org.openhab.core.types.Command;
31 import org.openhab.core.types.RefreshType;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34
35 /**
36  * The {@link WemoHandler} is responsible for handling commands, which are
37  * sent to one of the channels and to update their states.
38  *
39  * @author Hans-Jörg Merk - Initial contribution
40  * @author Kai Kreuzer - some refactoring for performance and simplification
41  * @author Stefan Bußweiler - Added new thing status handling
42  * @author Erdoan Hadzhiyusein - Adapted the class to work with the new DateTimeType
43  * @author Mihir Patil - Added standby switch
44  * @author Jacob Laursen - Refactoring
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
65         addSubscription(BASICEVENT);
66         if (THING_TYPE_INSIGHT.equals(thing.getThingTypeUID())) {
67             addSubscription(INSIGHTEVENT);
68         }
69         pollingJob = scheduler.scheduleWithFixedDelay(this::poll, 0, DEFAULT_REFRESH_INTERVAL_SECONDS,
70                 TimeUnit.SECONDS);
71     }
72
73     @Override
74     public void dispose() {
75         logger.debug("WemoHandler disposed for thing {}", getThing().getUID());
76
77         ScheduledFuture<?> job = this.pollingJob;
78         if (job != null) {
79             job.cancel(true);
80         }
81         this.pollingJob = null;
82         super.dispose();
83     }
84
85     private void poll() {
86         synchronized (jobLock) {
87             if (pollingJob == null) {
88                 return;
89             }
90             try {
91                 logger.debug("Polling job");
92                 // Check if the Wemo device is set in the UPnP service registry
93                 if (!isUpnpDeviceRegistered()) {
94                     logger.debug("UPnP device {} not yet registered", getUDN());
95                     updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE,
96                             "@text/config-status.pending.device-not-registered [\"" + getUDN() + "\"]");
97                     return;
98                 }
99                 updateWemoState();
100             } catch (Exception e) {
101                 logger.debug("Exception during poll: {}", e.getMessage(), e);
102             }
103         }
104     }
105
106     @Override
107     public void handleCommand(ChannelUID channelUID, Command command) {
108         String wemoURL = getWemoURL(BASICACTION);
109         if (wemoURL == null) {
110             logger.debug("Failed to send command '{}' for device '{}': URL cannot be created", command,
111                     getThing().getUID());
112             return;
113         }
114         if (command instanceof RefreshType) {
115             try {
116                 updateWemoState();
117             } catch (Exception e) {
118                 logger.debug("Exception during poll", e);
119             }
120         } else if (CHANNEL_STATE.equals(channelUID.getId())) {
121             if (command instanceof OnOffType) {
122                 try {
123                     boolean binaryState = OnOffType.ON.equals(command) ? true : false;
124                     String soapHeader = "\"urn:Belkin:service:basicevent:1#SetBinaryState\"";
125                     String content = createBinaryStateContent(binaryState);
126                     wemoHttpCaller.executeCall(wemoURL, soapHeader, content);
127                     updateStatus(ThingStatus.ONLINE);
128                 } catch (Exception e) {
129                     logger.warn("Failed to send command '{}' for device '{}': {}", command, getThing().getUID(),
130                             e.getMessage());
131                     updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
132                 }
133             }
134         }
135     }
136
137     /**
138      * The {@link updateWemoState} polls the actual state of a WeMo device and
139      * calls {@link onValueReceived} to update the statemap and channels..
140      *
141      */
142     protected void updateWemoState() {
143         String actionService = BASICACTION;
144         String action = "GetBinaryState";
145         String variable = "BinaryState";
146         String value = null;
147         if ("insight".equals(getThing().getThingTypeUID().getId())) {
148             action = "GetInsightParams";
149             variable = "InsightParams";
150             actionService = INSIGHTACTION;
151         }
152         String wemoURL = getWemoURL(actionService);
153         if (wemoURL == null) {
154             logger.debug("Failed to get actual state for device '{}': URL cannot be created", getThing().getUID());
155             return;
156         }
157         String soapHeader = "\"urn:Belkin:service:" + actionService + ":1#" + action + "\"";
158         String content = createStateRequestContent(action, actionService);
159         try {
160             String wemoCallResponse = wemoHttpCaller.executeCall(wemoURL, soapHeader, content);
161             if ("InsightParams".equals(variable)) {
162                 value = substringBetween(wemoCallResponse, "<InsightParams>", "</InsightParams>");
163             } else {
164                 value = substringBetween(wemoCallResponse, "<BinaryState>", "</BinaryState>");
165             }
166             if (value.length() != 0) {
167                 logger.trace("New state '{}' for device '{}' received", value, getThing().getUID());
168                 this.onValueReceived(variable, value, actionService + "1");
169             }
170             updateStatus(ThingStatus.ONLINE);
171         } catch (Exception e) {
172             logger.warn("Failed to get actual state for device '{}': {}", getThing().getUID(), e.getMessage());
173             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
174         }
175     }
176 }