2 * Copyright (c) 2010-2023 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.mercedesme.internal.handler;
15 import java.net.SocketException;
16 import java.util.Optional;
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;
38 * The {@link AccountHandler} takes care of the valid authorization for the user account
40 * @author Bernd Weymann - Initial contribution
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();
49 Optional<AccountConfiguration> config = Optional.empty();
51 public AccountHandler(Bridge bridge, HttpClient hc, OAuthFactory oaf) {
58 public void handleCommand(ChannelUID channelUID, Command command) {
59 // no commands available
63 public void initialize() {
64 config = Optional.of(getConfigAs(AccountConfiguration.class));
66 String configValidReason = configValid();
67 if (!configValidReason.isEmpty()) {
68 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, configValidReason);
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);
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());
91 Utils.addPort(config.get().callbackPort);
93 if (!updateConfig.containsKey("callbackIP")) {
96 ip = Utils.getCallbackIP();
97 updateConfig.put("callbackIP", ip);
98 } catch (SocketException e) {
99 logger.info("Cannot detect IP address {}", e.getMessage());
102 super.updateConfiguration(updateConfig);
103 // get new config after update
104 config = Optional.of(getConfigAs(AccountConfiguration.class));
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;
119 return Constants.EMPTY;
124 public void dispose() {
125 if (server.isPresent()) {
126 CallbackServer serv = server.get();
129 server = Optional.empty();
130 Utils.removePort(config.get().callbackPort);
135 public void handleRemoval() {
136 server.ifPresent(s -> s.deleteOAuthServiceAndAccessToken());
137 super.handleRemoval();
141 * https://next.openhab.org/javadoc/latest/org/openhab/core/auth/client/oauth2/package-summary.html
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);
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") + "\"]");
162 public String getToken() {
163 return server.get().getToken();
166 public String getImageApiKey() {
167 return config.get().imageApiKey;
171 public String toString() {
172 return Integer.toString(config.get().callbackPort);