]> git.basschouten.com Git - openhab-addons.git/blob
d84761fee05f6ada6b55c726ef71260a3d5d046e
[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.windcentrale.internal.handler;
14
15 import static java.util.function.Predicate.not;
16
17 import java.util.Collection;
18 import java.util.List;
19 import java.util.Objects;
20 import java.util.concurrent.CopyOnWriteArrayList;
21 import java.util.concurrent.Future;
22 import java.util.stream.Collectors;
23 import java.util.stream.Stream;
24
25 import org.eclipse.jdt.annotation.NonNullByDefault;
26 import org.eclipse.jdt.annotation.Nullable;
27 import org.openhab.binding.windcentrale.internal.WindcentraleDiscoveryService;
28 import org.openhab.binding.windcentrale.internal.api.RequestListener;
29 import org.openhab.binding.windcentrale.internal.api.TokenProvider;
30 import org.openhab.binding.windcentrale.internal.api.WindcentraleAPI;
31 import org.openhab.binding.windcentrale.internal.config.AccountConfiguration;
32 import org.openhab.binding.windcentrale.internal.exception.FailedGettingDataException;
33 import org.openhab.binding.windcentrale.internal.exception.InvalidAccessTokenException;
34 import org.openhab.binding.windcentrale.internal.listener.ThingStatusListener;
35 import org.openhab.core.io.net.http.HttpClientFactory;
36 import org.openhab.core.thing.Bridge;
37 import org.openhab.core.thing.ChannelUID;
38 import org.openhab.core.thing.ThingStatus;
39 import org.openhab.core.thing.ThingStatusDetail;
40 import org.openhab.core.thing.binding.BaseBridgeHandler;
41 import org.openhab.core.thing.binding.ThingHandlerService;
42 import org.openhab.core.types.Command;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
45
46 /**
47  * The {@link WindcentraleAccountHandler} provides the {@link WindcentraleAPI} instance used by the windmill handlers.
48  *
49  * @author Wouter Born - Initial contribution
50  */
51 @NonNullByDefault
52 public class WindcentraleAccountHandler extends BaseBridgeHandler {
53
54     private final Logger logger = LoggerFactory.getLogger(WindcentraleAccountHandler.class);
55
56     private final HttpClientFactory httpClientFactory;
57     private final List<ThingStatusListener> thingStatusListeners = new CopyOnWriteArrayList<>();
58
59     private @Nullable WindcentraleAPI api;
60     private @Nullable Exception apiException;
61     private @Nullable Future<?> initializeFuture;
62
63     private final RequestListener requestListener = new RequestListener() {
64         @Override
65         public void onError(Exception exception) {
66             apiException = exception;
67             logger.debug("API exception occurred");
68             updateThingStatus();
69         }
70
71         @Override
72         public void onSuccess() {
73             if (apiException != null) {
74                 apiException = null;
75                 logger.debug("API exception cleared");
76                 updateThingStatus();
77             }
78         }
79     };
80
81     public WindcentraleAccountHandler(Bridge bridge, HttpClientFactory httpClientFactory) {
82         super(bridge);
83         this.httpClientFactory = httpClientFactory;
84     }
85
86     public void addThingStatusListener(ThingStatusListener listener) {
87         thingStatusListeners.add(listener);
88         listener.thingStatusChanged(thing, thing.getStatus());
89     }
90
91     @Override
92     public void dispose() {
93         Future<?> localFuture = initializeFuture;
94         if (localFuture != null) {
95             localFuture.cancel(true);
96             initializeFuture = null;
97         }
98
99         WindcentraleAPI localAPI = api;
100         if (localAPI != null) {
101             localAPI.dispose();
102             api = null;
103         }
104     }
105
106     public @Nullable WindcentraleAPI getAPI() {
107         return api;
108     }
109
110     @Override
111     public void initialize() {
112         updateStatus(ThingStatus.UNKNOWN);
113
114         initializeFuture = scheduler.submit(() -> {
115             api = initializeAPI();
116             updateThingStatus();
117         });
118     }
119
120     private WindcentraleAPI initializeAPI() {
121         AccountConfiguration config = getConfigAs(AccountConfiguration.class);
122         TokenProvider tokenProvider = new TokenProvider(httpClientFactory, config.username, config.password);
123
124         WindcentraleAPI api = new WindcentraleAPI(httpClientFactory, tokenProvider);
125         api.addRequestListener(requestListener);
126         apiException = null;
127
128         try {
129             api.getProjects();
130             api.getLiveData();
131         } catch (FailedGettingDataException | InvalidAccessTokenException e) {
132             apiException = e;
133         }
134         return api;
135     }
136
137     @Override
138     public Collection<Class<? extends ThingHandlerService>> getServices() {
139         return List.of(WindcentraleDiscoveryService.class);
140     }
141
142     @Override
143     public void handleCommand(ChannelUID channelUID, Command command) {
144     }
145
146     public void removeThingStatusListener(ThingStatusListener listener) {
147         thingStatusListeners.remove(listener);
148     }
149
150     @Override
151     protected void updateStatus(ThingStatus status, ThingStatusDetail detail, @Nullable String comment) {
152         ThingStatus oldStatus = thing.getStatus();
153         super.updateStatus(status, detail, comment);
154         ThingStatus newStatus = thing.getStatus();
155
156         if (!oldStatus.equals(newStatus)) {
157             logger.debug("Updating listeners with status {}", status);
158             for (ThingStatusListener listener : thingStatusListeners) {
159                 listener.thingStatusChanged(thing, status);
160             }
161         }
162     }
163
164     private void updateThingStatus() {
165         Exception e = apiException;
166         if (e != null) {
167             if (e instanceof InvalidAccessTokenException) {
168                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
169             } else {
170                 Throwable cause = e.getCause();
171                 String description = Stream
172                         .of(Objects.requireNonNullElse(e.getMessage(), ""),
173                                 cause == null ? "" : Objects.requireNonNullElse(cause.getMessage(), ""))
174                         .filter(not(String::isBlank)) //
175                         .collect(Collectors.joining(": "));
176                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, description);
177             }
178         } else {
179             updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE);
180         }
181     }
182 }