]> git.basschouten.com Git - openhab-addons.git/blob
91ee984033d0aa8492add75a0e4c598a2a9fce37
[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.groheondus.internal.handler;
14
15 import java.io.IOException;
16 import java.time.Duration;
17 import java.time.Instant;
18 import java.time.temporal.ChronoUnit;
19 import java.util.concurrent.ScheduledFuture;
20 import java.util.concurrent.TimeUnit;
21
22 import javax.security.auth.login.LoginException;
23
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.openhab.binding.groheondus.internal.GroheOndusAccountConfiguration;
27 import org.openhab.core.storage.Storage;
28 import org.openhab.core.thing.Bridge;
29 import org.openhab.core.thing.ChannelUID;
30 import org.openhab.core.thing.ThingStatus;
31 import org.openhab.core.thing.ThingStatusDetail;
32 import org.openhab.core.thing.binding.BaseBridgeHandler;
33 import org.openhab.core.types.Command;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36
37 import io.github.floriansw.ondus.api.OndusService;
38
39 /**
40  * @author Florian Schmidt and Arne Wohlert - Initial contribution
41  */
42 @NonNullByDefault
43 public class GroheOndusAccountHandler extends BaseBridgeHandler {
44
45     private static final String STORAGE_KEY_REFRESH_TOKEN = "refreshToken";
46
47     private final Logger logger = LoggerFactory.getLogger(GroheOndusAccountHandler.class);
48
49     private Storage<String> storage;
50     private @Nullable OndusService ondusService;
51     private @Nullable ScheduledFuture<?> reloginFuture;
52
53     public GroheOndusAccountHandler(Bridge bridge, Storage<String> storage) {
54         super(bridge);
55         this.storage = storage;
56     }
57
58     public OndusService getService() {
59         OndusService ret = this.ondusService;
60         if (ret == null) {
61             throw new IllegalStateException("OndusService requested, which is null (UNINITIALIZED)");
62         }
63         return ret;
64     }
65
66     @Override
67     public void handleCommand(ChannelUID channelUID, Command command) {
68         // Nothing to do for bridge
69     }
70
71     @Override
72     public void dispose() {
73         if (ondusService != null) {
74             ondusService = null;
75         }
76         if (reloginFuture != null) {
77             reloginFuture.cancel(true);
78         }
79         super.dispose();
80     }
81
82     private void login() {
83         GroheOndusAccountConfiguration config = getConfigAs(GroheOndusAccountConfiguration.class);
84         if (config.username == null || config.password == null) {
85             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
86                     "@text/error.login.missing.credentials");
87         } else {
88             // Config appears to be ok, lets try
89             try {
90                 OndusService ondusService;
91                 if (storage.containsKey(STORAGE_KEY_REFRESH_TOKEN)) {
92                     try {
93                         logger.debug("Trying to login using refresh token");
94                         ondusService = OndusService.login(storage.get(STORAGE_KEY_REFRESH_TOKEN));
95                     } catch (LoginException e) {
96                         logger.debug("Refresh token invalid, try again with username and password");
97                         ondusService = OndusService.loginWebform(config.username, config.password);
98                     }
99                 } else {
100                     logger.debug("No refresh token found, trying to log in using username and password");
101                     ondusService = OndusService.loginWebform(config.username, config.password);
102                 }
103                 this.ondusService = ondusService;
104
105                 // Assuming everything went fine...
106                 Instant expiresAt = ondusService.authorizationExpiresAt();
107                 // Refresh 5 minutes before expiry
108                 Instant refreshTime = expiresAt.minus(5, ChronoUnit.MINUTES);
109                 final OndusService ondusServiceInner = ondusService;
110                 if (refreshTime.isAfter(Instant.now())) {
111                     Duration durationUntilRefresh = Duration.between(Instant.now(), refreshTime);
112                     reloginFuture = scheduler.schedule(() -> {
113                         try {
114                             logger.debug("Refreshing token");
115                             this.storage.put(STORAGE_KEY_REFRESH_TOKEN, ondusServiceInner.refreshAuthorization());
116                             logger.debug("Refreshed token, token expires at {}",
117                                     ondusServiceInner.authorizationExpiresAt());
118                         } catch (Exception e) {
119                             logger.debug("Could not refresh token for GROHE ONDUS account, removing refresh token", e);
120                             this.storage.remove(STORAGE_KEY_REFRESH_TOKEN);
121                         }
122                         login();
123                     }, durationUntilRefresh.getSeconds(), TimeUnit.SECONDS);
124                     logger.debug("Scheduled token refresh at {}", refreshTime);
125                     updateStatus(ThingStatus.ONLINE);
126                 } else {
127                     // Refresh time in the past (happens)
128                     logger.debug("Refresh time for token was in the past, waiting a minute and retrying");
129                     this.storage.remove(STORAGE_KEY_REFRESH_TOKEN);
130                     reloginFuture = scheduler.schedule(this::login, 1, TimeUnit.MINUTES);
131                 }
132
133             } catch (LoginException e) {
134                 logger.debug("Grohe api login failed", e);
135                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "@text/error.login.failed");
136             } catch (IOException e) {
137                 logger.debug("Communication error while logging into the grohe api", e);
138                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
139
140                 // Cleanup and retry
141                 this.storage.remove(STORAGE_KEY_REFRESH_TOKEN);
142                 reloginFuture = scheduler.schedule(this::login, 1, TimeUnit.MINUTES);
143             }
144         }
145     }
146
147     @Override
148     public void initialize() {
149         login();
150     }
151 }