]> git.basschouten.com Git - openhab-addons.git/blob
1b369d19ab0b4577122c6b8a046174f42e4037ab
[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.mercedesme.internal.handler;
14
15 import java.net.SocketException;
16 import java.util.Optional;
17
18 import org.eclipse.jdt.annotation.NonNullByDefault;
19 import org.eclipse.jetty.client.HttpClient;
20 import org.openhab.binding.mercedesme.internal.Constants;
21 import org.openhab.binding.mercedesme.internal.config.AccountConfiguration;
22 import org.openhab.binding.mercedesme.internal.server.CallbackServer;
23 import org.openhab.binding.mercedesme.internal.server.Utils;
24 import org.openhab.core.auth.client.oauth2.AccessTokenRefreshListener;
25 import org.openhab.core.auth.client.oauth2.AccessTokenResponse;
26 import org.openhab.core.auth.client.oauth2.OAuthFactory;
27 import org.openhab.core.config.core.Configuration;
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 /**
38  * The {@link AccountHandler} takes care of the valid authorization for the user account
39  *
40  * @author Bernd Weymann - Initial contribution
41  */
42 @NonNullByDefault
43 public class AccountHandler extends BaseBridgeHandler implements AccessTokenRefreshListener {
44     private final Logger logger = LoggerFactory.getLogger(AccountHandler.class);
45     private final OAuthFactory oAuthFactory;
46     private final HttpClient httpClient;
47     private Optional<CallbackServer> server = Optional.empty();
48
49     Optional<AccountConfiguration> config = Optional.empty();
50
51     public AccountHandler(Bridge bridge, HttpClient hc, OAuthFactory oaf) {
52         super(bridge);
53         httpClient = hc;
54         oAuthFactory = oaf;
55     }
56
57     @Override
58     public void handleCommand(ChannelUID channelUID, Command command) {
59         // no commands available
60     }
61
62     @Override
63     public void initialize() {
64         config = Optional.of(getConfigAs(AccountConfiguration.class));
65         autodetectCallback();
66         String configValidReason = configValid();
67         if (!configValidReason.isEmpty()) {
68             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, configValidReason);
69         } else {
70             String callbackUrl = Utils.getCallbackAddress(config.get().callbackIP, config.get().callbackPort);
71             thing.setProperty("callbackUrl", callbackUrl);
72             server = Optional.of(new CallbackServer(this, httpClient, oAuthFactory, config.get(), callbackUrl));
73             if (!server.get().start()) {
74                 String textKey = Constants.STATUS_TEXT_PREFIX + thing.getThingTypeUID().getId()
75                         + Constants.STATUS_SERVER_RESTART;
76                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, textKey);
77             } else {
78                 // get fresh token
79                 this.getToken();
80             }
81         }
82     }
83
84     private void autodetectCallback() {
85         // if Callback IP and Callback Port are not set => autodetect these values
86         config = Optional.of(getConfigAs(AccountConfiguration.class));
87         Configuration updateConfig = super.editConfiguration();
88         if (!updateConfig.containsKey("callbackPort")) {
89             updateConfig.put("callbackPort", Utils.getFreePort());
90         } else {
91             Utils.addPort(config.get().callbackPort);
92         }
93         if (!updateConfig.containsKey("callbackIP")) {
94             String ip;
95             try {
96                 ip = Utils.getCallbackIP();
97                 updateConfig.put("callbackIP", ip);
98             } catch (SocketException e) {
99                 logger.info("Cannot detect IP address {}", e.getMessage());
100             }
101         }
102         super.updateConfiguration(updateConfig);
103         // get new config after update
104         config = Optional.of(getConfigAs(AccountConfiguration.class));
105     }
106
107     private String configValid() {
108         config = Optional.of(getConfigAs(AccountConfiguration.class));
109         String textKey = Constants.STATUS_TEXT_PREFIX + thing.getThingTypeUID().getId();
110         if (config.get().callbackIP.equals(Constants.NOT_SET)) {
111             return textKey + Constants.STATUS_IP_MISSING;
112         } else if (config.get().callbackPort == -1) {
113             return textKey + Constants.STATUS_PORT_MISSING;
114         } else if (config.get().clientId.equals(Constants.NOT_SET)) {
115             return textKey + Constants.STATUS_CLIENT_ID_MISSING;
116         } else if (config.get().clientSecret.equals(Constants.NOT_SET)) {
117             return textKey + Constants.STATUS_CLIENT_SECRET_MISSING;
118         } else {
119             return Constants.EMPTY;
120         }
121     }
122
123     @Override
124     public void dispose() {
125         if (server.isPresent()) {
126             CallbackServer serv = server.get();
127             serv.stop();
128             serv.dispose();
129             server = Optional.empty();
130             Utils.removePort(config.get().callbackPort);
131         }
132     }
133
134     @Override
135     public void handleRemoval() {
136         server.ifPresent(s -> s.deleteOAuthServiceAndAccessToken());
137         super.handleRemoval();
138     }
139
140     /**
141      * https://next.openhab.org/javadoc/latest/org/openhab/core/auth/client/oauth2/package-summary.html
142      */
143     @Override
144     public void onAccessTokenResponse(AccessTokenResponse tokenResponse) {
145         if (!tokenResponse.getAccessToken().isEmpty()) {
146             // token not empty - fine
147             updateStatus(ThingStatus.ONLINE);
148         } else if (server.isEmpty()) {
149             // server not running - fix first
150             String textKey = Constants.STATUS_TEXT_PREFIX + thing.getThingTypeUID().getId()
151                     + Constants.STATUS_SERVER_RESTART;
152             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, textKey);
153         } else {
154             // all failed - start manual authorization
155             String textKey = Constants.STATUS_TEXT_PREFIX + thing.getThingTypeUID().getId()
156                     + Constants.STATUS_AUTH_NEEDED;
157             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE,
158                     textKey + " [\"" + thing.getProperties().get("callbackUrl") + "\"]");
159         }
160     }
161
162     public String getToken() {
163         return server.get().getToken();
164     }
165
166     public String getImageApiKey() {
167         return config.get().imageApiKey;
168     }
169
170     @Override
171     public String toString() {
172         return Integer.toString(config.get().callbackPort);
173     }
174 }