]> git.basschouten.com Git - openhab-addons.git/commitdiff
[tesla] Remove (broken) options to obtain refresh token through credentials (#12537)
authorKai Kreuzer <kai@openhab.org>
Thu, 31 Mar 2022 07:11:55 +0000 (09:11 +0200)
committerGitHub <noreply@github.com>
Thu, 31 Mar 2022 07:11:55 +0000 (09:11 +0200)
* Remove (broken) option to obtain refresh token through credentials
* Remove outdated event streaming code
* Update README

Signed-off-by: Kai Kreuzer <kai@openhab.org>
bundles/org.openhab.binding.tesla/README.md
bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/TeslaBindingConstants.java
bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/command/TeslaCommandExtension.java [deleted file]
bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/discovery/TeslaAccountDiscoveryService.java [deleted file]
bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/handler/TeslaAccountHandler.java
bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/handler/TeslaSSOHandler.java
bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/handler/TeslaVehicleHandler.java
bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/sso/TokenResponse.java
bundles/org.openhab.binding.tesla/src/main/resources/OH-INF/i18n/tesla.properties
bundles/org.openhab.binding.tesla/src/main/resources/OH-INF/thing/account.xml

index 49bac129727ead2bc22b9021b1404fbdaabe8df5..e5c9aa53feab2ceaf1ebfe3dfaecef513068f04e 100644 (file)
@@ -5,7 +5,8 @@ The integration happens through the Tesla Owners Remote API.
 
 ## Supported Things
 
-All current Tesla models are supported by this binding. Access is established through a Tesla account as a bridge.
+All current Tesla models are supported by this binding.
+Access is established through a Tesla account as a bridge.
 
 | Thing Type | Description                                  |
 |------------|----------------------------------------------|
@@ -19,41 +20,16 @@ All current Tesla models are supported by this binding. Access is established th
 
 ## Auto Discovery
 
-If the authentication with the Tesla Account is done through the openHAB console (see "Bridge Configuration" option 1 below), the account is automatically added to the Inbox.
+The account cannot be automatically discovered, but has to be created manually.
 
-Furthermore, once an account is configured, it is automatically queried for associated vehicles and an Inbox entry is created for each of them.
+Once an account is configured, it is automatically queried for associated vehicles and an Inbox entry is created for each of them.
 
-## Bridge Configuration
-
-The `account` bridge requires an OAuth2 refresh token as the only parameter `refreshToken`.
-
-There are three different ways of obtaining the token.
-
-NOTE: Tesla has introduced some captcha mechanism, which might prevent options 1 and 2 from working as expected.
-In case you are only receiving error messages, please make use of option 3!
-
-1. Use the openHAB console
-
-Run the following command on the console and provide your Tesla account credentials (the same that you use in the official Tesla app):
+Note: Vehicles that are asleep might not be discovered, so you might want to wake it up through the Tesla app first.
 
-```
-openhab> openhab:tesla login
-Username (email): mail@example.com
-Password: topsecret
-Attempting login...Attempting login...
-Refresh token: xxxxxxxxxx
-```
-When successfully doing the login through the console, openHAB will automatically create an Inbox entry that is preconfigured with the refresh token, which you can now simply approve.
-
-Alternatively, you can use the refresh token to textually configure your `account` bridge or enter it in a manually created "Tesla Account" thing in the UI.
-
-2. Provide your credentials in the UI
 
-If you do not want to use the openHAB console, you can also manually create a "Tesla Account" thing in the UI by providing your username and password as parameters (to show them, use the "Show More" button) in the "Edit Thing" view and leaving the refresh token parameter field empty.
-
-openHAB will use the provided credentials to retrieve and set the refresh token and automatically delete your password from the configuration afterwards for safety reasons.
+## Bridge Configuration
 
-3. Use external tools
+The `account` bridge requires an OAuth2 refresh token as the only parameter `refreshToken`.
 
 There are a few 3rd party tools available that have specialized on getting hold of refresh tokens for the Tesla API.
 Please note that we in general consider it dangerous to enter your credentials into some 3rd party app - you will have to trust the author not to send or store those credentials anywhere.
index c75597000468859d15d886eec176197bef27df20..43c72f58511f1a70d9c1888f73dd2a151c0379f8 100644 (file)
@@ -112,6 +112,4 @@ public class TeslaBindingConstants {
     public static final String CONFIG_ALLOWWAKEUPFORCOMMANDS = "allowWakeupForCommands";
     public static final String CONFIG_ENABLEEVENTS = "enableEvents";
     public static final String CONFIG_REFRESHTOKEN = "refreshToken";
-    public static final String CONFIG_USERNAME = "username";
-    public static final String CONFIG_PASSWORD = "password";
 }
diff --git a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/command/TeslaCommandExtension.java b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/command/TeslaCommandExtension.java
deleted file mode 100644 (file)
index 648fa2b..0000000
+++ /dev/null
@@ -1,128 +0,0 @@
-/**
- * Copyright (c) 2010-2022 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.tesla.internal.command;
-
-import java.io.BufferedReader;
-import java.io.InputStreamReader;
-import java.util.Arrays;
-import java.util.List;
-
-import javax.ws.rs.client.ClientBuilder;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.tesla.internal.TeslaBindingConstants;
-import org.openhab.binding.tesla.internal.discovery.TeslaAccountDiscoveryService;
-import org.openhab.binding.tesla.internal.handler.TeslaSSOHandler;
-import org.openhab.core.config.discovery.DiscoveryResult;
-import org.openhab.core.config.discovery.DiscoveryResultBuilder;
-import org.openhab.core.io.console.Console;
-import org.openhab.core.io.console.extensions.AbstractConsoleCommandExtension;
-import org.openhab.core.io.console.extensions.ConsoleCommandExtension;
-import org.openhab.core.io.net.http.HttpClientFactory;
-import org.openhab.core.thing.ThingUID;
-import org.openhab.core.util.UIDUtils;
-import org.osgi.service.component.annotations.Activate;
-import org.osgi.service.component.annotations.Component;
-import org.osgi.service.component.annotations.Reference;
-import org.osgi.service.component.annotations.ReferenceCardinality;
-
-/**
- * Console commands for interacting with the Tesla integration
- *
- * @author Nicolai Grødum - Initial contribution
- * @author Kai Kreuzer - refactored to use Tesla account thing
- */
-@NonNullByDefault
-@Component(service = ConsoleCommandExtension.class)
-public class TeslaCommandExtension extends AbstractConsoleCommandExtension {
-
-    private static final String CMD_LOGIN = "login";
-
-    @Reference(cardinality = ReferenceCardinality.OPTIONAL)
-    private @Nullable ClientBuilder injectedClientBuilder;
-
-    private final TeslaAccountDiscoveryService teslaAccountDiscoveryService;
-    private final HttpClientFactory httpClientFactory;
-
-    @Activate
-    public TeslaCommandExtension(@Reference TeslaAccountDiscoveryService teslaAccountDiscoveryService,
-            @Reference HttpClientFactory httpClientFactory) {
-        super("tesla", "Interact with the Tesla integration.");
-        this.teslaAccountDiscoveryService = teslaAccountDiscoveryService;
-        this.httpClientFactory = httpClientFactory;
-    }
-
-    @Override
-    public void execute(String[] args, Console console) {
-        if (args.length > 0) {
-            String subCommand = args[0];
-            switch (subCommand) {
-                case CMD_LOGIN:
-                    if (args.length == 1) {
-                        try {
-                            BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
-                            console.print("Username (email): ");
-                            String username = br.readLine();
-                            console.println(username);
-
-                            console.print("Password: ");
-
-                            String pwd = br.readLine();
-                            console.println("");
-                            console.println("Attempting login...");
-                            login(console, username, pwd);
-                        } catch (Exception e) {
-                            console.println(e.toString());
-                        }
-                    } else if (args.length == 3) {
-                        login(console, args[1], args[2]);
-                    } else {
-                        printUsage(console);
-                    }
-                    break;
-
-                default:
-                    console.println("Unknown command '" + subCommand + "'");
-                    printUsage(console);
-                    break;
-            }
-        } else {
-            printUsage(console);
-        }
-    }
-
-    @Override
-    public List<String> getUsages() {
-        return Arrays.asList(buildCommandUsage(CMD_LOGIN + " [<user email>] [<password>]",
-                "Authenticates the user and provides a refresh token."));
-    }
-
-    private void login(Console console, String username, String password) {
-        TeslaSSOHandler ssoHandler = new TeslaSSOHandler(httpClientFactory.getCommonHttpClient());
-
-        String refreshToken = ssoHandler.authenticate(username, password);
-        if (refreshToken != null) {
-            console.println("Refresh token: " + refreshToken);
-
-            ThingUID thingUID = new ThingUID(TeslaBindingConstants.THING_TYPE_ACCOUNT, UIDUtils.encode(username));
-            DiscoveryResult result = DiscoveryResultBuilder.create(thingUID).withLabel("Tesla Account")
-                    .withProperty(TeslaBindingConstants.CONFIG_REFRESHTOKEN, refreshToken)
-                    .withProperty(TeslaBindingConstants.CONFIG_USERNAME, username)
-                    .withRepresentationProperty(TeslaBindingConstants.CONFIG_USERNAME).build();
-            teslaAccountDiscoveryService.thingDiscovered(result);
-        } else {
-            console.println("Failed to retrieve refresh token");
-        }
-    }
-}
diff --git a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/discovery/TeslaAccountDiscoveryService.java b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/discovery/TeslaAccountDiscoveryService.java
deleted file mode 100644 (file)
index 1fc9e75..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-/**
- * Copyright (c) 2010-2022 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.tesla.internal.discovery;
-
-import java.util.Map;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.tesla.internal.TeslaHandlerFactory;
-import org.openhab.binding.tesla.internal.command.TeslaCommandExtension;
-import org.openhab.core.config.discovery.AbstractDiscoveryService;
-import org.openhab.core.config.discovery.DiscoveryResult;
-import org.openhab.core.config.discovery.DiscoveryService;
-import org.osgi.service.component.annotations.Component;
-
-/**
- * This is a discovery service, is used by the {@link TeslaCommandExtension} for
- * automatically creating Tesla accounts.
- *
- * @author Kai Kreuzer - Initial contribution
- *
- */
-@Component(service = { TeslaAccountDiscoveryService.class, DiscoveryService.class })
-@NonNullByDefault
-public class TeslaAccountDiscoveryService extends AbstractDiscoveryService {
-
-    public TeslaAccountDiscoveryService() throws IllegalArgumentException {
-        super(TeslaHandlerFactory.SUPPORTED_THING_TYPES_UIDS, 10, true);
-    }
-
-    @Override
-    protected void startScan() {
-    }
-
-    @Override
-    public void activate(@Nullable Map<String, Object> configProperties) {
-        super.activate(configProperties);
-    }
-
-    @Override
-    public void deactivate() {
-        super.deactivate();
-    }
-
-    @Override
-    public void thingDiscovered(DiscoveryResult discoveryResult) {
-        super.thingDiscovered(discoveryResult);
-    }
-}
index 8f870c42e11b9c0ffab895b3791ca29460d333ab..6ac8a1eb05c3ea7c3fc90e81a994d71d67ddb5e9 100644 (file)
@@ -14,12 +14,9 @@ package org.openhab.binding.tesla.internal.handler;
 
 import static org.openhab.binding.tesla.internal.TeslaBindingConstants.*;
 
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
 import java.time.Instant;
 import java.time.ZoneId;
 import java.time.format.DateTimeFormatter;
-import java.util.Base64;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
@@ -30,20 +27,15 @@ import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.ReentrantLock;
 
 import javax.ws.rs.client.Client;
-import javax.ws.rs.client.ClientRequestContext;
-import javax.ws.rs.client.ClientRequestFilter;
 import javax.ws.rs.client.Entity;
 import javax.ws.rs.client.WebTarget;
 import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
 
-import org.openhab.binding.tesla.internal.TeslaBindingConstants;
 import org.openhab.binding.tesla.internal.discovery.TeslaVehicleDiscoveryService;
 import org.openhab.binding.tesla.internal.protocol.Vehicle;
 import org.openhab.binding.tesla.internal.protocol.VehicleConfig;
 import org.openhab.binding.tesla.internal.protocol.sso.TokenResponse;
-import org.openhab.core.config.core.Configuration;
 import org.openhab.core.io.net.http.HttpClientFactory;
 import org.openhab.core.thing.Bridge;
 import org.openhab.core.thing.ChannelUID;
@@ -269,33 +261,11 @@ public class TeslaAccountHandler extends BaseBridgeHandler {
         }
 
         if (hasExpired) {
-            String username = (String) getConfig().get(CONFIG_USERNAME);
-            String password = (String) getConfig().get(CONFIG_PASSWORD);
             String refreshToken = (String) getConfig().get(CONFIG_REFRESHTOKEN);
 
             if (refreshToken == null || refreshToken.isEmpty()) {
-                if (username != null && !username.isEmpty() && password != null && !password.isEmpty()) {
-                    try {
-                        refreshToken = ssoHandler.authenticate(username, password);
-                    } catch (Exception e) {
-                        logger.error("An exception occurred while obtaining refresh token with username/password: '{}'",
-                                e.getMessage());
-                    }
-
-                    if (refreshToken != null) {
-                        // store refresh token from SSO endpoint in config, clear the password
-                        Configuration cfg = editConfiguration();
-                        cfg.put(TeslaBindingConstants.CONFIG_REFRESHTOKEN, refreshToken);
-                        cfg.remove(TeslaBindingConstants.CONFIG_PASSWORD);
-                        updateConfiguration(cfg);
-                    } else {
-                        return new ThingStatusInfo(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
-                                "Failed to obtain refresh token with username/password.");
-                    }
-                } else {
-                    return new ThingStatusInfo(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
-                            "Neither a refresh token nor credentials are provided.");
-                }
+                return new ThingStatusInfo(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
+                        "No refresh token is provided.");
             }
 
             this.logonToken = ssoHandler.getAccessToken(refreshToken);
@@ -432,28 +402,6 @@ public class TeslaAccountHandler extends BaseBridgeHandler {
         }
     };
 
-    public static class Authenticator implements ClientRequestFilter {
-        private final String user;
-        private final String password;
-
-        public Authenticator(String user, String password) {
-            this.user = user;
-            this.password = password;
-        }
-
-        @Override
-        public void filter(ClientRequestContext requestContext) throws IOException {
-            MultivaluedMap<String, Object> headers = requestContext.getHeaders();
-            final String basicAuthentication = getBasicAuthentication();
-            headers.add("Authorization", basicAuthentication);
-        }
-
-        private String getBasicAuthentication() {
-            String token = this.user + ":" + this.password;
-            return "Basic " + Base64.getEncoder().encodeToString(token.getBytes(StandardCharsets.UTF_8));
-        }
-    }
-
     protected class Request implements Runnable {
 
         private static final int NO_OF_RETRIES = 3;
index edae2f431bd03c32d0cc39af37c20e4abeaa8c33..be121492acfa774fcf4bd2ffb4aae01865f30b25 100644 (file)
@@ -14,11 +14,6 @@ package org.openhab.binding.tesla.internal.handler;
 
 import static org.openhab.binding.tesla.internal.TeslaBindingConstants.*;
 
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.Base64;
-import java.util.Iterator;
-import java.util.Random;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
@@ -27,17 +22,9 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
 import org.eclipse.jetty.client.HttpClient;
 import org.eclipse.jetty.client.api.ContentResponse;
-import org.eclipse.jetty.client.util.FormContentProvider;
 import org.eclipse.jetty.client.util.StringContentProvider;
 import org.eclipse.jetty.http.HttpHeader;
 import org.eclipse.jetty.http.HttpMethod;
-import org.eclipse.jetty.util.Fields;
-import org.eclipse.jetty.util.Fields.Field;
-import org.jsoup.Jsoup;
-import org.jsoup.nodes.Document;
-import org.jsoup.nodes.Element;
-import org.openhab.binding.tesla.internal.protocol.sso.AuthorizationCodeExchangeRequest;
-import org.openhab.binding.tesla.internal.protocol.sso.AuthorizationCodeExchangeResponse;
 import org.openhab.binding.tesla.internal.protocol.sso.RefreshTokenRequest;
 import org.openhab.binding.tesla.internal.protocol.sso.TokenResponse;
 import org.slf4j.Logger;
@@ -93,178 +80,6 @@ public class TeslaSSOHandler {
         return null;
     }
 
-    /**
-     * Authenticates using username/password against Tesla SSO endpoints.
-     *
-     * @param username Username
-     * @param password Password
-     * @return Refresh token for use with {@link getAccessToken}
-     */
-    @Nullable
-    public String authenticate(String username, String password) {
-        String codeVerifier = generateRandomString(86);
-        String codeChallenge = null;
-        String state = generateRandomString(10);
-
-        try {
-            codeChallenge = getCodeChallenge(codeVerifier);
-        } catch (NoSuchAlgorithmException e) {
-            logger.error("An exception occurred while building login page request: '{}'", e.getMessage());
-            return null;
-        }
-
-        final org.eclipse.jetty.client.api.Request loginPageRequest = httpClient
-                .newRequest(URI_SSO + "/" + PATH_AUTHORIZE);
-        loginPageRequest.method(HttpMethod.GET);
-        loginPageRequest.followRedirects(false);
-
-        addQueryParameters(loginPageRequest, codeChallenge, state);
-
-        ContentResponse loginPageResponse = executeHttpRequest(loginPageRequest);
-        if (loginPageResponse == null
-                || (loginPageResponse.getStatus() != 200 && loginPageResponse.getStatus() != 302)) {
-            logger.debug("Failed to obtain SSO login page, response status code: {}",
-                    (loginPageResponse != null ? loginPageResponse.getStatus() : "no response"));
-            return null;
-        }
-
-        logger.debug("Obtained SSO login page");
-
-        String authorizationCode = null;
-
-        if (loginPageResponse.getStatus() == 302) {
-            String redirectLocation = loginPageResponse.getHeaders().get(HttpHeader.LOCATION);
-            if (isValidRedirectLocation(redirectLocation)) {
-                authorizationCode = extractAuthorizationCodeFromUri(redirectLocation);
-            } else {
-                logger.debug("Unexpected redirect location received when fetching login page: {}", redirectLocation);
-                return null;
-            }
-        } else {
-            Fields postData = new Fields();
-
-            try {
-                Document doc = Jsoup.parse(loginPageResponse.getContentAsString());
-                logger.trace("{}", doc.toString());
-                Element loginForm = doc.getElementsByTag("form").first();
-
-                Iterator<Element> elIt = loginForm.getElementsByTag("input").iterator();
-                while (elIt.hasNext()) {
-                    Element input = elIt.next();
-                    if (input.attr("type").equalsIgnoreCase("hidden")) {
-                        postData.add(input.attr("name"), input.attr("value"));
-                    }
-                }
-            } catch (Exception e) {
-                logger.error("Failed to parse login page: {}", e.getMessage());
-                logger.debug("login page response {}", loginPageResponse.getContentAsString());
-                return null;
-            }
-
-            postData.add("identity", username);
-            postData.add("credential", password);
-
-            final org.eclipse.jetty.client.api.Request formSubmitRequest = httpClient
-                    .newRequest(URI_SSO + "/" + PATH_AUTHORIZE);
-            formSubmitRequest.method(HttpMethod.POST);
-            formSubmitRequest.content(new FormContentProvider(postData));
-            formSubmitRequest.followRedirects(false); // this should return a 302 ideally, but that location doesn't
-                                                      // exist
-            addQueryParameters(formSubmitRequest, codeChallenge, state);
-
-            ContentResponse formSubmitResponse = executeHttpRequest(formSubmitRequest);
-            if (formSubmitResponse == null || formSubmitResponse.getStatus() != 302) {
-                logger.debug("Failed to obtain code from SSO login page when submitting form, response status code: {}",
-                        (formSubmitResponse != null ? formSubmitResponse.getStatus() : "no response"));
-                return null;
-            }
-
-            String redirectLocation = formSubmitResponse.getHeaders().get(HttpHeader.LOCATION);
-            if (!isValidRedirectLocation(redirectLocation)) {
-                logger.debug("Redirect location not set or doesn't match expected callback URI {}: {}", URI_CALLBACK,
-                        redirectLocation);
-                return null;
-            }
-
-            logger.debug("Obtained valid redirect location");
-            authorizationCode = extractAuthorizationCodeFromUri(redirectLocation);
-        }
-
-        if (authorizationCode == null) {
-            logger.debug("Did not receive an authorization code");
-            return null;
-        }
-
-        // exchange authorization code for SSO access + refresh token
-        AuthorizationCodeExchangeRequest request = new AuthorizationCodeExchangeRequest(authorizationCode,
-                codeVerifier);
-        String payload = gson.toJson(request);
-
-        final org.eclipse.jetty.client.api.Request tokenExchangeRequest = httpClient
-                .newRequest(URI_SSO + "/" + PATH_TOKEN);
-        tokenExchangeRequest.content(new StringContentProvider(payload));
-        tokenExchangeRequest.header(HttpHeader.CONTENT_TYPE, "application/json");
-        tokenExchangeRequest.method(HttpMethod.POST);
-
-        ContentResponse response = executeHttpRequest(tokenExchangeRequest);
-        if (response != null && response.getStatus() == 200) {
-            String responsePayload = response.getContentAsString();
-            AuthorizationCodeExchangeResponse ssoTokenResponse = gson.fromJson(responsePayload.trim(),
-                    AuthorizationCodeExchangeResponse.class);
-            if (ssoTokenResponse != null && ssoTokenResponse.token_type != null
-                    && !ssoTokenResponse.access_token.isEmpty()) {
-                logger.debug("Obtained valid SSO refresh token");
-                return ssoTokenResponse.refresh_token;
-            }
-        } else {
-            logger.debug("An error occurred while exchanging authorization code for SSO refresh token: {}",
-                    (response != null ? response.getStatus() : "no response"));
-        }
-
-        return null;
-    }
-
-    private Boolean isValidRedirectLocation(@Nullable String redirectLocation) {
-        return redirectLocation != null && redirectLocation.startsWith(URI_CALLBACK);
-    }
-
-    @Nullable
-    private String extractAuthorizationCodeFromUri(String uri) {
-        Field code = httpClient.newRequest(uri).getParams().get("code");
-        return code != null ? code.getValue() : null;
-    }
-
-    private String getCodeChallenge(String codeVerifier) throws NoSuchAlgorithmException {
-        MessageDigest digest = MessageDigest.getInstance("SHA-256");
-        byte[] hash = digest.digest(codeVerifier.getBytes());
-
-        StringBuilder hashStr = new StringBuilder(hash.length * 2);
-        for (byte b : hash) {
-            hashStr.append(String.format("%02x", b));
-        }
-
-        return Base64.getUrlEncoder().encodeToString(hashStr.toString().getBytes());
-    }
-
-    private String generateRandomString(int length) {
-        Random random = new Random();
-
-        String generatedString = random.ints('a', 'z' + 1).limit(length)
-                .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append).toString();
-
-        return generatedString;
-    }
-
-    private void addQueryParameters(org.eclipse.jetty.client.api.Request request, String codeChallenge, String state) {
-        request.param("client_id", CLIENT_ID);
-        request.param("code_challenge", codeChallenge);
-        request.param("code_challenge_method", "S256");
-        request.param("redirect_uri", URI_CALLBACK);
-        request.param("response_type", "code");
-        request.param("scope", SSO_SCOPES);
-        request.param("state", state);
-    }
-
     @Nullable
     private ContentResponse executeHttpRequest(org.eclipse.jetty.client.api.Request request) {
         request.timeout(10, TimeUnit.SECONDS);
index e7c89ca0cfacb2a28ea98c72bd844c8097e7dc71..35a20a19cb20d5ddc103459c643db7fe6b8bf945 100644 (file)
@@ -14,14 +14,9 @@ package org.openhab.binding.tesla.internal.handler;
 
 import static org.openhab.binding.tesla.internal.TeslaBindingConstants.*;
 
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.text.SimpleDateFormat;
-import java.util.Arrays;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.Map;
@@ -29,7 +24,6 @@ import java.util.Set;
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.ReentrantLock;
-import java.util.stream.Collectors;
 
 import javax.measure.quantity.Temperature;
 import javax.ws.rs.ProcessingException;
@@ -41,10 +35,8 @@ import javax.ws.rs.core.Response;
 
 import org.eclipse.jdt.annotation.Nullable;
 import org.openhab.binding.tesla.internal.TeslaBindingConstants;
-import org.openhab.binding.tesla.internal.TeslaBindingConstants.EventKeys;
 import org.openhab.binding.tesla.internal.TeslaChannelSelectorProxy;
 import org.openhab.binding.tesla.internal.TeslaChannelSelectorProxy.TeslaChannelSelector;
-import org.openhab.binding.tesla.internal.handler.TeslaAccountHandler.Authenticator;
 import org.openhab.binding.tesla.internal.handler.TeslaAccountHandler.Request;
 import org.openhab.binding.tesla.internal.protocol.ChargeState;
 import org.openhab.binding.tesla.internal.protocol.ClimateState;
@@ -69,7 +61,6 @@ import org.openhab.core.thing.ThingStatusDetail;
 import org.openhab.core.thing.binding.BaseThingHandler;
 import org.openhab.core.types.Command;
 import org.openhab.core.types.RefreshType;
-import org.openhab.core.types.State;
 import org.openhab.core.types.UnDefType;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -88,13 +79,8 @@ import com.google.gson.JsonParser;
  */
 public class TeslaVehicleHandler extends BaseThingHandler {
 
-    private static final int EVENT_STREAM_PAUSE = 5000;
-    private static final int EVENT_TIMESTAMP_AGE_LIMIT = 3000;
-    private static final int EVENT_TIMESTAMP_MAX_DELTA = 10000;
     private static final int FAST_STATUS_REFRESH_INTERVAL = 15000;
     private static final int SLOW_STATUS_REFRESH_INTERVAL = 60000;
-    private static final int EVENT_MAXIMUM_ERRORS_IN_INTERVAL = 10;
-    private static final int EVENT_ERROR_INTERVAL_SECONDS = 15;
     private static final int API_SLEEP_INTERVAL_MINUTES = 20;
     private static final int MOVE_THRESHOLD_INTERVAL_MINUTES = 5;
 
@@ -113,7 +99,6 @@ public class TeslaVehicleHandler extends BaseThingHandler {
 
     protected boolean allowWakeUp;
     protected boolean allowWakeUpForCommands;
-    protected boolean enableEvents = false;
     protected long lastTimeStamp;
     protected long apiIntervalTimestamp;
     protected int apiIntervalErrors;
@@ -184,13 +169,6 @@ public class TeslaVehicleHandler extends BaseThingHandler {
         } finally {
             lock.unlock();
         }
-
-        if (enableEvents) {
-            if (eventThread == null) {
-                eventThread = new Thread(eventRunnable, "openHAB-Tesla-Events-" + getThing().getUID());
-                eventThread.start();
-            }
-        }
     }
 
     @Override
@@ -1015,173 +993,4 @@ public class TeslaVehicleHandler extends BaseThingHandler {
             }
         }
     };
-
-    protected Runnable eventRunnable = new Runnable() {
-        Response eventResponse;
-        BufferedReader eventBufferedReader;
-        InputStreamReader eventInputStreamReader;
-        boolean isEstablished = false;
-
-        protected boolean establishEventStream() {
-            try {
-                if (!isEstablished) {
-                    eventBufferedReader = null;
-
-                    eventClient = clientBuilder.build()
-                            .register(new Authenticator((String) getConfig().get(CONFIG_USERNAME), vehicle.tokens[0]));
-                    eventTarget = eventClient.target(URI_EVENT).path(vehicle.vehicle_id + "/").queryParam("values",
-                            Arrays.asList(EventKeys.values()).stream().skip(1).map(Enum::toString)
-                                    .collect(Collectors.joining(",")));
-                    eventResponse = eventTarget.request(MediaType.TEXT_PLAIN_TYPE).get();
-
-                    logger.debug("Event Stream: Establishing the event stream: Response: {}:{}",
-                            eventResponse.getStatus(), eventResponse.getStatusInfo());
-
-                    if (eventResponse.getStatus() == 200) {
-                        InputStream dummy = (InputStream) eventResponse.getEntity();
-                        eventInputStreamReader = new InputStreamReader(dummy);
-                        eventBufferedReader = new BufferedReader(eventInputStreamReader);
-                        isEstablished = true;
-                    } else if (eventResponse.getStatus() == 401) {
-                        isEstablished = false;
-                    } else {
-                        isEstablished = false;
-                    }
-
-                    if (!isEstablished) {
-                        eventIntervalErrors++;
-                        if (eventIntervalErrors >= EVENT_MAXIMUM_ERRORS_IN_INTERVAL) {
-                            logger.warn(
-                                    "Reached the maximum number of errors ({}) for the current interval ({} seconds)",
-                                    EVENT_MAXIMUM_ERRORS_IN_INTERVAL, EVENT_ERROR_INTERVAL_SECONDS);
-                            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
-                            eventClient.close();
-                        }
-
-                        if ((System.currentTimeMillis() - eventIntervalTimestamp) > 1000
-                                * EVENT_ERROR_INTERVAL_SECONDS) {
-                            logger.trace("Resetting the error counter. ({} errors in the last interval)",
-                                    eventIntervalErrors);
-                            eventIntervalTimestamp = System.currentTimeMillis();
-                            eventIntervalErrors = 0;
-                        }
-                    }
-                }
-            } catch (Exception e) {
-                logger.error(
-                        "Event stream: An exception occurred while establishing the event stream for the vehicle: '{}'",
-                        e.getMessage());
-                isEstablished = false;
-            }
-
-            return isEstablished;
-        }
-
-        @Override
-        public void run() {
-            while (true) {
-                try {
-                    if (getThing().getStatus() == ThingStatus.ONLINE) {
-                        if (isAwake()) {
-                            if (establishEventStream()) {
-                                String line = eventBufferedReader.readLine();
-
-                                while (line != null) {
-                                    logger.debug("Event stream: Received an event: '{}'", line);
-                                    String vals[] = line.split(",");
-                                    long currentTimeStamp = Long.valueOf(vals[0]);
-                                    long systemTimeStamp = System.currentTimeMillis();
-                                    if (logger.isDebugEnabled()) {
-                                        SimpleDateFormat dateFormatter = new SimpleDateFormat(
-                                                "yyyy-MM-dd'T'HH:mm:ss.SSS");
-                                        logger.debug("STS {} CTS {} Delta {}",
-                                                dateFormatter.format(new Date(systemTimeStamp)),
-                                                dateFormatter.format(new Date(currentTimeStamp)),
-                                                systemTimeStamp - currentTimeStamp);
-                                    }
-                                    if (systemTimeStamp - currentTimeStamp < EVENT_TIMESTAMP_AGE_LIMIT) {
-                                        if (currentTimeStamp > lastTimeStamp) {
-                                            lastTimeStamp = Long.valueOf(vals[0]);
-                                            if (logger.isDebugEnabled()) {
-                                                SimpleDateFormat dateFormatter = new SimpleDateFormat(
-                                                        "yyyy-MM-dd'T'HH:mm:ss.SSS");
-                                                logger.debug("Event Stream: Event stamp is {}",
-                                                        dateFormatter.format(new Date(lastTimeStamp)));
-                                            }
-                                            for (int i = 0; i < EventKeys.values().length; i++) {
-                                                TeslaChannelSelector selector = TeslaChannelSelector
-                                                        .getValueSelectorFromRESTID((EventKeys.values()[i]).toString());
-                                                if (!selector.isProperty()) {
-                                                    State newState = teslaChannelSelectorProxy.getState(vals[i],
-                                                            selector, editProperties());
-                                                    if (newState != null && !"".equals(vals[i])) {
-                                                        updateState(selector.getChannelID(), newState);
-                                                    } else {
-                                                        updateState(selector.getChannelID(), UnDefType.UNDEF);
-                                                    }
-                                                } else {
-                                                    Map<String, String> properties = editProperties();
-                                                    properties.put(selector.getChannelID(),
-                                                            (selector.getState(vals[i])).toString());
-                                                    updateProperties(properties);
-                                                }
-                                            }
-                                        } else {
-                                            if (logger.isDebugEnabled()) {
-                                                SimpleDateFormat dateFormatter = new SimpleDateFormat(
-                                                        "yyyy-MM-dd'T'HH:mm:ss.SSS");
-                                                logger.debug(
-                                                        "Event stream: Discarding an event with an out of sync timestamp {} (last is {})",
-                                                        dateFormatter.format(new Date(currentTimeStamp)),
-                                                        dateFormatter.format(new Date(lastTimeStamp)));
-                                            }
-                                        }
-                                    } else {
-                                        if (logger.isDebugEnabled()) {
-                                            SimpleDateFormat dateFormatter = new SimpleDateFormat(
-                                                    "yyyy-MM-dd'T'HH:mm:ss.SSS");
-                                            logger.debug(
-                                                    "Event Stream: Discarding an event that differs {} ms from the system time: {} (system is {})",
-                                                    systemTimeStamp - currentTimeStamp,
-                                                    dateFormatter.format(currentTimeStamp),
-                                                    dateFormatter.format(systemTimeStamp));
-                                        }
-                                        if (systemTimeStamp - currentTimeStamp > EVENT_TIMESTAMP_MAX_DELTA) {
-                                            logger.trace("Event stream: The event stream will be reset");
-                                            isEstablished = false;
-                                        }
-                                    }
-                                    line = eventBufferedReader.readLine();
-                                }
-                                logger.trace("Event stream: The end of stream was reached");
-                                isEstablished = false;
-                            }
-                        } else {
-                            logger.debug("Event stream: The vehicle is not awake");
-                            if (vehicle != null) {
-                                if (allowWakeUp) {
-                                    // wake up the vehicle until streaming token <> 0
-                                    logger.debug("Event stream: Waking up the vehicle");
-                                    wakeUp();
-                                }
-                            } else {
-                                vehicle = queryVehicle();
-                            }
-                            Thread.sleep(EVENT_STREAM_PAUSE);
-                        }
-                    }
-                } catch (IOException | NumberFormatException e) {
-                    logger.debug("Event stream: An exception occurred while reading events: '{}'", e.getMessage());
-                    isEstablished = false;
-                } catch (InterruptedException e) {
-                    isEstablished = false;
-                }
-
-                if (Thread.interrupted()) {
-                    logger.debug("Event stream: the event stream was interrupted");
-                    return;
-                }
-            }
-        }
-    };
 }
