2 * Copyright (c) 2010-2022 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.groheondus.internal.handler;
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;
22 import javax.security.auth.login.LoginException;
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;
37 import io.github.floriansw.ondus.api.OndusService;
40 * @author Florian Schmidt and Arne Wohlert - Initial contribution
43 public class GroheOndusAccountHandler extends BaseBridgeHandler {
45 private static final String STORAGE_KEY_REFRESH_TOKEN = "refreshToken";
47 private final Logger logger = LoggerFactory.getLogger(GroheOndusAccountHandler.class);
49 private Storage<String> storage;
50 private @Nullable OndusService ondusService;
51 private @Nullable ScheduledFuture<?> reloginFuture;
53 public GroheOndusAccountHandler(Bridge bridge, Storage<String> storage) {
55 this.storage = storage;
58 public OndusService getService() {
59 OndusService ret = this.ondusService;
61 throw new IllegalStateException("OndusService requested, which is null (UNINITIALIZED)");
67 public void handleCommand(ChannelUID channelUID, Command command) {
68 // Nothing to do for bridge
72 public void dispose() {
73 if (ondusService != null) {
76 if (reloginFuture != null) {
77 reloginFuture.cancel(true);
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");
88 // Config appears to be ok, lets try
90 OndusService ondusService;
91 if (storage.containsKey(STORAGE_KEY_REFRESH_TOKEN)) {
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);
100 logger.debug("No refresh token found, trying to log in using username and password");
101 ondusService = OndusService.loginWebform(config.username, config.password);
103 this.ondusService = ondusService;
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(() -> {
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);
123 }, durationUntilRefresh.getSeconds(), TimeUnit.SECONDS);
124 logger.debug("Scheduled token refresh at {}", refreshTime);
125 updateStatus(ThingStatus.ONLINE);
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);
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());
141 this.storage.remove(STORAGE_KEY_REFRESH_TOKEN);
142 reloginFuture = scheduler.schedule(this::login, 1, TimeUnit.MINUTES);
148 public void initialize() {