From f39415b631419541a7ab392727d8238dde103100 Mon Sep 17 00:00:00 2001 From: lsiepel Date: Sun, 8 Dec 2024 14:35:41 +0100 Subject: [PATCH] [tesla] Add null annotations (#17582) Signed-off-by: Leo Siepel --- .../internal/TeslaChannelSelectorProxy.java | 20 +- .../TeslaVehicleDiscoveryService.java | 14 +- .../internal/handler/TeslaAccountHandler.java | 108 +++---- .../internal/handler/TeslaEventEndpoint.java | 50 ++-- .../internal/handler/TeslaSSOHandler.java | 12 +- .../internal/handler/TeslaVehicleHandler.java | 276 ++++++++++-------- .../internal/handler/VehicleListener.java | 4 +- .../tesla/internal/protocol/ChargeState.java | 69 ----- .../tesla/internal/protocol/ClimateState.java | 53 ---- .../tesla/internal/protocol/DriveState.java | 42 --- .../tesla/internal/protocol/GUIState.java | 31 -- .../tesla/internal/protocol/Vehicle.java | 39 --- .../internal/protocol/VehicleConfig.java | 63 ---- .../tesla/internal/protocol/VehicleData.java | 46 --- .../tesla/internal/protocol/VehicleState.java | 64 ---- .../internal/protocol/dto/ChargeState.java | 115 ++++++++ .../internal/protocol/dto/ClimateState.java | 92 ++++++ .../internal/protocol/dto/DriveState.java | 52 ++++ .../internal/protocol/{ => dto}/Event.java | 10 +- .../tesla/internal/protocol/dto/GUIState.java | 43 +++ .../protocol/{ => dto}/SoftwareUpdate.java | 13 +- .../tesla/internal/protocol/dto/Vehicle.java | 49 ++++ .../internal/protocol/dto/VehicleConfig.java | 92 ++++++ .../internal/protocol/dto/VehicleData.java | 72 +++++ .../internal/protocol/dto/VehicleState.java | 94 ++++++ .../sso/AuthorizationCodeExchangeRequest.java | 18 +- .../AuthorizationCodeExchangeResponse.java | 16 +- .../{ => dto}/sso/RefreshTokenRequest.java | 17 +- .../{ => dto}/sso/TokenExchangeRequest.java | 13 +- .../protocol/{ => dto}/sso/TokenResponse.java | 20 +- .../throttler/AbstractChannelThrottler.java | 6 +- .../AbstractMultiRateChannelThrottler.java | 6 +- .../internal/throttler/ChannelThrottler.java | 6 + .../throttler/QueueChannelThrottler.java | 7 +- .../tesla/internal/throttler/Rate.java | 3 + .../throttler/ScheduledChannelThrottler.java | 8 +- .../internal/throttler/TimeProvider.java | 3 + 37 files changed, 981 insertions(+), 665 deletions(-) delete mode 100644 bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/ChargeState.java delete mode 100644 bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/ClimateState.java delete mode 100644 bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/DriveState.java delete mode 100644 bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/GUIState.java delete mode 100644 bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/Vehicle.java delete mode 100644 bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/VehicleConfig.java delete mode 100644 bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/VehicleData.java delete mode 100644 bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/VehicleState.java create mode 100644 bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/ChargeState.java create mode 100644 bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/ClimateState.java create mode 100644 bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/DriveState.java rename bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/{ => dto}/Event.java (73%) create mode 100644 bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/GUIState.java rename bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/{ => dto}/SoftwareUpdate.java (67%) create mode 100644 bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/Vehicle.java create mode 100644 bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/VehicleConfig.java create mode 100644 bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/VehicleData.java create mode 100644 bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/VehicleState.java rename bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/{ => dto}/sso/AuthorizationCodeExchangeRequest.java (67%) rename bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/{ => dto}/sso/AuthorizationCodeExchangeResponse.java (63%) rename bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/{ => dto}/sso/RefreshTokenRequest.java (62%) rename bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/{ => dto}/sso/TokenExchangeRequest.java (55%) rename bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/{ => dto}/sso/TokenResponse.java (58%) diff --git a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/TeslaChannelSelectorProxy.java b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/TeslaChannelSelectorProxy.java index 8bcef86df0539..aab9c92498ca8 100644 --- a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/TeslaChannelSelectorProxy.java +++ b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/TeslaChannelSelectorProxy.java @@ -19,6 +19,8 @@ import java.util.Date; import java.util.Map; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.library.types.DateTimeType; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.OnOffType; @@ -33,6 +35,7 @@ import org.openhab.core.library.unit.Units; import org.openhab.core.types.State; import org.openhab.core.types.Type; +import org.openhab.core.types.UnDefType; /** * The {@link TeslaChannelSelectorProxy} class is a helper class to instantiate @@ -40,6 +43,7 @@ * * @author Karel Goderis - Initial contribution */ +@NonNullByDefault public class TeslaChannelSelectorProxy { public enum TeslaChannelSelector { @@ -939,11 +943,11 @@ public State getState(String s, TeslaChannelSelectorProxy proxy, Map properties) { State someState = super.getState(s); - if (someState != null) { + if (someState != UnDefType.UNDEF) { BigDecimal value = ((DecimalType) someState).toBigDecimal(); return new QuantityType<>(value, ImperialUnits.MILES_PER_HOUR); } else { - return null; + return UnDefType.UNDEF; } } }, @@ -1062,12 +1066,12 @@ public State getState(String s, TeslaChannelSelectorProxy proxy, Map typeClass; private final boolean isProperty; - private TeslaChannelSelector(String restID, String channelID, Class typeClass, + private TeslaChannelSelector(@Nullable String restID, String channelID, Class typeClass, boolean isProperty) { this.restID = restID; this.channelID = channelID; @@ -1077,7 +1081,8 @@ private TeslaChannelSelector(String restID, String channelID, Class +public class TeslaVehicleDiscoveryService extends AbstractThingHandlerDiscoveryService implements VehicleListener { private final Logger logger = LoggerFactory.getLogger(TeslaVehicleDiscoveryService.class); @@ -63,13 +65,13 @@ public void dispose() { } @Override - public void vehicleFound(Vehicle vehicle, VehicleConfig vehicleConfig) { + public void vehicleFound(Vehicle vehicle, @Nullable VehicleConfig vehicleConfig) { ThingTypeUID type = vehicleConfig == null ? TeslaBindingConstants.THING_TYPE_VEHICLE : vehicleConfig.identifyModel(); if (type != null) { logger.debug("Found a {} vehicle", type.getId()); ThingUID thingUID = new ThingUID(type, thingHandler.getThing().getUID(), vehicle.vin); - DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withLabel(vehicle.display_name) + DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withLabel(vehicle.displayName) .withBridge(thingHandler.getThing().getUID()).withProperty(TeslaBindingConstants.VIN, vehicle.vin) .build(); thingDiscovered(discoveryResult); diff --git a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/handler/TeslaAccountHandler.java b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/handler/TeslaAccountHandler.java index 59e92f8c61cf8..898339f9940a6 100644 --- a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/handler/TeslaAccountHandler.java +++ b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/handler/TeslaAccountHandler.java @@ -31,12 +31,14 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +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.TeslaVehicleDiscoveryService; -import org.openhab.binding.tesla.internal.protocol.Vehicle; -import org.openhab.binding.tesla.internal.protocol.VehicleConfig; -import org.openhab.binding.tesla.internal.protocol.VehicleData; -import org.openhab.binding.tesla.internal.protocol.sso.TokenResponse; +import org.openhab.binding.tesla.internal.protocol.dto.Vehicle; +import org.openhab.binding.tesla.internal.protocol.dto.VehicleConfig; +import org.openhab.binding.tesla.internal.protocol.dto.VehicleData; +import org.openhab.binding.tesla.internal.protocol.dto.sso.TokenResponse; import org.openhab.core.io.net.http.HttpClientFactory; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.ChannelUID; @@ -63,6 +65,7 @@ * @author Nicolai Grødum - Adding token based auth * @author Kai Kreuzer - refactored to use separate vehicle handlers */ +@NonNullByDefault public class TeslaAccountHandler extends BaseBridgeHandler { public static final int API_MAXIMUM_ERRORS_IN_INTERVAL = 3; @@ -86,6 +89,7 @@ public class TeslaAccountHandler extends BaseBridgeHandler { private final ThingTypeMigrationService thingTypeMigrationService; // Threading and Job related variables + @Nullable protected ScheduledFuture connectJob; protected long lastTimeStamp; @@ -93,10 +97,12 @@ public class TeslaAccountHandler extends BaseBridgeHandler { protected int apiIntervalErrors; protected long eventIntervalTimestamp; protected int eventIntervalErrors; - protected ReentrantLock lock; + + protected ReentrantLock lock = new ReentrantLock(); private final Gson gson = new Gson(); + @Nullable private TokenResponse logonToken; private final Set vehicleListeners = new HashSet<>(); @@ -122,31 +128,17 @@ public void initialize() { updateStatus(ThingStatus.UNKNOWN); - lock = new ReentrantLock(); - lock.lock(); - - try { - if (connectJob == null || connectJob.isCancelled()) { - connectJob = scheduler.scheduleWithFixedDelay(connectRunnable, 0, CONNECT_RETRY_INTERVAL, - TimeUnit.MILLISECONDS); - } - } finally { - lock.unlock(); - } + connectJob = scheduler.scheduleWithFixedDelay(connectRunnable, 0, CONNECT_RETRY_INTERVAL, + TimeUnit.MILLISECONDS); } @Override public void dispose() { logger.debug("Disposing the Tesla account handler for {}", getThing().getUID()); - - lock.lock(); - try { - if (connectJob != null && !connectJob.isCancelled()) { - connectJob.cancel(true); - connectJob = null; - } - } finally { - lock.unlock(); + ScheduledFuture connectJob = this.connectJob; + if (connectJob != null && !connectJob.isCancelled()) { + connectJob.cancel(true); + this.connectJob = null; } } @@ -167,19 +159,25 @@ public void handleCommand(ChannelUID channelUID, Command command) { // we do not have any channels -> nothing to do here } - public String getAuthHeader() { - if (logonToken != null) { - return "Bearer " + logonToken.access_token; + public @Nullable String getAuthHeader() { + String accessToken = getAccessToken(); + if (accessToken != null) { + return "Bearer " + accessToken; } else { return null; } } - public String getAccessToken() { - return logonToken.access_token; + public @Nullable String getAccessToken() { + TokenResponse logonToken = this.logonToken; + if (logonToken != null) { + return logonToken.accessToken; + } else { + return null; + } } - protected boolean checkResponse(Response response, boolean immediatelyFail) { + protected boolean checkResponse(@Nullable Response response, boolean immediatelyFail) { if (response != null && response.getStatus() == 200) { return true; } else if (response != null && response.getStatus() == 401) { @@ -221,17 +219,23 @@ protected Vehicle[] queryVehicles() { if (!checkResponse(response, true)) { logger.debug("An error occurred while querying the vehicle"); - return null; + return new Vehicle[0]; } JsonObject jsonObject = JsonParser.parseString(response.readEntity(String.class)).getAsJsonObject(); Vehicle[] vehicleArray = gson.fromJson(jsonObject.getAsJsonArray("response"), Vehicle[].class); - + if (vehicleArray == null) { + logger.debug("Response resulted in unexpected null array"); + return new Vehicle[0]; + } for (Vehicle vehicle : vehicleArray) { String responseString = invokeAndParse(vehicle.id, null, null, dataRequestTarget, 0); VehicleConfig vehicleConfig = null; if (responseString != null && !responseString.isBlank()) { - vehicleConfig = gson.fromJson(responseString, VehicleData.class).vehicle_config; + VehicleData vehicleData = gson.fromJson(responseString, VehicleData.class); + if (vehicleData != null) { + vehicleConfig = vehicleData.vehicleConfig; + } } for (VehicleListener listener : vehicleListeners) { listener.vehicleFound(vehicle, vehicleConfig); @@ -251,7 +255,7 @@ protected Vehicle[] queryVehicles() { logger.debug("Querying the vehicle: VIN {}", vehicle.vin); String vehicleJSON = gson.toJson(vehicle); vehicleHandler.parseAndUpdate("queryVehicle", null, vehicleJSON); - logger.trace("Vehicle is id {}/vehicle_id {}/tokens {}", vehicle.id, vehicle.vehicle_id, + logger.trace("Vehicle is id {}/vehicle_id {}/tokens {}", vehicle.id, vehicle.vehicleId, vehicle.tokens); } } @@ -274,8 +278,8 @@ ThingStatusInfo authenticate() { logger.debug("Current authentication time {}", DATE_FORMATTER.format(Instant.now())); if (token != null) { - Instant tokenCreationInstant = Instant.ofEpochMilli(token.created_at * 1000); - Instant tokenExpiresInstant = Instant.ofEpochMilli((token.created_at + token.expires_in) * 1000); + Instant tokenCreationInstant = Instant.ofEpochMilli(token.createdAt * 1000); + Instant tokenExpiresInstant = Instant.ofEpochMilli((token.createdAt + token.expiresIn) * 1000); logger.debug("Found a request token from {}", DATE_FORMATTER.format(tokenCreationInstant)); logger.debug("Access token expiration time {}", DATE_FORMATTER.format(tokenExpiresInstant)); @@ -306,8 +310,8 @@ ThingStatusInfo authenticate() { return new ThingStatusInfo(ThingStatus.ONLINE, ThingStatusDetail.NONE, null); } - protected String invokeAndParse(String vehicleId, String command, String payLoad, WebTarget target, - int noOfretries) { + protected @Nullable String invokeAndParse(@Nullable String vehicleId, @Nullable String command, + @Nullable String payLoad, WebTarget target, int noOfretries) { logger.debug("Invoking: {}", command); if (vehicleId != null) { @@ -316,26 +320,29 @@ protected String invokeAndParse(String vehicleId, String command, String payLoad if (payLoad != null) { if (command != null) { response = target.resolveTemplate("cmd", command).resolveTemplate("vid", vehicleId).request() - .header("Authorization", "Bearer " + logonToken.access_token) + .header("Authorization", getAuthHeader()) .post(Entity.entity(payLoad, MediaType.APPLICATION_JSON_TYPE)); } else { response = target.resolveTemplate("vid", vehicleId).request() - .header("Authorization", "Bearer " + logonToken.access_token) + .header("Authorization", getAuthHeader()) .post(Entity.entity(payLoad, MediaType.APPLICATION_JSON_TYPE)); } } else if (command != null) { response = target.resolveTemplate("cmd", command).resolveTemplate("vid", vehicleId) - .request(MediaType.APPLICATION_JSON_TYPE) - .header("Authorization", "Bearer " + logonToken.access_token).get(); + .request(MediaType.APPLICATION_JSON_TYPE).header("Authorization", getAuthHeader()).get(); } else { response = target.resolveTemplate("vid", vehicleId).request(MediaType.APPLICATION_JSON_TYPE) - .header("Authorization", "Bearer " + logonToken.access_token).get(); + .header("Authorization", getAuthHeader()).get(); } if (!checkResponse(response, false)) { + if (response == null) { + logger.debug( + "An error occurred while communicating with the vehicle during request, the response was null"); + return null; + } logger.debug("An error occurred while communicating with the vehicle during request {}: {}: {}", - command, (response != null) ? response.getStatus() : "", - (response != null) ? response.getStatusInfo().getReasonPhrase() : "No Response"); + command, response.getStatus(), response.getStatusInfo().getReasonPhrase()); if (response.getStatus() == 408 && noOfretries > 0) { try { // we give the vehicle a moment to wake up and try the request again @@ -377,7 +384,7 @@ protected String invokeAndParse(String vehicleId, String command, String payLoad if (authenticationResult.getStatus() == ThingStatus.ONLINE) { // get a list of vehicles Response response = productsTarget.request(MediaType.APPLICATION_JSON_TYPE) - .header("Authorization", "Bearer " + logonToken.access_token).get(); + .header("Authorization", getAuthHeader()).get(); if (response != null && response.getStatus() == 200 && response.hasEntity()) { updateStatus(ThingStatus.ONLINE); @@ -436,11 +443,12 @@ protected class Request implements Runnable { private TeslaVehicleHandler handler; private String request; + @Nullable private String payLoad; private WebTarget target; private boolean allowWakeUpForCommands; - public Request(TeslaVehicleHandler handler, String request, String payLoad, WebTarget target, + public Request(TeslaVehicleHandler handler, String request, @Nullable String payLoad, WebTarget target, boolean allowWakeUpForCommands) { this.handler = handler; this.request = request; @@ -467,8 +475,8 @@ public void run() { } } - public Request newRequest(TeslaVehicleHandler teslaVehicleHandler, String command, String payLoad, WebTarget target, - boolean allowWakeUpForCommands) { + public Request newRequest(TeslaVehicleHandler teslaVehicleHandler, String command, @Nullable String payLoad, + WebTarget target, boolean allowWakeUpForCommands) { return new Request(teslaVehicleHandler, command, payLoad, target, allowWakeUpForCommands); } diff --git a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/handler/TeslaEventEndpoint.java b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/handler/TeslaEventEndpoint.java index 54fe6ac721bda..d026f7ea92c9b 100644 --- a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/handler/TeslaEventEndpoint.java +++ b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/handler/TeslaEventEndpoint.java @@ -29,7 +29,7 @@ import org.eclipse.jetty.websocket.api.WebSocketListener; import org.eclipse.jetty.websocket.api.WebSocketPingPongListener; import org.eclipse.jetty.websocket.client.WebSocketClient; -import org.openhab.binding.tesla.internal.protocol.Event; +import org.openhab.binding.tesla.internal.protocol.dto.Event; import org.openhab.core.io.net.http.WebSocketFactory; import org.openhab.core.thing.ThingUID; import org.openhab.core.thing.util.ThingWebClientUtil; @@ -54,25 +54,20 @@ public class TeslaEventEndpoint implements WebSocketListener, WebSocketPingPongL private final Logger logger = LoggerFactory.getLogger(TeslaEventEndpoint.class); private String endpointId; - protected WebSocketFactory webSocketFactory; private WebSocketClient client; private ConnectionState connectionState = ConnectionState.CLOSED; private @Nullable Session session; - private EventHandler eventHandler; + private @Nullable EventHandler eventHandler; private final Gson gson = new Gson(); public TeslaEventEndpoint(ThingUID uid, WebSocketFactory webSocketFactory) { - try { - this.endpointId = "TeslaEventEndpoint-" + uid.getAsString(); + this.endpointId = "TeslaEventEndpoint-" + uid.getAsString(); - String name = ThingWebClientUtil.buildWebClientConsumerName(uid, null); - client = webSocketFactory.createWebSocketClient(name); - this.client.setConnectTimeout(TIMEOUT_MILLISECONDS); - this.client.setMaxIdleTimeout(IDLE_TIMEOUT_MILLISECONDS); - } catch (Exception e) { - throw new RuntimeException(e); - } + String name = ThingWebClientUtil.buildWebClientConsumerName(uid, null); + client = webSocketFactory.createWebSocketClient(name); + this.client.setConnectTimeout(TIMEOUT_MILLISECONDS); + this.client.setMaxIdleTimeout(IDLE_TIMEOUT_MILLISECONDS); } public void close() { @@ -117,19 +112,22 @@ public void connect(URI endpointURI) { } @Override - public void onWebSocketConnect(Session session) { - logger.debug("{} : Connected to {} with hash {}", endpointId, session.getRemoteAddress().getAddress(), - session.hashCode()); + public void onWebSocketConnect(@Nullable Session session) { + logger.debug("{} : Connected to {} with hash {}", endpointId, + (session != null) ? session.getRemoteAddress().getAddress() : "Unknown", + (session != null) ? session.hashCode() : -1); connectionState = ConnectionState.CONNECTED; this.session = session; } public void closeConnection() { + Session session = this.session; try { connectionState = ConnectionState.CLOSING; if (session != null && session.isOpen()) { logger.debug("{} : Closing the session", endpointId); session.close(StatusCode.NORMAL, "bye"); + this.session = session; } } catch (Exception e) { logger.error("{} : An exception occurred while closing the session : {}", endpointId, e.getMessage()); @@ -138,14 +136,14 @@ public void closeConnection() { } @Override - public void onWebSocketClose(int statusCode, String reason) { + public void onWebSocketClose(int statusCode, @Nullable String reason) { logger.debug("{} : Closed the session with status {} for reason {}", endpointId, statusCode, reason); connectionState = ConnectionState.CLOSED; this.session = null; } @Override - public void onWebSocketText(String message) { + public void onWebSocketText(@Nullable String message) { // NoOp } @@ -158,10 +156,13 @@ public void onWebSocketBinary(byte[] payload, int offset, int length) { try { while ((str = in.readLine()) != null) { logger.trace("{} : Received raw data '{}'", endpointId, str); - if (this.eventHandler != null) { + EventHandler eventHandler = this.eventHandler; + if (eventHandler != null) { try { Event event = gson.fromJson(str, Event.class); - this.eventHandler.handleEvent(event); + if (event != null) { + eventHandler.handleEvent(event); + } } catch (RuntimeException e) { logger.error("{} : An exception occurred while processing raw data : {}", endpointId, e.getMessage()); @@ -176,12 +177,14 @@ public void onWebSocketBinary(byte[] payload, int offset, int length) { @Override public void onWebSocketError(Throwable cause) { logger.error("{} : An error occurred in the session : {}", endpointId, cause.getMessage()); + Session session = this.session; if (session != null && session.isOpen()) { session.close(StatusCode.ABNORMAL, "Session Error"); } } public void sendMessage(String message) throws IOException { + Session session = this.session; try { if (session != null) { logger.debug("{} : Sending raw data '{}'", endpointId, message); @@ -198,6 +201,7 @@ public void sendMessage(String message) throws IOException { } public void ping() { + Session session = this.session; try { if (session != null) { ByteBuffer buffer = ByteBuffer.allocate(8).putLong(System.nanoTime()).flip(); @@ -209,8 +213,9 @@ public void ping() { } @Override - public void onWebSocketPing(ByteBuffer payload) { + public void onWebSocketPing(@Nullable ByteBuffer payload) { ByteBuffer buffer = ByteBuffer.allocate(8).putLong(System.nanoTime()).flip(); + Session session = this.session; try { if (session != null) { session.getRemote().sendPing(buffer); @@ -221,7 +226,10 @@ public void onWebSocketPing(ByteBuffer payload) { } @Override - public void onWebSocketPong(ByteBuffer payload) { + public void onWebSocketPong(@Nullable ByteBuffer payload) { + if (payload == null) { + return; + } long start = payload.getLong(); long roundTrip = System.nanoTime() - start; diff --git a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/handler/TeslaSSOHandler.java b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/handler/TeslaSSOHandler.java index 1ba2bbe69b1a4..7fa6953b633e8 100644 --- a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/handler/TeslaSSOHandler.java +++ b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/handler/TeslaSSOHandler.java @@ -28,8 +28,8 @@ import org.eclipse.jetty.client.util.StringContentProvider; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpMethod; -import org.openhab.binding.tesla.internal.protocol.sso.RefreshTokenRequest; -import org.openhab.binding.tesla.internal.protocol.sso.TokenResponse; +import org.openhab.binding.tesla.internal.protocol.dto.sso.RefreshTokenRequest; +import org.openhab.binding.tesla.internal.protocol.dto.sso.TokenResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -72,10 +72,10 @@ public TokenResponse getAccessToken(String refreshToken) { String refreshTokenResponse = refreshResponse.getContentAsString(); TokenResponse tokenResponse = gson.fromJson(refreshTokenResponse.trim(), TokenResponse.class); - if (tokenResponse != null && tokenResponse.access_token != null && !tokenResponse.access_token.isEmpty()) { - tokenResponse.created_at = Instant.now().getEpochSecond(); - logger.debug("Access token expires in {} seconds at {}", tokenResponse.expires_in, DATE_FORMATTER - .format(Instant.ofEpochMilli((tokenResponse.created_at + tokenResponse.expires_in) * 1000))); + if (tokenResponse != null && tokenResponse.accessToken != null && !tokenResponse.accessToken.isEmpty()) { + tokenResponse.createdAt = Instant.now().getEpochSecond(); + logger.debug("Access token expires in {} seconds at {}", tokenResponse.expiresIn, DATE_FORMATTER + .format(Instant.ofEpochMilli((tokenResponse.createdAt + tokenResponse.expiresIn) * 1000))); return tokenResponse; } else { logger.debug("An error occurred while exchanging SSO auth token for API access token."); diff --git a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/handler/TeslaVehicleHandler.java b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/handler/TeslaVehicleHandler.java index b4af61a80b250..7ef596e6b90cb 100644 --- a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/handler/TeslaVehicleHandler.java +++ b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/handler/TeslaVehicleHandler.java @@ -25,6 +25,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -37,21 +38,22 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +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.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.Request; -import org.openhab.binding.tesla.internal.protocol.ChargeState; -import org.openhab.binding.tesla.internal.protocol.ClimateState; -import org.openhab.binding.tesla.internal.protocol.DriveState; -import org.openhab.binding.tesla.internal.protocol.Event; -import org.openhab.binding.tesla.internal.protocol.GUIState; -import org.openhab.binding.tesla.internal.protocol.SoftwareUpdate; -import org.openhab.binding.tesla.internal.protocol.Vehicle; -import org.openhab.binding.tesla.internal.protocol.VehicleData; -import org.openhab.binding.tesla.internal.protocol.VehicleState; +import org.openhab.binding.tesla.internal.protocol.dto.ChargeState; +import org.openhab.binding.tesla.internal.protocol.dto.ClimateState; +import org.openhab.binding.tesla.internal.protocol.dto.DriveState; +import org.openhab.binding.tesla.internal.protocol.dto.Event; +import org.openhab.binding.tesla.internal.protocol.dto.GUIState; +import org.openhab.binding.tesla.internal.protocol.dto.SoftwareUpdate; +import org.openhab.binding.tesla.internal.protocol.dto.Vehicle; +import org.openhab.binding.tesla.internal.protocol.dto.VehicleData; +import org.openhab.binding.tesla.internal.protocol.dto.VehicleState; import org.openhab.binding.tesla.internal.throttler.QueueChannelThrottler; import org.openhab.binding.tesla.internal.throttler.Rate; import org.openhab.core.io.net.http.WebSocketFactory; @@ -64,6 +66,7 @@ import org.openhab.core.library.types.StringType; import org.openhab.core.library.unit.SIUnits; import org.openhab.core.library.unit.Units; +import org.openhab.core.thing.Bridge; import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.Thing; import org.openhab.core.thing.ThingStatus; @@ -88,9 +91,9 @@ * @author Karel Goderis - Initial contribution * @author Kai Kreuzer - Refactored to use separate account handler and improved configuration options */ +@NonNullByDefault public class TeslaVehicleHandler extends BaseThingHandler { - private static final int FAST_STATUS_REFRESH_INTERVAL = 15000; private static final int SLOW_STATUS_REFRESH_INTERVAL = 60000; private static final int API_SLEEP_INTERVAL_MINUTES = 20; private static final int MOVE_THRESHOLD_INTERVAL_MINUTES_DEFAULT = 5; @@ -105,13 +108,21 @@ public class TeslaVehicleHandler extends BaseThingHandler { private final Logger logger = LoggerFactory.getLogger(TeslaVehicleHandler.class); // Vehicle state variables + @Nullable protected Vehicle vehicle; + @Nullable protected String vehicleJSON; + @Nullable protected DriveState driveState; + @Nullable protected GUIState guiState; + @Nullable protected VehicleState vehicleState; + @Nullable protected ChargeState chargeState; + @Nullable protected ClimateState climateState; + @Nullable protected SoftwareUpdate softwareUpdate; protected boolean allowWakeUp; @@ -127,7 +138,7 @@ public class TeslaVehicleHandler extends BaseThingHandler { protected long eventIntervalTimestamp; protected int eventIntervalErrors; protected int inactivity = MOVE_THRESHOLD_INTERVAL_MINUTES_DEFAULT; - protected ReentrantLock lock; + protected ReentrantLock lock = new ReentrantLock(); protected double lastLongitude; protected double lastLatitude; @@ -140,13 +151,13 @@ public class TeslaVehicleHandler extends BaseThingHandler { protected String lastState = ""; protected boolean isInactive = false; - protected TeslaAccountHandler account; + protected @NonNullByDefault({}) TeslaAccountHandler account; - protected QueueChannelThrottler stateThrottler; - protected TeslaChannelSelectorProxy teslaChannelSelectorProxy = new TeslaChannelSelectorProxy(); - protected Thread eventThread; - protected ScheduledFuture stateJob; + protected @Nullable QueueChannelThrottler stateThrottler; + protected @Nullable Thread eventThread; + protected @Nullable ScheduledFuture stateJob; protected WebSocketFactory webSocketFactory; + protected TeslaChannelSelectorProxy teslaChannelSelectorProxy = new TeslaChannelSelectorProxy(); private final Gson gson = new Gson(); @@ -155,68 +166,62 @@ public TeslaVehicleHandler(Thing thing, WebSocketFactory webSocketFactory) { this.webSocketFactory = webSocketFactory; } - @SuppressWarnings("null") @Override public void initialize() { logger.trace("Initializing the Tesla handler for {}", getThing().getUID()); updateStatus(ThingStatus.UNKNOWN); + allowWakeUp = (boolean) getConfig().get(TeslaBindingConstants.CONFIG_ALLOWWAKEUP); allowWakeUpForCommands = (boolean) getConfig().get(TeslaBindingConstants.CONFIG_ALLOWWAKEUPFORCOMMANDS); enableEvents = (boolean) getConfig().get(TeslaBindingConstants.CONFIG_ENABLEEVENTS); - Number inactivityParam = (Number) getConfig().get(TeslaBindingConstants.CONFIG_INACTIVITY); - inactivity = inactivityParam == null ? MOVE_THRESHOLD_INTERVAL_MINUTES_DEFAULT : inactivityParam.intValue(); - Boolean useDriveStateParam = (boolean) getConfig().get(TeslaBindingConstants.CONFIG_USEDRIVESTATE); - useDriveState = useDriveStateParam == null ? false : useDriveStateParam; - Boolean useAdvancedStatesParam = (boolean) getConfig().get(TeslaBindingConstants.CONFIG_USEDADVANCEDSTATES); - useAdvancedStates = useAdvancedStatesParam == null ? false : useAdvancedStatesParam; - - account = (TeslaAccountHandler) getBridge().getHandler(); + + inactivity = Objects.requireNonNullElse((Number) getConfig().get(TeslaBindingConstants.CONFIG_INACTIVITY), + MOVE_THRESHOLD_INTERVAL_MINUTES_DEFAULT).intValue(); + useDriveState = Objects + .requireNonNullElse((boolean) getConfig().get(TeslaBindingConstants.CONFIG_USEDRIVESTATE), false); + useAdvancedStates = Objects + .requireNonNullElse((boolean) getConfig().get(TeslaBindingConstants.CONFIG_USEDADVANCEDSTATES), false); + Bridge bridge = getBridge(); + if (bridge == null || !(bridge.getHandler() instanceof TeslaAccountHandler teslaAccountHandler)) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED); + return; + } + account = teslaAccountHandler; lock = new ReentrantLock(); scheduler.execute(this::queryVehicleAndUpdate); - lock.lock(); - try { - Map channels = new HashMap<>(); - channels.put(DATA_THROTTLE, new Rate(1, 1, TimeUnit.SECONDS)); - channels.put(COMMAND_THROTTLE, new Rate(20, 1, TimeUnit.MINUTES)); - - Rate firstRate = new Rate(20, 1, TimeUnit.MINUTES); - Rate secondRate = new Rate(200, 10, TimeUnit.MINUTES); - stateThrottler = new QueueChannelThrottler(firstRate, scheduler, channels); - stateThrottler.addRate(secondRate); - - if (stateJob == null || stateJob.isCancelled()) { - stateJob = scheduler.scheduleWithFixedDelay(stateRunnable, 0, SLOW_STATUS_REFRESH_INTERVAL, - TimeUnit.MILLISECONDS); - } - - if (enableEvents) { - if (eventThread == null) { - eventThread = new Thread(eventRunnable, "OH-binding-" + getThing().getUID() + "-events"); - eventThread.start(); - } - } - } finally { - lock.unlock(); + Map channels = new HashMap<>(); + channels.put(DATA_THROTTLE, new Rate(1, 1, TimeUnit.SECONDS)); + channels.put(COMMAND_THROTTLE, new Rate(20, 1, TimeUnit.MINUTES)); + + Rate firstRate = new Rate(20, 1, TimeUnit.MINUTES); + Rate secondRate = new Rate(200, 10, TimeUnit.MINUTES); + QueueChannelThrottler stateThrottler = new QueueChannelThrottler(firstRate, scheduler, channels); + stateThrottler.addRate(secondRate); + this.stateThrottler = stateThrottler; + stateJob = scheduler.scheduleWithFixedDelay(stateRunnable, 0, SLOW_STATUS_REFRESH_INTERVAL, + TimeUnit.MILLISECONDS); + + if (enableEvents) { + Thread eventThread = new Thread(eventRunnable, "OH-binding-" + getThing().getUID() + "-events"); + eventThread.start(); + this.eventThread = eventThread; } } @Override public void dispose() { logger.trace("Disposing the Tesla handler for {}", getThing().getUID()); - lock.lock(); - try { - if (stateJob != null && !stateJob.isCancelled()) { - stateJob.cancel(true); - stateJob = null; - } - if (eventThread != null && !eventThread.isInterrupted()) { - eventThread.interrupt(); - eventThread = null; - } - } finally { - lock.unlock(); + ScheduledFuture stateJob = this.stateJob; + if (stateJob != null && !stateJob.isCancelled()) { + stateJob.cancel(true); + this.stateJob = null; + } + Thread eventThread = this.eventThread; + if (eventThread != null && !eventThread.isInterrupted()) { + eventThread.interrupt(); + this.eventThread = null; } } @@ -225,7 +230,8 @@ public void dispose() { * * @return the vehicle id */ - public String getVehicleId() { + public @Nullable String getVehicleId() { + Vehicle vehicle = this.vehicle; if (vehicle != null) { return vehicle.id; } else { @@ -248,9 +254,9 @@ public void handleCommand(ChannelUID channelUID, Command command) { setActive(); // Request the state of all known variables. This is sub-optimal, but the requests get scheduled and - // throttled so we are safe not to break the Tesla SLA + // throbridgettled so we are safe not to break the Tesla SLA requestAllData(); - } else if (selector != null) { + } else { if (!isAwake() && allowWakeUpForCommands) { logger.debug("Waking vehicle to send command."); wakeUp(); @@ -267,10 +273,16 @@ public void handleCommand(ChannelUID channelUID, Command command) { setChargeLimit(0); } else if (command instanceof IncreaseDecreaseType && command == IncreaseDecreaseType.INCREASE) { - setChargeLimit(Math.min(chargeState.charge_limit_soc + 1, 100)); + ChargeState chargeState = this.chargeState; + if (chargeState != null) { + setChargeLimit(Math.min(chargeState.chargeLimitSoc + 1, 100)); + } } else if (command instanceof IncreaseDecreaseType && command == IncreaseDecreaseType.DECREASE) { - setChargeLimit(Math.max(chargeState.charge_limit_soc - 1, 0)); + ChargeState chargeState = this.chargeState; + if (chargeState != null) { + setChargeLimit(Math.max(chargeState.chargeLimitSoc - 1, 0)); + } } break; } @@ -298,23 +310,17 @@ public void handleCommand(ChannelUID channelUID, Command command) { break; case COMBINED_TEMP: { QuantityType quantity = commandToQuantityType(command); - if (quantity != null) { - setCombinedTemperature(quanityToRoundedFloat(quantity)); - } + setCombinedTemperature(quanityToRoundedFloat(quantity)); break; } case DRIVER_TEMP: { QuantityType quantity = commandToQuantityType(command); - if (quantity != null) { - setDriverTemperature(quanityToRoundedFloat(quantity)); - } + setDriverTemperature(quanityToRoundedFloat(quantity)); break; } case PASSENGER_TEMP: { QuantityType quantity = commandToQuantityType(command); - if (quantity != null) { - setPassengerTemperature(quanityToRoundedFloat(quantity)); - } + setPassengerTemperature(quanityToRoundedFloat(quantity)); break; } case SENTRY_MODE: { @@ -411,11 +417,12 @@ public void handleCommand(ChannelUID channelUID, Command command) { } case RT: { if (command instanceof OnOffType onOffCommand) { + VehicleState vehicleState = this.vehicleState; if (onOffCommand == OnOffType.ON) { - if (vehicleState.rt == 0) { + if (vehicleState != null && vehicleState.rt == 0) { openTrunk(); } - } else if (vehicleState.rt == 1) { + } else if (vehicleState != null && vehicleState.rt == 1) { closeTrunk(); } } @@ -459,9 +466,10 @@ public void handleCommand(ChannelUID channelUID, Command command) { } } - public void sendCommand(String command, String payLoad, WebTarget target) { + public void sendCommand(String command, @Nullable String payLoad, WebTarget target) { if (COMMAND_WAKE_UP.equals(command) || isAwake() || allowWakeUpForCommands) { Request request = account.newRequest(this, command, payLoad, target, allowWakeUpForCommands); + QueueChannelThrottler stateThrottler = this.stateThrottler; if (stateThrottler != null) { stateThrottler.submit(COMMAND_THROTTLE, request); } @@ -475,6 +483,7 @@ public void sendCommand(String command) { public void sendCommand(String command, String payLoad) { if (COMMAND_WAKE_UP.equals(command) || isAwake() || allowWakeUpForCommands) { Request request = account.newRequest(this, command, payLoad, account.commandTarget, allowWakeUpForCommands); + QueueChannelThrottler stateThrottler = this.stateThrottler; if (stateThrottler != null) { stateThrottler.submit(COMMAND_THROTTLE, request); } @@ -484,16 +493,18 @@ public void sendCommand(String command, String payLoad) { public void sendCommand(String command, WebTarget target) { if (COMMAND_WAKE_UP.equals(command) || isAwake() || allowWakeUpForCommands) { Request request = account.newRequest(this, command, "{}", target, allowWakeUpForCommands); + QueueChannelThrottler stateThrottler = this.stateThrottler; if (stateThrottler != null) { stateThrottler.submit(COMMAND_THROTTLE, request); } } } - public void requestData(String command, String payLoad) { + public void requestData(String command, @Nullable String payLoad) { if (COMMAND_WAKE_UP.equals(command) || isAwake() || (!"vehicleData".equals(command) && allowWakeUpForCommands)) { Request request = account.newRequest(this, command, payLoad, account.dataRequestTarget, false); + QueueChannelThrottler stateThrottler = this.stateThrottler; if (stateThrottler != null) { stateThrottler.submit(DATA_THROTTLE, request); } @@ -529,14 +540,16 @@ public void requestAllData() { } protected boolean isAwake() { - return vehicle != null && "online".equals(vehicle.state) && vehicle.vehicle_id != null; + Vehicle vehicle = this.vehicle; + return vehicle != null && "online".equals(vehicle.state) && vehicle.vehicleId != null; } protected boolean isInMotion() { + DriveState driveState = this.driveState; if (driveState != null) { - if (driveState.speed != null && driveState.shift_state != null) { + if (driveState.speed != null && driveState.shiftState != null) { return !"Undefined".equals(driveState.speed) - && (!"P".equals(driveState.shift_state) || !"Undefined".equals(driveState.shift_state)); + && (!"P".equals(driveState.shiftState) || !"Undefined".equals(driveState.shiftState)); } } return false; @@ -551,15 +564,16 @@ protected boolean isInactive() { } protected boolean isCharging() { - return chargeState != null && "Charging".equals(chargeState.charging_state); + ChargeState chargeState = this.chargeState; + return chargeState != null && "Charging".equals(chargeState.chargingState); } protected boolean notReadyForSleep() { boolean status; int computedInactivityPeriod = inactivity; - + VehicleState vehicleState = this.vehicleState; if (useAdvancedStates) { - if (vehicleState.is_user_present && !isInMotion()) { + if (vehicleState != null && vehicleState.isUserPresent && !isInMotion()) { logger.debug("Car is occupied but stationary."); if (lastAdvModesTimestamp < (System.currentTimeMillis() - (THRESHOLD_INTERVAL_FOR_ADVANCED_MINUTES * 60 * 1000))) { @@ -568,7 +582,7 @@ protected boolean notReadyForSleep() { return (backOffCounter++ % 6 == 0); // using 6 should make sure 1 out of 5 pollers get serviced, // about every min. } - } else if (vehicleState.sentry_mode) { + } else if (vehicleState != null && vehicleState.sentryMode) { logger.debug("Car is in sentry mode."); if (lastAdvModesTimestamp < (System.currentTimeMillis() - (THRESHOLD_INTERVAL_FOR_ADVANCED_MINUTES * 60 * 1000))) { @@ -576,23 +590,23 @@ protected boolean notReadyForSleep() { } else { return (backOffCounter++ % 6 == 0); } - } else if ((vehicleState.center_display_state != 0) && (!isInMotion())) { + } else if (vehicleState != null && (vehicleState.centerDisplayState != 0) && (!isInMotion())) { logger.debug("Car is in camp, climate keep, dog, or other mode preventing sleep. Mode {}", - vehicleState.center_display_state); + vehicleState.centerDisplayState); return (backOffCounter++ % 6 == 0); } else { lastAdvModesTimestamp = System.currentTimeMillis(); } } - if (vehicleState != null && vehicleState.homelink_nearby) { + if (vehicleState != null && vehicleState.homelinkNearby) { computedInactivityPeriod = MOVE_THRESHOLD_INTERVAL_MINUTES_DEFAULT; logger.debug("Car is at home. Movement or drive state threshold is {} min.", MOVE_THRESHOLD_INTERVAL_MINUTES_DEFAULT); } - + DriveState driveState = this.driveState; if (useDriveState) { - if (driveState.shift_state != null) { + if (driveState != null && driveState.shiftState != null) { logger.debug("Car drive state not null and not ready to sleep."); return true; } else { @@ -632,7 +646,7 @@ protected void setActive() { lastLongitude = 0; } - protected boolean checkResponse(Response response, boolean immediatelyFail) { + protected boolean checkResponse(@Nullable Response response, boolean immediatelyFail) { if (response != null && response.getStatus() == 200) { return true; } else if (response != null && response.getStatus() == 401) { @@ -711,11 +725,13 @@ public void setCombinedTemperature(float temperature) { } public void setDriverTemperature(float temperature) { - setTemperature(temperature, climateState != null ? climateState.passenger_temp_setting : temperature); + ClimateState climateState = this.climateState; + setTemperature(temperature, climateState != null ? climateState.passengerTempSetting : temperature); } public void setPassengerTemperature(float temperature) { - setTemperature(climateState != null ? climateState.driver_temp_setting : temperature, temperature); + ClimateState climateState = this.climateState; + setTemperature(climateState != null ? climateState.passengerTempSetting : temperature, temperature); } public void openFrunk() { @@ -734,7 +750,7 @@ public void closeTrunk() { openTrunk(); } - public void setValetMode(boolean b, Integer pin) { + public void setValetMode(boolean b, @Nullable Integer pin) { JsonObject payloadObject = new JsonObject(); payloadObject.addProperty("on", b); if (pin != null) { @@ -785,7 +801,7 @@ public void setSteeringWheelHeater(boolean isOn) { sendCommand(COMMAND_STEERING_WHEEL_HEATER, gson.toJson(payloadObject), account.commandTarget); } - protected Vehicle queryVehicle() { + protected @Nullable Vehicle queryVehicle() { String authHeader = account.getAuthHeader(); if (authHeader != null) { @@ -805,14 +821,17 @@ protected Vehicle queryVehicle() { JsonObject jsonObject = JsonParser.parseString(response.readEntity(String.class)).getAsJsonObject(); Vehicle[] vehicleArray = gson.fromJson(jsonObject.getAsJsonArray("response"), Vehicle[].class); - + if (vehicleArray == null) { + logger.debug("Response resulted in unexpected null array"); + return null; + } for (Vehicle vehicle : vehicleArray) { logger.debug("Querying the vehicle: VIN {}", vehicle.vin); if (vehicle.vin.equals(getConfig().get(VIN))) { vehicleJSON = gson.toJson(vehicle); parseAndUpdate("queryVehicle", null, vehicleJSON); if (logger.isTraceEnabled()) { - logger.trace("Vehicle is id {}/vehicle_id {}/tokens {}", vehicle.id, vehicle.vehicle_id, + logger.trace("Vehicle is id {}/vehicle_id {}/tokens {}", vehicle.id, vehicle.vehicleId, vehicle.tokens); } return vehicle; @@ -831,9 +850,9 @@ protected void queryVehicleAndUpdate() { vehicle = queryVehicle(); } - public void parseAndUpdate(String request, String payLoad, String result) { + public void parseAndUpdate(@Nullable String request, @Nullable String payLoad, @Nullable String result) { final double locationThreshold = .0000001; - + Vehicle vehicle = this.vehicle; try { if (request != null && result != null && !"null".equals(result)) { updateStatus(ThingStatus.ONLINE); @@ -848,12 +867,12 @@ public void parseAndUpdate(String request, String payLoad, String result) { return; } - if (vehicle != null && ("asleep".equals(vehicle.state) || "offline".equals(vehicle.state))) { + if ("asleep".equals(vehicle.state) || "offline".equals(vehicle.state)) { logger.debug("Vehicle is {}", vehicle.state); return; } - if (vehicle != null && !lastState.equals(vehicle.state)) { + if (!lastState.equals(vehicle.state)) { lastState = vehicle.state; // in case vehicle changed to awake, refresh all data @@ -888,43 +907,44 @@ public void parseAndUpdate(String request, String payLoad, String result) { return; } - driveState = vehicleData.drive_state; - if (Math.abs(lastLatitude - driveState.latitude) > locationThreshold - || Math.abs(lastLongitude - driveState.longitude) > locationThreshold) { + DriveState driveState = this.driveState = vehicleData.driveState; + + if (driveState != null && (Math.abs(lastLatitude - driveState.latitude) > locationThreshold + || Math.abs(lastLongitude - driveState.longitude) > locationThreshold)) { logger.debug("Vehicle moved, resetting last location timestamp"); lastLatitude = driveState.latitude; lastLongitude = driveState.longitude; lastLocationChangeTimestamp = System.currentTimeMillis(); } - logger.trace("Drive state: {}", driveState.shift_state); + logger.trace("Drive state: {}", driveState != null ? driveState.shiftState : "null"); - if ((driveState.shift_state == null) && (lastValidDriveStateNotNull)) { + if ((driveState != null && driveState.shiftState == null) && (lastValidDriveStateNotNull)) { logger.debug("Set NULL shiftstate time"); lastValidDriveStateNotNull = false; lastDriveStateChangeToNullTimestamp = System.currentTimeMillis(); - } else if (driveState.shift_state != null) { + } else if (driveState != null && driveState.shiftState != null) { logger.trace("Clear NULL shiftstate time"); lastValidDriveStateNotNull = true; } - guiState = vehicleData.gui_settings; + guiState = vehicleData.guiSettings; - vehicleState = vehicleData.vehicle_state; + VehicleState vehicleState = this.vehicleState = vehicleData.vehicleState; - chargeState = vehicleData.charge_state; + chargeState = vehicleData.chargeState; if (isCharging()) { updateState(CHANNEL_CHARGE, OnOffType.ON); } else { updateState(CHANNEL_CHARGE, OnOffType.OFF); } - climateState = vehicleData.climate_state; + ClimateState climateState = this.climateState = vehicleData.climateState; BigDecimal avgtemp = roundBigDecimal(new BigDecimal( - (climateState.driver_temp_setting + climateState.passenger_temp_setting) / 2.0f)); + (climateState.passengerTempSetting + climateState.passengerTempSetting) / 2.0f)); updateState(CHANNEL_COMBINED_TEMP, new QuantityType<>(avgtemp, SIUnits.CELSIUS)); - softwareUpdate = vehicleState.software_update; + SoftwareUpdate softwareUpdate = this.softwareUpdate = vehicleState.softwareUpdate; try { lock.lock(); @@ -990,8 +1010,13 @@ public void parseAndUpdate(String request, String payLoad, String result) { @SuppressWarnings("unchecked") protected QuantityType commandToQuantityType(Command command) { - if (command instanceof QuantityType) { - return ((QuantityType) command).toUnit(SIUnits.CELSIUS); + if (command instanceof QuantityType quantityCommand) { + QuantityType commandInCelsius = quantityCommand.toUnit(SIUnits.CELSIUS); + if (commandInCelsius == null) { + logger.warn("Unable to convert command {} to CELSIUS", command); + } else { + return commandInCelsius; + } } return new QuantityType<>(new BigDecimal(command.toString()), SIUnits.CELSIUS); } @@ -1024,18 +1049,19 @@ protected BigDecimal roundBigDecimal(BigDecimal value) { }; protected Runnable eventRunnable = new Runnable() { + @Nullable TeslaEventEndpoint eventEndpoint; boolean isAuthenticated = false; long lastPingTimestamp = 0; @Override public void run() { - eventEndpoint = new TeslaEventEndpoint(getThing().getUID(), webSocketFactory); + TeslaEventEndpoint eventEndpoint = new TeslaEventEndpoint(getThing().getUID(), webSocketFactory); eventEndpoint.addEventHandler(new TeslaEventEndpoint.EventHandler() { @Override - public void handleEvent(Event event) { + public void handleEvent(@Nullable Event event) { if (event != null) { - switch (event.msg_type) { + switch (event.msgType) { case "control:hello": logger.debug("Event : Received hello"); break; @@ -1068,7 +1094,7 @@ public void handleEvent(Event event) { if (!selector.isProperty()) { State newState = teslaChannelSelectorProxy.getState(vals[i], selector, editProperties()); - if (newState != null && !"".equals(vals[i])) { + if (!"".equals(vals[i])) { updateState(selector.getChannelID(), newState); } else { updateState(selector.getChannelID(), UnDefType.UNDEF); @@ -1115,29 +1141,28 @@ public void handleEvent(Event event) { } break; case "data:error": - logger.debug("Event : Received an error: '{}'/'{}'", event.value, event.error_type); + logger.debug("Event : Received an error: '{}'/'{}'", event.value, event.errorType); eventEndpoint.closeConnection(); break; } } } }); - + this.eventEndpoint = eventEndpoint; while (true) { try { if (getThing().getStatus() == ThingStatus.ONLINE) { if (isAwake()) { eventEndpoint.connect(new URI(URI_EVENT)); - if (eventEndpoint.isConnected()) { if (!isAuthenticated) { - logger.debug("Event : Authenticating vehicle {}", vehicle.vehicle_id); + logger.debug("Event : Authenticating vehicle {}", vehicle.vehicleId); JsonObject payloadObject = new JsonObject(); payloadObject.addProperty("msg_type", "data:subscribe_oauth"); payloadObject.addProperty("token", account.getAccessToken()); payloadObject.addProperty("value", Arrays.asList(EventKeys.values()).stream() .skip(1).map(Enum::toString).collect(Collectors.joining(","))); - payloadObject.addProperty("tag", vehicle.vehicle_id); + payloadObject.addProperty("tag", vehicle.vehicleId); eventEndpoint.sendMessage(gson.toJson(payloadObject)); isAuthenticated = true; @@ -1200,6 +1225,7 @@ public void handleEvent(Event event) { if (Thread.interrupted()) { logger.debug("Event : The event thread was interrupted"); eventEndpoint.close(); + this.eventEndpoint = eventEndpoint; return; } } diff --git a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/handler/VehicleListener.java b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/handler/VehicleListener.java index 4008bd4f4ebdb..0a1c030676c95 100644 --- a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/handler/VehicleListener.java +++ b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/handler/VehicleListener.java @@ -14,8 +14,8 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.tesla.internal.protocol.Vehicle; -import org.openhab.binding.tesla.internal.protocol.VehicleConfig; +import org.openhab.binding.tesla.internal.protocol.dto.Vehicle; +import org.openhab.binding.tesla.internal.protocol.dto.VehicleConfig; /** * The {@link VehicleListener} interface can be implemented by classes that want to be informed about diff --git a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/ChargeState.java b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/ChargeState.java deleted file mode 100644 index 69018cbe2070d..0000000000000 --- a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/ChargeState.java +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Copyright (c) 2010-2024 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.protocol; - -/** - * The {@link ChargeState} is a datastructure to capture - * variables sent by the Tesla Vehicle - * - * @author Karel Goderis - Initial contribution - */ -public class ChargeState { - public boolean battery_heater_on; - public boolean charge_enable_request; - public boolean charge_port_door_open; - public boolean charge_to_max_range; - public boolean eu_vehicle; - public boolean fast_charger_present; - public boolean managed_charging_active; - public boolean managed_charging_user_canceled; - public boolean motorized_charge_port; - public boolean not_enough_power_to_heat; - public boolean scheduled_charging_pending; - public boolean trip_charging; - public float battery_current; - public float battery_range; - public float charge_energy_added; - public float charge_miles_added_ideal; - public float charge_miles_added_rated; - public float charge_rate; - public float est_battery_range; - public float ideal_battery_range; - public float time_to_full_charge; - public int battery_level; - public int charge_amps; - public int charge_current_request; - public int charge_current_request_max; - public int charge_limit_soc; - public int charge_limit_soc_max; - public int charge_limit_soc_min; - public int charge_limit_soc_std; - public int charger_actual_current; - public int charger_phases; - public int charger_pilot_current; - public int charger_power; - public int charger_voltage; - public int max_range_charge_counter; - public int usable_battery_level; - public String charge_port_latch; - public String charging_state; - public String conn_charge_cable; - public String fast_charger_brand; - public String fast_charger_type; - public String managed_charging_start_time; - public String scheduled_charging_start_time; - public String user_charge_enable_request; - - ChargeState() { - } -} diff --git a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/ClimateState.java b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/ClimateState.java deleted file mode 100644 index 9eec75556b006..0000000000000 --- a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/ClimateState.java +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright (c) 2010-2024 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.protocol; - -/** - * The {@link ClimateState} is a datastructure to capture - * variables sent by the Tesla Vehicle - * - * @author Karel Goderis - Initial contribution - */ -public class ClimateState { - - public boolean battery_heater; - public boolean battery_heater_no_power; - public boolean is_auto_conditioning_on; - public boolean is_climate_on; - public boolean is_front_defroster_on; - public boolean is_preconditioning; - public boolean is_rear_defroster_on; - public int seat_heater_left; - public int seat_heater_rear_center; - public int seat_heater_rear_left; - public int seat_heater_rear_right; - public int seat_heater_right; - public boolean side_mirror_heaters; - public boolean smart_preconditioning; - public boolean steering_wheel_heater; - public boolean wiper_blade_heater; - public float driver_temp_setting; - public float inside_temp; - public float outside_temp; - public float passenger_temp_setting; - public int fan_status; - public int left_temp_direction; - public int max_avail_temp; - public int min_avail_temp; - public int right_temp_direction; - public int seat_heater_rear_left_back; - public int seat_heater_rear_right_back; - - ClimateState() { - } -} diff --git a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/DriveState.java b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/DriveState.java deleted file mode 100644 index 1403cc15e2755..0000000000000 --- a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/DriveState.java +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright (c) 2010-2024 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.protocol; - -/** - * The {@link DriveState} is a datastructure to capture - * variables sent by the Tesla Vehicle - * - * @author Karel Goderis - Initial contribution - */ -public class DriveState { - - public String active_route_destination; - public double active_route_latitude; - public double active_route_longitude; - public double active_route_miles_to_arrival; - public double active_route_minutes_to_arrival; - public double active_route_traffic_minutes_delay; - public double latitude; - public double longitude; - public double native_latitude; - public double native_longitude; - public int gps_as_of; - public int heading; - public int native_location_supported; - public String native_type; - public String shift_state; - public String speed; - - DriveState() { - } -} diff --git a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/GUIState.java b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/GUIState.java deleted file mode 100644 index ebb73c9d57598..0000000000000 --- a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/GUIState.java +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright (c) 2010-2024 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.protocol; - -/** - * The {@link GUIState} is a datastructure to capture - * variables sent by the Tesla Vehicle - * - * @author Karel Goderis - Initial contribution - */ -public class GUIState { - - public String gui_distance_units; - public String gui_temperature_units; - public String gui_charge_rate_units; - public String gui_24_hour_time; - public String gui_range_display; - - public GUIState() { - } -} diff --git a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/Vehicle.java b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/Vehicle.java deleted file mode 100644 index a3e37f36a8cc0..0000000000000 --- a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/Vehicle.java +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright (c) 2010-2024 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.protocol; - -/** - * The {@link Vehicle} is a datastructure to capture - * variables sent by the Tesla Vehicle - * - * @author Karel Goderis - Initial contribution - */ -public class Vehicle { - - public String color; - public String display_name; - public String id; - public String option_codes; - public String vehicle_id; - public String vin; - public String[] tokens; - public String state; - public boolean remote_start_enabled; - public boolean calendar_enabled; - public boolean notifications_enabled; - public String backseat_token; - public String backseat_token_updated_at; - - Vehicle() { - } -} diff --git a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/VehicleConfig.java b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/VehicleConfig.java deleted file mode 100644 index 85ea588a75662..0000000000000 --- a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/VehicleConfig.java +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright (c) 2010-2024 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.protocol; - -import org.openhab.binding.tesla.internal.TeslaBindingConstants; -import org.openhab.core.thing.ThingTypeUID; - -/** - * The {@link VehicleConfig} is a data structure to capture - * vehicle configuration variables sent by the Tesla Vehicle - * - * @author Dan Cunningham - Initial contribution - */ -public class VehicleConfig { - public boolean can_accept_navigation_requests; - public boolean can_actuate_trunks; - public boolean eu_vehicle; - public boolean has_air_suspension; - public boolean has_ludicrous_mode; - public boolean motorized_charge_port; - public boolean plg; - public boolean rhd; - public boolean use_range_badging; - public int rear_seat_heaters; - public int rear_seat_type; - public int sun_roof_installed; - public long timestamp; - public String car_special_type; - public String car_type; - public String charge_port_type; - public String exterior_color; - public String roof_color; - public String spoiler_type; - public String third_row_seats; - public String trim_badging; - public String wheel_type; - - public ThingTypeUID identifyModel() { - switch (car_type) { - case "models": - case "models2": - return TeslaBindingConstants.THING_TYPE_MODELS; - case "modelx": - return TeslaBindingConstants.THING_TYPE_MODELX; - case "model3": - return TeslaBindingConstants.THING_TYPE_MODEL3; - case "modely": - return TeslaBindingConstants.THING_TYPE_MODELY; - default: - return TeslaBindingConstants.THING_TYPE_VEHICLE; - } - } -} diff --git a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/VehicleData.java b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/VehicleData.java deleted file mode 100644 index 172ea20188cdc..0000000000000 --- a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/VehicleData.java +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright (c) 2010-2024 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.protocol; - -/** - * The {@link VehicleData} is a data structure to capture - * variables sent by the Tesla API about a vehicle. - * - * @author Kai Kreuzer - Initial contribution - */ -public class VehicleData { - - public String color; - public String display_name; - public String id; - public String option_codes; - public String vehicle_id; - public String vin; - public String[] tokens; - public String state; - public boolean remote_start_enabled; - public boolean calendar_enabled; - public boolean notifications_enabled; - public String backseat_token; - public String backseat_token_updated_at; - - public ChargeState charge_state; - public ClimateState climate_state; - public DriveState drive_state; - public GUIState gui_settings; - public VehicleConfig vehicle_config; - public VehicleState vehicle_state; - - VehicleData() { - } -} diff --git a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/VehicleState.java b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/VehicleState.java deleted file mode 100644 index 38c7c175aa80e..0000000000000 --- a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/VehicleState.java +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Copyright (c) 2010-2024 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.protocol; - -/** - * The {@link VehicleState} is a datastructure to capture - * variables sent by the Tesla Vehicle - * - * @author Karel Goderis - Initial contribution - */ -public class VehicleState { - - public boolean dark_rims; - public boolean has_spoiler; - public boolean homelink_nearby; - public boolean is_user_present; - public boolean locked; - public boolean notifications_supported; - public boolean parsed_calendar_supported; - public boolean remote_start; - public boolean remote_start_supported; - public boolean rhd; - public boolean sentry_mode; - public boolean valet_mode; - public boolean valet_pin_needed; - public float odometer; - public int center_display_state; - public int df; - public int dr; - public int ft; - public int pf; - public int pr; - public int rear_seat_heaters; - public int rt; - public int seat_type; - public int sun_roof_installed; - public int sun_roof_percent_open; - public String autopark_state; - public String autopark_state_v2; - public String autopark_style; - public String car_version; - public String exterior_color; - public String last_autopark_error; - public String perf_config; - public String roof_color; - public String sun_roof_state; - public String vehicle_name; - public String wheel_type; - - public SoftwareUpdate software_update; - - VehicleState() { - } -} diff --git a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/ChargeState.java b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/ChargeState.java new file mode 100644 index 0000000000000..6cb5fa49312dc --- /dev/null +++ b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/ChargeState.java @@ -0,0 +1,115 @@ +/** + * Copyright (c) 2010-2024 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.protocol.dto; + +import com.google.gson.annotations.SerializedName; + +/** + * The {@link ChargeState} is a datastructure to capture + * variables sent by the Tesla Vehicle + * + * @author Karel Goderis - Initial contribution + */ +public class ChargeState { + @SerializedName("battery_heater_on") + public boolean batteryHeaterOn; + @SerializedName("charge_enable_request") + public boolean chargeEnableRequest; + @SerializedName("charge_port_door_open") + public boolean chargePortDoorOpen; + @SerializedName("charge_to_max_range") + public boolean chargeToMaxRange; + @SerializedName("eu_vehicle") + public boolean euVehicle; + @SerializedName("fast_charger_present") + public boolean fastChargerPresent; + @SerializedName("managed_charging_active") + public boolean managedChargingActive; + @SerializedName("managed_charging_user_canceled") + public boolean managedChargingUserCanceled; + @SerializedName("motorized_charge_port") + public boolean motorizedChargePort; + @SerializedName("not_enough_power_to_heat") + public boolean notEnoughPowerToHeat; + @SerializedName("scheduled_charging_pending") + public boolean scheduledChargingPending; + @SerializedName("trip_charging") + public boolean tripCharging; + @SerializedName("battery_current") + public float batteryCurrent; + @SerializedName("battery_range") + public float batteryRange; + @SerializedName("charge_energy_added") + public float chargeEnergyAdded; + @SerializedName("charge_miles_added_ideal") + public float chargeMilesAddedIdeal; + @SerializedName("charge_miles_added_rated") + public float chargeMilesAddedRated; + @SerializedName("aaacharge_rateaa") + public float chargeRate; + @SerializedName("est_battery_range") + public float estBatteryRange; + @SerializedName("ideal_battery_range") + public float idealBatteryRange; + @SerializedName("time_to_full_charge") + public float timeToFullCharge; + @SerializedName("battery_level") + public int batteryLevel; + @SerializedName("charge_amps") + public int chargeAmps; + @SerializedName("charge_current_request") + public int chargeCurrentRequest; + @SerializedName("charge_current_request_max") + public int chargeCurrentRequestMax; + @SerializedName("charge_limit_soc") + public int chargeLimitSoc; + @SerializedName("charge_limit_soc_max") + public int chargeLimitSocMax; + @SerializedName("charge_limit_soc_min") + public int chargeLimitSocMin; + @SerializedName("charge_limit_soc_std") + public int chargeLimitSocStd; + @SerializedName("charger_actual_current") + public int chargerActualCurrent; + @SerializedName("charger_phases") + public int chargerPhases; + @SerializedName("charger_pilot_current") + public int chargerPilotCurrent; + @SerializedName("charger_power") + public int chargerPower; + @SerializedName("charger_voltage") + public int chargerVoltage; + @SerializedName("max_range_charge_counter") + public int maxRangeChargeCounter; + @SerializedName("usable_battery_level") + public int usableBatteryLevel; + @SerializedName("charge_port_latch") + public String chargePortLatch; + @SerializedName("charging_state") + public String chargingState; + @SerializedName("conn_charge_cable") + public String connChargeCable; + @SerializedName("fast_charger_brand") + public String fastChargerBrand; + @SerializedName("fast_charger_type") + public String fastChargerType; + @SerializedName("managed_charging_start_time") + public String managedChargingStartTime; + @SerializedName("scheduled_charging_start_time") + public String scheduledChargingStartTime; + @SerializedName("user_charge_enable_request") + public String userChargeEnableRequest; + + ChargeState() { + } +} diff --git a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/ClimateState.java b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/ClimateState.java new file mode 100644 index 0000000000000..c8bea92971018 --- /dev/null +++ b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/ClimateState.java @@ -0,0 +1,92 @@ +/** + * Copyright (c) 2010-2024 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.protocol.dto; + +import com.google.gson.annotations.SerializedName; + +/** + * The {@link ClimateState} is a datastructure to capture + * variables sent by the Tesla Vehicle + * + * @author Karel Goderis - Initial contribution + */ +public class ClimateState { + + @SerializedName("allow_cabin_overheat_protection") + public boolean allowCabinOverheatProtection; + @SerializedName("auto_seat_climate_left") + public boolean autoSeatClimateLeft; + @SerializedName("auto_seat_climate_right") + public boolean autoSeatClimateRight; + @SerializedName("battery_heater") + public boolean batteryHeater; + @SerializedName("battery_heater_no_power") + public boolean batteryHeaterNoPower; + @SerializedName("cabin_overheat_protection") + public String cabinOverheatProtection; + @SerializedName("cabin_overheat_protection_actively_cooling") + public boolean cabinOverheatProtectionActivelyCooling; + @SerializedName("climate_keeper_mode") + public String climateKeeperMode; + @SerializedName("cop_activation_temperature") + public String copActivationTemperature; + @SerializedName("defrost_mode") + public int defrostMode; + @SerializedName("driver_temp_setting") + public float driverTempSetting; + @SerializedName("fan_status") + public int fanStatus; + @SerializedName("hvac_auto_request") + public String hvacAutoRequest; + @SerializedName("inside_temp") + public float insideTemp; + @SerializedName("is_auto_conditioning_on") + public boolean isAutoConditioningOn; + @SerializedName("is_climate_on") + public boolean isClimateOn; + @SerializedName("is_front_defroster_on") + public boolean isFrontDefrosterOn; + @SerializedName("is_preconditioning") + public boolean isPreconditioning; + @SerializedName("is_rear_defroster_on") + public boolean isRearDefrosterOn; + @SerializedName("left_temp_direction") + public int leftTempDirection; + @SerializedName("max_avail_temp") + public float maxAvailTemp; + @SerializedName("min_avail_temp") + public float minAvailTemp; + @SerializedName("outside_temp") + public float outsideTemp; + @SerializedName("passenger_temp_setting") + public float passengerTempSetting; + @SerializedName("remote_heater_control_enabled") + public boolean remoteHeaterControlEnabled; + @SerializedName("right_temp_direction") + public int rightTempDirection; + @SerializedName("seat_heater_left") + public int seatHeaterLeft; + @SerializedName("seat_heater_right") + public int seatHeaterRight; + @SerializedName("side_mirror_heaters") + public boolean sideMirrorHeaters; + @SerializedName("supports_fan_only_cabin_overheat_protection") + public boolean supportsFanOnlyCabinOverheatProtection; + @SerializedName("timestamp") + public long timestamp; + @SerializedName("wiper_blade_heater") + public boolean wiperBladeHeater; + + ClimateState() { + } +} diff --git a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/DriveState.java b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/DriveState.java new file mode 100644 index 0000000000000..0c452195b3e3e --- /dev/null +++ b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/DriveState.java @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2010-2024 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.protocol.dto; + +import com.google.gson.annotations.SerializedName; + +/** + * The {@link DriveState} is a datastructure to capture + * variables sent by the Tesla Vehicle + * + * @author Karel Goderis - Initial contribution + */ +public class DriveState { + + @SerializedName("gps_as_of") + public int gpsAsOf; + @SerializedName("heading") + public int heading; + @SerializedName("latitude") + public double latitude; + @SerializedName("longitude") + public double longitude; + @SerializedName("native_latitude") + public double nativeLatitude; + @SerializedName("native_location_supported") + public int nativeLocationSupported; + @SerializedName("native_longitude") + public double nativeLongitude; + @SerializedName("native_type") + public String nativeType; + @SerializedName("power") + public int power; + @SerializedName("shift_state") + public String shiftState; + @SerializedName("speed") + public String speed; + @SerializedName("timestamp") + public long timestamp; + + DriveState() { + } +} diff --git a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/Event.java b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/Event.java similarity index 73% rename from bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/Event.java rename to bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/Event.java index 02e6fbf1802b6..bdb0bef47b62d 100644 --- a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/Event.java +++ b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/Event.java @@ -10,7 +10,9 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.tesla.internal.protocol; +package org.openhab.binding.tesla.internal.protocol.dto; + +import com.google.gson.annotations.SerializedName; /** * The {@link Event} is a datastructure to capture @@ -19,9 +21,11 @@ * @author Karel Goderis - Initial contribution */ public class Event { - public String msg_type; + @SerializedName("msg_type") + public String msgType; public String value; public String tag; - public String error_type; + @SerializedName("error_type") + public String errorType; public int connectionTimeout; } diff --git a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/GUIState.java b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/GUIState.java new file mode 100644 index 0000000000000..dea7ebf585286 --- /dev/null +++ b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/GUIState.java @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2010-2024 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.protocol.dto; + +import com.google.gson.annotations.SerializedName; + +/** + * The {@link GUIState} is a datastructure to capture + * variables sent by the Tesla Vehicle + * + * @author Karel Goderis - Initial contribution + */ +public class GUIState { + + @SerializedName("gui_24_hour_time") + public boolean gui24HourTime; + @SerializedName("gui_charge_rate_units") + public String guiChargeRateUnits; + @SerializedName("gui_distance_units") + public String guiDistanceUnits; + @SerializedName("gui_range_display") + public String guiRangeDisplay; + @SerializedName("gui_temperature_units") + public String guiTemperatureUnits; + @SerializedName("show_range_units") + public boolean showRangeUnits; + @SerializedName("timestamp") + + public Long timestamp; + + public GUIState() { + } +} diff --git a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/SoftwareUpdate.java b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/SoftwareUpdate.java similarity index 67% rename from bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/SoftwareUpdate.java rename to bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/SoftwareUpdate.java index 8a58aada2a8ec..6e0103f627a3d 100644 --- a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/SoftwareUpdate.java +++ b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/SoftwareUpdate.java @@ -10,7 +10,9 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.tesla.internal.protocol; +package org.openhab.binding.tesla.internal.protocol.dto; + +import com.google.gson.annotations.SerializedName; /** * The {@link SoftwareUpdate} is a datastructure to capture @@ -20,9 +22,12 @@ */ public class SoftwareUpdate { - public int download_perc; - public int expected_duration_sec; - public int install_perc; + @SerializedName("download_perc") + public int downloadPerc; + @SerializedName("expected_duration_sec") + public int expectedDurationSec; + @SerializedName("install_perc") + public int installPerc; public String status; public String version; diff --git a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/Vehicle.java b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/Vehicle.java new file mode 100644 index 0000000000000..5d6908a7388ae --- /dev/null +++ b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/Vehicle.java @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2010-2024 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.protocol.dto; + +import com.google.gson.annotations.SerializedName; + +/** + * The {@link Vehicle} is a datastructure to capture + * variables sent by the Tesla Vehicle + * + * @author Karel Goderis - Initial contribution + */ +public class Vehicle { + + public String color; + @SerializedName("display_name") + public String displayName; + public String id; + @SerializedName("option_codes") + public String optionCodes; + @SerializedName("vehicle_id") + public String vehicleId; + public String vin; + public String[] tokens; + public String state; + @SerializedName("remote_start_enabled") + public boolean remoteStartEnabled; + @SerializedName("calendar_enabled") + public boolean calendarEnabled; + @SerializedName("notifications_enabled") + public boolean notificationsEnabled; + @SerializedName("backseat_token") + public String backseatToken; + @SerializedName("backseat_token_updated_at") + public String backseatTokenUpdatedAt; + + Vehicle() { + } +} diff --git a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/VehicleConfig.java b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/VehicleConfig.java new file mode 100644 index 0000000000000..e1fdf9ecf1d6c --- /dev/null +++ b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/VehicleConfig.java @@ -0,0 +1,92 @@ +/** + * Copyright (c) 2010-2024 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.protocol.dto; + +import org.openhab.binding.tesla.internal.TeslaBindingConstants; +import org.openhab.core.thing.ThingTypeUID; + +import com.google.gson.annotations.SerializedName; + +/** + * The {@link VehicleConfig} is a data structure to capture + * vehicle configuration variables sent by the Tesla Vehicle + * + * @author Dan Cunningham - Initial contribution + */ +public class VehicleConfig { + + @SerializedName("can_accept_navigation_requests") + public boolean canAcceptNavigationRequests; + @SerializedName("can_actuate_trunks") + public boolean canActuateTrunks; + @SerializedName("car_special_type") + public String carSpecialType; + @SerializedName("car_type") + public String carType; + @SerializedName("charge_port_type") + public String chargePortType; + @SerializedName("ece_restrictions") + public boolean eceRestrictions; + @SerializedName("eu_vehicle") + public boolean euVehicle; + @SerializedName("exterior_color") + public String exteriorColor; + @SerializedName("has_air_suspension") + public boolean hasAirSuspension; + @SerializedName("has_ludicrous_mode") + public boolean hasLudicrousMode; + @SerializedName("motorized_charge_port") + public boolean motorizedChargePort; + @SerializedName("plg") + public boolean plg; + @SerializedName("rear_seat_heaters") + public int rearSeatHeaters; + @SerializedName("rear_seat_type") + public int rearSeatType; + @SerializedName("rhd") + public boolean rhd; + @SerializedName("roof_color") + public String roofColor; + @SerializedName("seat_type") + public int seatType; + @SerializedName("spoiler_type") + public String spoilerType; + @SerializedName("sun_roof_installed") + public int sunRoofInstalled; + @SerializedName("third_row_seats") + public String thirdRowSeats; + @SerializedName("timestamp") + public Long timestamp; + @SerializedName("trim_badging") + public String trimBadging; + @SerializedName("use_range_badging") + public boolean useRangeBadging; + @SerializedName("wheel_type") + public String wheelType; + + public ThingTypeUID identifyModel() { + switch (carType) { + case "models": + case "models2": + return TeslaBindingConstants.THING_TYPE_MODELS; + case "modelx": + return TeslaBindingConstants.THING_TYPE_MODELX; + case "model3": + return TeslaBindingConstants.THING_TYPE_MODEL3; + case "modely": + return TeslaBindingConstants.THING_TYPE_MODELY; + default: + return TeslaBindingConstants.THING_TYPE_VEHICLE; + } + } +} diff --git a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/VehicleData.java b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/VehicleData.java new file mode 100644 index 0000000000000..1c941ad86f594 --- /dev/null +++ b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/VehicleData.java @@ -0,0 +1,72 @@ +/** + * Copyright (c) 2010-2024 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.protocol.dto; + +import java.util.List; + +import com.google.gson.annotations.SerializedName; + +/** + * The {@link VehicleData} is a data structure to capture + * variables sent by the Tesla API about a vehicle. + * + * @author Kai Kreuzer - Initial contribution + */ +public class VehicleData { + + @SerializedName("id") + public Long id; + @SerializedName("user_id") + public int userId; + @SerializedName("vehicle_id") + public String vehicleId; + @SerializedName("vin") + public String vin; + @SerializedName("display_name") + public String displayName; + @SerializedName("color") + public Object color; + @SerializedName("access_type") + public String accessType; + @SerializedName("tokens") + public List tokens; + @SerializedName("state") + public String state; + @SerializedName("in_service") + public boolean inService; + @SerializedName("id_s") + public String idS; + @SerializedName("calendar_enabled") + public boolean calendarEnabled; + @SerializedName("api_version") + public int apiVersion; + @SerializedName("backseat_token") + public Object backseatToken; + @SerializedName("backseat_token_updated_at") + public Object backseatTokenUpdatedAt; + @SerializedName("charge_state") + public ChargeState chargeState; + @SerializedName("climate_state") + public ClimateState climateState; + @SerializedName("drive_state") + public DriveState driveState; + @SerializedName("gui_settings") + public GUIState guiSettings; + @SerializedName("vehicle_config") + public VehicleConfig vehicleConfig; + @SerializedName("vehicle_state") + public VehicleState vehicleState; + + VehicleData() { + } +} diff --git a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/VehicleState.java b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/VehicleState.java new file mode 100644 index 0000000000000..7a5e66b61289c --- /dev/null +++ b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/VehicleState.java @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2010-2024 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.protocol.dto; + +import com.google.gson.annotations.SerializedName; + +/** + * The {@link VehicleState} is a datastructure to capture + * variables sent by the Tesla Vehicle + * + * @author Karel Goderis - Initial contribution + */ +public class VehicleState { + + @SerializedName("dark_rims") + public boolean darkRims; + @SerializedName("has_spoiler") + public boolean hasSpoiler; + @SerializedName("homelink_nearby") + public boolean homelinkNearby; + @SerializedName("is_user_present") + public boolean isUserPresent; + public boolean locked; + @SerializedName("notifications_supported") + public boolean notificationsSupported; + @SerializedName("parsed_calendar_supported") + public boolean parsedCalendarSupported; + @SerializedName("remote_start") + public boolean remoteStart; + @SerializedName("remote_start_supported") + public boolean remoteStartSupported; + public boolean rhd; + @SerializedName("sentry_mode") + public boolean sentryMode; + @SerializedName("valet_mode") + public boolean valetMode; + @SerializedName("valet_pin_needed") + public boolean valetPinNeeded; + public float odometer; + @SerializedName("center_display_state") + public int centerDisplayState; + public int df; + public int dr; + public int ft; + public int pf; + public int pr; + @SerializedName("rear_seat_heaters") + public int rearSeatHeaters; + public int rt; + @SerializedName("seat_type") + public int seatType; + @SerializedName("sun_roof_installed") + public int sunRoofInstalled; + @SerializedName("sun_roof_percent_open") + public int sunRoofPercentOpen; + @SerializedName("autopark_state") + public String autoparkState; + @SerializedName("autopark_state_v2") + public String autoparkStateV2; + @SerializedName("autopark_style") + public String autoparkStyle; + @SerializedName("car_version") + public String carVersion; + @SerializedName("exterior_color") + public String exteriorColor; + @SerializedName("last_autopark_error") + public String lastAutoparkError; + @SerializedName("perf_config") + public String perfConfig; + @SerializedName("roof_color") + public String roofColor; + @SerializedName("sun_roof_state") + public String sunRoofState; + @SerializedName("vehicle_name") + public String vehicleName; + @SerializedName("wheel_type") + public String wheelType; + + @SerializedName("software_update") + public SoftwareUpdate softwareUpdate; + + VehicleState() { + } +} diff --git a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/sso/AuthorizationCodeExchangeRequest.java b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/sso/AuthorizationCodeExchangeRequest.java similarity index 67% rename from bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/sso/AuthorizationCodeExchangeRequest.java rename to bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/sso/AuthorizationCodeExchangeRequest.java index 07ac2d4e5b0cc..eba92c637f083 100644 --- a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/sso/AuthorizationCodeExchangeRequest.java +++ b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/sso/AuthorizationCodeExchangeRequest.java @@ -10,10 +10,12 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.tesla.internal.protocol.sso; +package org.openhab.binding.tesla.internal.protocol.dto.sso; import static org.openhab.binding.tesla.internal.TeslaBindingConstants.*; +import com.google.gson.annotations.SerializedName; + /** * The {@link AuthorizationCodeExchangeRequest} is a datastructure to exchange * the authorization code for an access token on the SSO endpoint @@ -22,14 +24,18 @@ */ @SuppressWarnings("unused") // Unused fields must not be removed since they are used for serialization to JSON public class AuthorizationCodeExchangeRequest { - private String grant_type = "authorization_code"; - private String client_id = CLIENT_ID; + @SerializedName("grant_type") + private String grantType = "authorization_code"; + @SerializedName("client_id") + private String clientId = CLIENT_ID; private String code; - private String code_verifier; - private String redirect_uri = URI_CALLBACK; + @SerializedName("code_verifier") + private String codeVerifier; + @SerializedName("redirect_uri") + private String redirectUri = URI_CALLBACK; public AuthorizationCodeExchangeRequest(String code, String codeVerifier) { this.code = code; - this.code_verifier = codeVerifier; + this.codeVerifier = codeVerifier; } } diff --git a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/sso/AuthorizationCodeExchangeResponse.java b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/sso/AuthorizationCodeExchangeResponse.java similarity index 63% rename from bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/sso/AuthorizationCodeExchangeResponse.java rename to bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/sso/AuthorizationCodeExchangeResponse.java index ff767af29c2aa..3eaee54176227 100644 --- a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/sso/AuthorizationCodeExchangeResponse.java +++ b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/sso/AuthorizationCodeExchangeResponse.java @@ -10,7 +10,9 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.tesla.internal.protocol.sso; +package org.openhab.binding.tesla.internal.protocol.dto.sso; + +import com.google.gson.annotations.SerializedName; /** * The {@link AuthorizationCodeExchangeResponse} is a datastructure to capture @@ -19,9 +21,13 @@ * @author Christian Güdel - Initial contribution */ public class AuthorizationCodeExchangeResponse { - public String access_token; - public String refresh_token; - public String expires_in; + @SerializedName("access_token") + public String accessToken; + @SerializedName("refresh_token") + public String refreshToken; + @SerializedName("expires_in") + public String expiresIn; public String state; - public String token_type; + @SerializedName("token_type") + public String tokenType; } diff --git a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/sso/RefreshTokenRequest.java b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/sso/RefreshTokenRequest.java similarity index 62% rename from bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/sso/RefreshTokenRequest.java rename to bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/sso/RefreshTokenRequest.java index 47bb477588573..b307caba78f15 100644 --- a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/sso/RefreshTokenRequest.java +++ b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/sso/RefreshTokenRequest.java @@ -10,10 +10,12 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.tesla.internal.protocol.sso; +package org.openhab.binding.tesla.internal.protocol.dto.sso; import static org.openhab.binding.tesla.internal.TeslaBindingConstants.*; +import com.google.gson.annotations.SerializedName; + /** * The {@link RefreshTokenRequest} is a datastructure to refresh * the access token for the SSO endpoint @@ -21,12 +23,15 @@ * @author Christian Güdel - Initial contribution */ public class RefreshTokenRequest { - public String grant_type = "refresh_token"; - public String client_id = CLIENT_ID; - public String refresh_token; + @SerializedName("grant_type") + public String grantType = "refresh_token"; + @SerializedName("client_id") + public String clientId = CLIENT_ID; + @SerializedName("refresh_token") + public String refreshToken; public String scope = SSO_SCOPES; - public RefreshTokenRequest(String refresh_token) { - this.refresh_token = refresh_token; + public RefreshTokenRequest(String refreshToken) { + this.refreshToken = refreshToken; } } diff --git a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/sso/TokenExchangeRequest.java b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/sso/TokenExchangeRequest.java similarity index 55% rename from bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/sso/TokenExchangeRequest.java rename to bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/sso/TokenExchangeRequest.java index 1835d9d9766b9..23e8f8c68fa5e 100644 --- a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/sso/TokenExchangeRequest.java +++ b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/sso/TokenExchangeRequest.java @@ -10,7 +10,9 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.tesla.internal.protocol.sso; +package org.openhab.binding.tesla.internal.protocol.dto.sso; + +import com.google.gson.annotations.SerializedName; /** * The {@link TokenExchangeRequest} is a datastructure to exchange @@ -19,7 +21,10 @@ * @author Christian Güdel - Initial contribution */ public class TokenExchangeRequest { - public String grant_type = "urn:ietf:params:oauth:grant-type:jwt-bearer"; - public String client_id = "81527cff06843c8634fdc09e8ac0abefb46ac849f38fe1e431c2ef2106796384"; - public String client_secret = "c7257eb71a564034f9419ee651c7d0e5f7aa6bfbd18bafb5c5c033b093bb2fa3"; + @SerializedName("grant_type") + public String grantType = "urn:ietf:params:oauth:grant-type:jwt-bearer"; + @SerializedName("client_id") + public String clientId = "81527cff06843c8634fdc09e8ac0abefb46ac849f38fe1e431c2ef2106796384"; + @SerializedName("client_secret") + public String clientSecret = "c7257eb71a564034f9419ee651c7d0e5f7aa6bfbd18bafb5c5c033b093bb2fa3"; } diff --git a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/sso/TokenResponse.java b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/sso/TokenResponse.java similarity index 58% rename from bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/sso/TokenResponse.java rename to bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/sso/TokenResponse.java index 7fa98aa7534b6..1948c9265ca88 100644 --- a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/sso/TokenResponse.java +++ b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/protocol/dto/sso/TokenResponse.java @@ -10,7 +10,9 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.openhab.binding.tesla.internal.protocol.sso; +package org.openhab.binding.tesla.internal.protocol.dto.sso; + +import com.google.gson.annotations.SerializedName; /** * The {@link TokenResponse} is a datastructure to capture @@ -19,12 +21,16 @@ * @author Nicolai Grødum - Initial contribution */ public class TokenResponse { - - public String access_token; - public String token_type; - public Long expires_in; - public Long created_at; - public String refresh_token; + @SerializedName("access_token") + public String accessToken; + @SerializedName("token_type") + public String tokenType; + @SerializedName("expires_in") + public Long expiresIn; + @SerializedName("created_at") + public Long createdAt; + @SerializedName("refresh_token") + public String refreshToken; public TokenResponse() { } diff --git a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/throttler/AbstractChannelThrottler.java b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/throttler/AbstractChannelThrottler.java index fe477366b6a5d..72c5ef1d50bef 100644 --- a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/throttler/AbstractChannelThrottler.java +++ b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/throttler/AbstractChannelThrottler.java @@ -16,12 +16,16 @@ import java.util.Map; import java.util.concurrent.ScheduledExecutorService; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + /** * The {@link AbstractChannelThrottler} is abstract class implementing a * throttler with one global execution rate, or rate limiter * * @author Karel Goderis - Initial contribution */ +@NonNullByDefault abstract class AbstractChannelThrottler implements ChannelThrottler { protected final Rate totalRate; @@ -37,7 +41,7 @@ protected AbstractChannelThrottler(Rate totalRate, ScheduledExecutorService sche this.timeProvider = timeProvider; } - protected synchronized long callTime(Rate channel) { + protected synchronized long callTime(@Nullable Rate channel) { long now = timeProvider.getCurrentTimeInMillis(); long callTime = totalRate.callTime(now); if (channel != null) { diff --git a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/throttler/AbstractMultiRateChannelThrottler.java b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/throttler/AbstractMultiRateChannelThrottler.java index 2798dbaad469c..90835d730254b 100644 --- a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/throttler/AbstractMultiRateChannelThrottler.java +++ b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/throttler/AbstractMultiRateChannelThrottler.java @@ -18,12 +18,16 @@ import java.util.Map; import java.util.concurrent.ScheduledExecutorService; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + /** * The {@link AbstractMultiRateChannelThrottler} is abstract class implementing * a throttler with multiple global execution rates, or rate limiters * * @author Karel Goderis - Initial contribution */ +@NonNullByDefault abstract class AbstractMultiRateChannelThrottler implements ChannelThrottler { protected final TimeProvider timeProvider; @@ -43,7 +47,7 @@ public synchronized void addRate(Rate rate) { this.rates.add(rate); } - protected synchronized long callTime(Rate channel) { + protected synchronized long callTime(@Nullable Rate channel) { long maxCallTime = 0; long finalCallTime = 0; long now = timeProvider.getCurrentTimeInMillis(); diff --git a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/throttler/ChannelThrottler.java b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/throttler/ChannelThrottler.java index 8e26c2e747878..f5e8e62751854 100644 --- a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/throttler/ChannelThrottler.java +++ b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/throttler/ChannelThrottler.java @@ -14,14 +14,20 @@ import java.util.concurrent.Future; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + /** * The {@link ChannelThrottler} defines the interface for to submit tasks to a * throttler * * @author Karel Goderis - Initial contribution */ +@NonNullByDefault public interface ChannelThrottler { + @Nullable Future submit(Runnable task); + @Nullable Future submit(Object channelKey, Runnable task); } diff --git a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/throttler/QueueChannelThrottler.java b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/throttler/QueueChannelThrottler.java index 14ab02885f280..766c620187087 100644 --- a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/throttler/QueueChannelThrottler.java +++ b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/throttler/QueueChannelThrottler.java @@ -22,6 +22,8 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,6 +33,7 @@ * * @author Karel Goderis - Initial contribution */ +@NonNullByDefault public final class QueueChannelThrottler extends AbstractMultiRateChannelThrottler { private final Logger logger = LoggerFactory.getLogger(QueueChannelThrottler.class); @@ -71,13 +74,13 @@ public QueueChannelThrottler(Rate someRate, ScheduledExecutorService scheduler, } @Override - public Future submit(Runnable task) { + public @Nullable Future submit(Runnable task) { return submit(null, task); } @SuppressWarnings({ "unchecked", "rawtypes" }) @Override - public Future submit(Object channelKey, Runnable task) { + public @Nullable Future submit(@Nullable Object channelKey, Runnable task) { FutureTask runTask = new FutureTask(task, null); try { if (tasks.offer(runTask, overallRate.timeInMillis(), TimeUnit.MILLISECONDS)) { diff --git a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/throttler/Rate.java b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/throttler/Rate.java index 8ccf681f18418..c45866430ccba 100644 --- a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/throttler/Rate.java +++ b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/throttler/Rate.java @@ -17,6 +17,8 @@ import java.util.ListIterator; import java.util.concurrent.TimeUnit; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * The {@link Rate} defines a rate limiter that accepts a number of calls to be * executed in a given time length. If the quota of calls is used, then calls @@ -24,6 +26,7 @@ * * @author Karel Goderis - Initial contribution */ +@NonNullByDefault public final class Rate { private final int numberCalls; diff --git a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/throttler/ScheduledChannelThrottler.java b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/throttler/ScheduledChannelThrottler.java index 6e357a225f31a..6873cc59efdb6 100644 --- a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/throttler/ScheduledChannelThrottler.java +++ b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/throttler/ScheduledChannelThrottler.java @@ -19,6 +19,9 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + /** * The {@link ScheduledChannelThrottler} implements a throttler that maintains a * single execution rates, and does not maintain order of calls (thus has to @@ -26,6 +29,7 @@ * * @author Karel Goderis - Initial contribution */ +@NonNullByDefault public final class ScheduledChannelThrottler extends AbstractChannelThrottler { public ScheduledChannelThrottler(Rate totalRate) { @@ -53,13 +57,13 @@ public void submitSync(Runnable task) throws InterruptedException { } @Override - public Future submit(Runnable task) { + public @Nullable Future submit(Runnable task) { long delay = callTime(null) - timeProvider.getCurrentTimeInMillis(); return scheduler.schedule(task, delay < 0 ? 0 : delay, TimeUnit.MILLISECONDS); } @Override - public Future submit(Object channelKey, Runnable task) { + public @Nullable Future submit(Object channelKey, Runnable task) { return scheduler.schedule(task, getThrottleDelay(channelKey), TimeUnit.MILLISECONDS); } } diff --git a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/throttler/TimeProvider.java b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/throttler/TimeProvider.java index bf4dbc28fa41f..386fbcb34b7c8 100644 --- a/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/throttler/TimeProvider.java +++ b/bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/throttler/TimeProvider.java @@ -12,11 +12,14 @@ */ package org.openhab.binding.tesla.internal.throttler; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * The {@link TimeProvider} provides time stamps * * @author Karel Goderis - Initial contribution */ +@NonNullByDefault public interface TimeProvider { static final TimeProvider SYSTEM_PROVIDER = new TimeProvider() { @Override