]> git.basschouten.com Git - openhab-addons.git/blob
577dbcac4226b25164022352f5dfdb1fc8546888
[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 java.net.URL;
16 import java.time.Instant;
17 import java.util.Map;
18 import java.util.concurrent.ConcurrentHashMap;
19 import java.util.concurrent.ScheduledFuture;
20 import java.util.concurrent.TimeUnit;
21
22 import org.eclipse.jdt.annotation.NonNullByDefault;
23 import org.eclipse.jdt.annotation.Nullable;
24 import org.openhab.binding.wemo.internal.WemoBindingConstants;
25 import org.openhab.binding.wemo.internal.http.WemoHttpCall;
26 import org.openhab.core.io.transport.upnp.UpnpIOParticipant;
27 import org.openhab.core.io.transport.upnp.UpnpIOService;
28 import org.openhab.core.thing.ChannelUID;
29 import org.openhab.core.thing.Thing;
30 import org.openhab.core.thing.binding.BaseThingHandler;
31 import org.openhab.core.types.Command;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34
35 /**
36  * {@link WemoBaseThingHandler} provides a base implementation for the
37  * concrete WeMo handlers.
38  *
39  * @author Jacob Laursen - Initial contribution
40  */
41 @NonNullByDefault
42 public abstract class WemoBaseThingHandler extends BaseThingHandler implements UpnpIOParticipant {
43
44     private static final int SUBSCRIPTION_RENEWAL_INITIAL_DELAY_SECONDS = 15;
45     private static final int SUBSCRIPTION_RENEWAL_INTERVAL_SECONDS = 60;
46
47     private final Logger logger = LoggerFactory.getLogger(WemoBaseThingHandler.class);
48
49     protected @Nullable UpnpIOService service;
50     protected WemoHttpCall wemoHttpCaller;
51     protected String host = "";
52
53     private Map<String, Instant> subscriptions = new ConcurrentHashMap<String, Instant>();
54     private @Nullable ScheduledFuture<?> subscriptionRenewalJob;
55
56     public WemoBaseThingHandler(Thing thing, UpnpIOService upnpIOService, WemoHttpCall wemoHttpCaller) {
57         super(thing);
58         this.service = upnpIOService;
59         this.wemoHttpCaller = wemoHttpCaller;
60     }
61
62     @Override
63     public void initialize() {
64         UpnpIOService service = this.service;
65         if (service != null) {
66             logger.debug("Registering UPnP participant for {}", getThing().getUID());
67             service.registerParticipant(this);
68         }
69     }
70
71     @Override
72     public void dispose() {
73         removeSubscriptions();
74         UpnpIOService service = this.service;
75         if (service != null) {
76             logger.debug("Unregistering UPnP participant for {}", getThing().getUID());
77             service.unregisterParticipant(this);
78         }
79         cancelSubscriptionRenewalJob();
80     }
81
82     @Override
83     public void handleCommand(ChannelUID channelUID, Command command) {
84         // can be overridden by subclasses
85     }
86
87     @Override
88     public void onStatusChanged(boolean status) {
89         // can be overridden by subclasses
90     }
91
92     @Override
93     public void onValueReceived(@Nullable String variable, @Nullable String value, @Nullable String service) {
94         // can be overridden by subclasses
95     }
96
97     @Override
98     public void onServiceSubscribed(@Nullable String service, boolean succeeded) {
99         if (service == null) {
100             return;
101         }
102         logger.debug("Subscription to service {} for {} {}", service, getUDN(), succeeded ? "succeeded" : "failed");
103         if (succeeded) {
104             subscriptions.put(service, Instant.now());
105         }
106     }
107
108     @Override
109     public @Nullable String getUDN() {
110         return (String) this.getThing().getConfiguration().get(WemoBindingConstants.UDN);
111     }
112
113     protected boolean isUpnpDeviceRegistered() {
114         UpnpIOService service = this.service;
115         return service != null && service.isRegistered(this);
116     }
117
118     protected void addSubscription(String serviceId) {
119         if (subscriptions.containsKey(serviceId)) {
120             logger.debug("{} already subscribed to {}", getUDN(), serviceId);
121             return;
122         }
123         if (subscriptions.isEmpty()) {
124             logger.debug("Adding first GENA subscription for {}, scheduling renewal job", getUDN());
125             scheduleSubscriptionRenewalJob();
126         }
127         subscriptions.put(serviceId, Instant.ofEpochSecond(0));
128         UpnpIOService service = this.service;
129         if (service == null) {
130             return;
131         }
132         if (!service.isRegistered(this)) {
133             logger.debug("Registering UPnP participant for {}", getUDN());
134             service.registerParticipant(this);
135         }
136         if (!service.isRegistered(this)) {
137             logger.debug("Trying to add GENA subscription {} for {}, but service is not registered", serviceId,
138                     getUDN());
139             return;
140         }
141         logger.debug("Adding GENA subscription {} for {}", serviceId, getUDN());
142         service.addSubscription(this, serviceId, WemoBindingConstants.SUBSCRIPTION_DURATION_SECONDS);
143     }
144
145     protected void removeSubscription(String serviceId) {
146         UpnpIOService service = this.service;
147         if (service == null) {
148             return;
149         }
150         subscriptions.remove(serviceId);
151         if (subscriptions.isEmpty()) {
152             logger.debug("Removing last GENA subscription for {}, cancelling renewal job", getUDN());
153             cancelSubscriptionRenewalJob();
154         }
155         if (!service.isRegistered(this)) {
156             logger.debug("Trying to remove GENA subscription {} for {}, but service is not registered", serviceId,
157                     getUDN());
158             return;
159         }
160         logger.debug("Unsubscribing {} from service {}", getUDN(), serviceId);
161         service.removeSubscription(this, serviceId);
162     }
163
164     private void scheduleSubscriptionRenewalJob() {
165         cancelSubscriptionRenewalJob();
166         this.subscriptionRenewalJob = scheduler.scheduleWithFixedDelay(this::renewSubscriptions,
167                 SUBSCRIPTION_RENEWAL_INITIAL_DELAY_SECONDS, SUBSCRIPTION_RENEWAL_INTERVAL_SECONDS, TimeUnit.SECONDS);
168     }
169
170     private void cancelSubscriptionRenewalJob() {
171         ScheduledFuture<?> subscriptionRenewalJob = this.subscriptionRenewalJob;
172         if (subscriptionRenewalJob != null) {
173             subscriptionRenewalJob.cancel(true);
174         }
175         this.subscriptionRenewalJob = null;
176     }
177
178     private void renewSubscriptions() {
179         if (subscriptions.isEmpty()) {
180             return;
181         }
182         UpnpIOService service = this.service;
183         if (service == null) {
184             return;
185         }
186         if (!service.isRegistered(this)) {
187             service.registerParticipant(this);
188         }
189         if (!service.isRegistered(this)) {
190             logger.debug("Trying to renew GENA subscriptions for {}, but service is not registered", getUDN());
191             return;
192         }
193         logger.debug("Renewing GENA subscriptions for {}", getUDN());
194         subscriptions.forEach((serviceId, lastRenewed) -> {
195             if (lastRenewed.isBefore(Instant.now().minusSeconds(
196                     WemoBindingConstants.SUBSCRIPTION_DURATION_SECONDS - SUBSCRIPTION_RENEWAL_INTERVAL_SECONDS))) {
197                 logger.debug("Subscription for service {} with timestamp {} has expired, renewing", serviceId,
198                         lastRenewed);
199                 service.removeSubscription(this, serviceId);
200                 service.addSubscription(this, serviceId, WemoBindingConstants.SUBSCRIPTION_DURATION_SECONDS);
201             }
202         });
203     }
204
205     private void removeSubscriptions() {
206         if (subscriptions.isEmpty()) {
207             return;
208         }
209         UpnpIOService service = this.service;
210         if (service == null) {
211             return;
212         }
213         if (!service.isRegistered(this)) {
214             logger.debug("Trying to remove GENA subscriptions for {}, but service is not registered",
215                     getThing().getUID());
216             return;
217         }
218         logger.debug("Removing GENA subscriptions for {}", getUDN());
219         subscriptions.forEach((serviceId, lastRenewed) -> {
220             logger.debug("Removing subscription for service {}", serviceId);
221             service.removeSubscription(this, serviceId);
222         });
223         subscriptions.clear();
224     }
225
226     protected String getHost() {
227         String localHost = host;
228         if (!localHost.isEmpty()) {
229             return localHost;
230         }
231         UpnpIOService localService = service;
232         if (localService != null) {
233             URL descriptorURL = localService.getDescriptorURL(this);
234             if (descriptorURL != null) {
235                 return descriptorURL.getHost();
236             }
237         }
238         return "";
239     }
240 }