Skip to content

Commit

Permalink
[tesla] Add null annotations (openhab#17582)
Browse files Browse the repository at this point in the history
Signed-off-by: Leo Siepel <[email protected]>
  • Loading branch information
lsiepel authored Dec 8, 2024
1 parent 931f39f commit f39415b
Show file tree
Hide file tree
Showing 37 changed files with 981 additions and 665 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -33,13 +35,15 @@
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
* and parameterize the {@link TeslaChannelSelector} Enum
*
* @author Karel Goderis - Initial contribution
*/
@NonNullByDefault
public class TeslaChannelSelectorProxy {

public enum TeslaChannelSelector {
Expand Down Expand Up @@ -939,11 +943,11 @@ public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, Str
@Override
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> 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;
}
}
},
Expand Down Expand Up @@ -1062,12 +1066,12 @@ public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, Str
},
WHEEL_TYPE("wheel_type", "wheeltype", StringType.class, true);

private final String restID;
private final @Nullable String restID;
private final String channelID;
private Class<? extends Type> typeClass;
private final boolean isProperty;

private TeslaChannelSelector(String restID, String channelID, Class<? extends Type> typeClass,
private TeslaChannelSelector(@Nullable String restID, String channelID, Class<? extends Type> typeClass,
boolean isProperty) {
this.restID = restID;
this.channelID = channelID;
Expand All @@ -1077,7 +1081,8 @@ private TeslaChannelSelector(String restID, String channelID, Class<? extends Ty

@Override
public String toString() {
return restID;
String restID = this.restID;
return restID != null ? restID : "null";
}

public String getChannelID() {
Expand Down Expand Up @@ -1107,7 +1112,7 @@ public State getState(String s) {
| InvocationTargetException e) {
}

return null;
return UnDefType.UNDEF;
}

public static TeslaChannelSelector getValueSelectorFromChannelID(String valueSelectorText)
Expand All @@ -1124,7 +1129,8 @@ public static TeslaChannelSelector getValueSelectorFromChannelID(String valueSel
public static TeslaChannelSelector getValueSelectorFromRESTID(String valueSelectorText)
throws IllegalArgumentException {
for (TeslaChannelSelector c : TeslaChannelSelector.values()) {
if (c.restID != null && c.restID.equals(valueSelectorText)) {
String restID = c.restID;
if (restID != null && restID.equals(valueSelectorText)) {
return c;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@
*/
package org.openhab.binding.tesla.internal.discovery;

import org.eclipse.jdt.annotation.NonNull;
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.TeslaHandlerFactory;
import org.openhab.binding.tesla.internal.handler.TeslaAccountHandler;
import org.openhab.binding.tesla.internal.handler.VehicleListener;
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;
import org.openhab.core.config.discovery.AbstractThingHandlerDiscoveryService;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
Expand All @@ -36,8 +37,9 @@
* @author Kai Kreuzer - Initial contribution
*
*/
@NonNullByDefault
@Component(scope = ServiceScope.PROTOTYPE, service = TeslaVehicleDiscoveryService.class)
public class TeslaVehicleDiscoveryService extends AbstractThingHandlerDiscoveryService<@NonNull TeslaAccountHandler>
public class TeslaVehicleDiscoveryService extends AbstractThingHandlerDiscoveryService<TeslaAccountHandler>
implements VehicleListener {
private final Logger logger = LoggerFactory.getLogger(TeslaVehicleDiscoveryService.class);

Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -86,17 +89,20 @@ public class TeslaAccountHandler extends BaseBridgeHandler {
private final ThingTypeMigrationService thingTypeMigrationService;

// Threading and Job related variables
@Nullable
protected ScheduledFuture<?> connectJob;

protected long lastTimeStamp;
protected long apiIntervalTimestamp;
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<VehicleListener> vehicleListeners = new HashSet<>();

Expand All @@ -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;
}
}

Expand All @@ -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) {
Expand Down Expand Up @@ -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);
Expand All @@ -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);
}
}
Expand All @@ -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));

Expand Down Expand Up @@ -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) {
Expand All @@ -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
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand All @@ -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);
}

Expand Down
Loading

0 comments on commit f39415b

Please sign in to comment.