index 93c8924b60b1beb3f208ea18f943a59992f9a023..7db32d0e524b23441024e72c9faedfab0a6b4d2c 100644 (file)
@@ -16,7 +16,7 @@ package org.openhab.binding.tesla.internal.protocol.sso;
  * The {@link TokenResponse} is a datastructure to capture
  * authentication response from Tesla Remote Service
  *
- * @author Nicolai Grødum
+ * @author Nicolai Grødum - Initial contribution
  */
 public class TokenResponse {
 
index aa6777427d0e46aeba392edaab378aaf87bb9037..2d33187b2c450ef9e7e835bc6d620a270318cab7 100644 (file)
@@ -18,12 +18,8 @@ thing-type.tesla.modely.description = A Tesla Model Y Vehicle
 
 # thing types config
 
-thing-type.config.tesla.account.password.label = Password
-thing-type.config.tesla.account.password.description = Password for the Tesla Remote Service
 thing-type.config.tesla.account.refreshToken.label = Refresh Token
-thing-type.config.tesla.account.refreshToken.description = Refresh token for account authentication. Use "openhab:tesla" to retrieve it.
-thing-type.config.tesla.account.username.label = Username
-thing-type.config.tesla.account.username.description = Username for the Tesla Remote Service, e.g email address.
+thing-type.config.tesla.account.refreshToken.description = Refresh token for account authentication. Please see the documentation on how to get hold of it.
 thing-type.config.tesla.model3.allowWakeup.label = Allow Wake-Up
 thing-type.config.tesla.model3.allowWakeup.description = Allows waking up the vehicle. Caution: This can result in huge vampire drain!
 thing-type.config.tesla.model3.allowWakeupForCommands.label = Allow Wake-Up For Commands
index 999eeb72275408b4f1705df38b98e23785c250ca..75038c75367249bc4b32d90ebe3e273a94c283b4 100644 (file)
                <config-description>
                        <parameter name="refreshToken" type="text" required="false">
                                <label>Refresh Token</label>
-                               <description>Refresh token for account authentication. Use "openhab:tesla" to retrieve it.</description>
-                       </parameter>
-                       <parameter name="username" type="text" required="false">
-                               <advanced>true</advanced>
-                               <label>Username</label>
-                               <description>Username for the Tesla Remote Service, e.g email address.</description>
-                       </parameter>
-                       <parameter name="password" type="text" required="false">
-                               <advanced>true</advanced>
-                               <context>password</context>
-                               <label>Password</label>
-                               <description>Password for the Tesla Remote Service</description>
+                               <description>Refresh token for account authentication. Please see the documentation on how to get hold of it.</description>
                        </parameter>
                </config-description>
        </bridge-type>