]> git.basschouten.com Git - openhab-addons.git/blob
5804401e1a16f0ef7d48d74b13746ffa5eb0ce5a
[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.jupnp.UpnpService;
24 import org.openhab.binding.wemo.internal.http.WemoHttpCall;
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  * @author Jacob Laursen - Refactoring
46  */
47 @NonNullByDefault
48 public abstract class WemoHandler extends WemoBaseThingHandler {
49
50     private final Logger logger = LoggerFactory.getLogger(WemoHandler.class);
51
52     private final Object jobLock = new Object();
53
54     private @Nullable ScheduledFuture<?> pollingJob;
55
56     public WemoHandler(Thing thing, UpnpIOService upnpIOService, UpnpService upnpService, WemoHttpCall wemoHttpCaller) {
57         super(thing, upnpIOService, upnpService, wemoHttpCaller);
58
59         logger.debug("Creating a WemoHandler for thing '{}'", getThing().getUID());
60     }
61
62     @Override
63     public void initialize() {
64         super.initialize();
65
66         addSubscription(BASICEVENT);
67         if (THING_TYPE_INSIGHT.equals(thing.getThingTypeUID())) {
68             addSubscription(INSIGHTEVENT);
69         }
70         pollingJob = scheduler.scheduleWithFixedDelay(this::poll, 0, DEFAULT_REFRESH_INTERVAL_SECONDS,
71                 TimeUnit.SECONDS);
72     }
73
74     @Override
75     public void dispose() {
76         logger.debug("WemoHandler disposed for thing {}", getThing().getUID());
77
78         ScheduledFuture<?> job = this.pollingJob;
79         if (job != null) {
80             job.cancel(true);
81         }
82         this.pollingJob = null;
83         super.dispose();
84     }
85
86     private void poll() {
87         synchronized (jobLock) {
88             if (pollingJob == null) {
89                 return;
90             }
91             try {
92                 logger.debug("Polling job");
93                 // Check if the Wemo device is set in the UPnP service registry
94                 if (!isUpnpDeviceRegistered()) {
95                     logger.debug("UPnP device {} not yet registered", getUDN());
96                     updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE,
97                             "@text/config-status.pending.device-not-registered [\"" + getUDN() + "\"]");
98                     return;
99                 }
100                 updateWemoState();
101             } catch (Exception e) {
102                 logger.debug("Exception during poll: {}", e.getMessage(), e);
103             }
104         }
105     }
106
107     @Override
108     public void handleCommand(ChannelUID channelUID, Command command) {
109         String wemoURL = getWemoURL(BASICACTION);
110         if (wemoURL == null) {
111             logger.debug("Failed to send command '{}' for device '{}': URL cannot be created", command,
112                     getThing().getUID());
113             return;
114         }
115         if (command instanceof RefreshType) {
116             try {
117                 updateWemoState();
118             } catch (Exception e) {
119                 logger.debug("Exception during poll", e);
120             }
121         } else if (CHANNEL_STATE.equals(channelUID.getId())) {
122             if (command instanceof OnOffType) {
123                 try {
124                     boolean binaryState = OnOffType.ON.equals(command) ? true : false;
125                     String soapHeader = "\"urn:Belkin:service:basicevent:1#SetBinaryState\"";
126                     String content = createBinaryStateContent(binaryState);
127                     wemoHttpCaller.executeCall(wemoURL, soapHeader, content);
128                     updateStatus(ThingStatus.ONLINE);
129                 } catch (Exception e) {
130                     logger.warn("Failed to send command '{}' for device '{}': {}", command, getThing().getUID(),
131                             e.getMessage());
132                     updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
133                 }
134             }
135         }
136     }
137
138     /**
139      * The {@link updateWemoState} polls the actual state of a WeMo device and
140      * calls {@link onValueReceived} to update the statemap and channels..
141      *
142      */
143     protected void updateWemoState() {
144         String actionService = BASICACTION;
145         String action = "GetBinaryState";
146         String variable = "BinaryState";
147         String value = null;
148         if ("insight".equals(getThing().getThingTypeUID().getId())) {
149             action = "GetInsightParams";
150             variable = "InsightParams";
151             actionService = INSIGHTACTION;
152         }
153         String wemoURL = getWemoURL(actionService);
154         if (wemoURL == null) {
155             logger.debug("Failed to get actual state for device '{}': URL cannot be created", getThing().getUID());
156             return;
157         }
158         String soapHeader = "\"urn:Belkin:service:" + actionService + ":1#" + action + "\"";
159         String content = createStateRequestContent(action, actionService);
160         try {
161             String wemoCallResponse = wemoHttpCaller.executeCall(wemoURL, soapHeader, content);
162             if ("InsightParams".equals(variable)) {
163                 value = substringBetween(wemoCallResponse, "<InsightParams>", "</InsightParams>");
164             } else {
165                 value = substringBetween(wemoCallResponse, "<BinaryState>", "</BinaryState>");
166             }
167             if (value.length() != 0) {
168                 logger.trace("New state '{}' for device '{}' received", value, getThing().getUID());
169                 this.onValueReceived(variable, value, actionService + "1");
170             }
171             updateStatus(ThingStatus.ONLINE);
172         } catch (Exception e) {
173             logger.warn("Failed to get actual state for device '{}': {}", getThing().getUID(), e.getMessage());
174             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
175         }
176     }
177 }