Skip to content

Commit f39415b

Browse files
authored
[tesla] Add null annotations (openhab#17582)
Signed-off-by: Leo Siepel <[email protected]>
1 parent 931f39f commit f39415b

37 files changed

+981
-665
lines changed

bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/TeslaChannelSelectorProxy.java

+13-7
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import java.util.Date;
2020
import java.util.Map;
2121

22+
import org.eclipse.jdt.annotation.NonNullByDefault;
23+
import org.eclipse.jdt.annotation.Nullable;
2224
import org.openhab.core.library.types.DateTimeType;
2325
import org.openhab.core.library.types.DecimalType;
2426
import org.openhab.core.library.types.OnOffType;
@@ -33,13 +35,15 @@
3335
import org.openhab.core.library.unit.Units;
3436
import org.openhab.core.types.State;
3537
import org.openhab.core.types.Type;
38+
import org.openhab.core.types.UnDefType;
3639

3740
/**
3841
* The {@link TeslaChannelSelectorProxy} class is a helper class to instantiate
3942
* and parameterize the {@link TeslaChannelSelector} Enum
4043
*
4144
* @author Karel Goderis - Initial contribution
4245
*/
46+
@NonNullByDefault
4347
public class TeslaChannelSelectorProxy {
4448

4549
public enum TeslaChannelSelector {
@@ -939,11 +943,11 @@ public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, Str
939943
@Override
940944
public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, String> properties) {
941945
State someState = super.getState(s);
942-
if (someState != null) {
946+
if (someState != UnDefType.UNDEF) {
943947
BigDecimal value = ((DecimalType) someState).toBigDecimal();
944948
return new QuantityType<>(value, ImperialUnits.MILES_PER_HOUR);
945949
} else {
946-
return null;
950+
return UnDefType.UNDEF;
947951
}
948952
}
949953
},
@@ -1062,12 +1066,12 @@ public State getState(String s, TeslaChannelSelectorProxy proxy, Map<String, Str
10621066
},
10631067
WHEEL_TYPE("wheel_type", "wheeltype", StringType.class, true);
10641068

1065-
private final String restID;
1069+
private final @Nullable String restID;
10661070
private final String channelID;
10671071
private Class<? extends Type> typeClass;
10681072
private final boolean isProperty;
10691073

1070-
private TeslaChannelSelector(String restID, String channelID, Class<? extends Type> typeClass,
1074+
private TeslaChannelSelector(@Nullable String restID, String channelID, Class<? extends Type> typeClass,
10711075
boolean isProperty) {
10721076
this.restID = restID;
10731077
this.channelID = channelID;
@@ -1077,7 +1081,8 @@ private TeslaChannelSelector(String restID, String channelID, Class<? extends Ty
10771081

10781082
@Override
10791083
public String toString() {
1080-
return restID;
1084+
String restID = this.restID;
1085+
return restID != null ? restID : "null";
10811086
}
10821087

10831088
public String getChannelID() {
@@ -1107,7 +1112,7 @@ public State getState(String s) {
11071112
| InvocationTargetException e) {
11081113
}
11091114

1110-
return null;
1115+
return UnDefType.UNDEF;
11111116
}
11121117

11131118
public static TeslaChannelSelector getValueSelectorFromChannelID(String valueSelectorText)
@@ -1124,7 +1129,8 @@ public static TeslaChannelSelector getValueSelectorFromChannelID(String valueSel
11241129
public static TeslaChannelSelector getValueSelectorFromRESTID(String valueSelectorText)
11251130
throws IllegalArgumentException {
11261131
for (TeslaChannelSelector c : TeslaChannelSelector.values()) {
1127-
if (c.restID != null && c.restID.equals(valueSelectorText)) {
1132+
String restID = c.restID;
1133+
if (restID != null && restID.equals(valueSelectorText)) {
11281134
return c;
11291135
}
11301136
}

bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/discovery/TeslaVehicleDiscoveryService.java

+8-6
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,14 @@
1212
*/
1313
package org.openhab.binding.tesla.internal.discovery;
1414

15-
import org.eclipse.jdt.annotation.NonNull;
15+
import org.eclipse.jdt.annotation.NonNullByDefault;
16+
import org.eclipse.jdt.annotation.Nullable;
1617
import org.openhab.binding.tesla.internal.TeslaBindingConstants;
1718
import org.openhab.binding.tesla.internal.TeslaHandlerFactory;
1819
import org.openhab.binding.tesla.internal.handler.TeslaAccountHandler;
1920
import org.openhab.binding.tesla.internal.handler.VehicleListener;
20-
import org.openhab.binding.tesla.internal.protocol.Vehicle;
21-
import org.openhab.binding.tesla.internal.protocol.VehicleConfig;
21+
import org.openhab.binding.tesla.internal.protocol.dto.Vehicle;
22+
import org.openhab.binding.tesla.internal.protocol.dto.VehicleConfig;
2223
import org.openhab.core.config.discovery.AbstractThingHandlerDiscoveryService;
2324
import org.openhab.core.config.discovery.DiscoveryResult;
2425
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
@@ -36,8 +37,9 @@
3637
* @author Kai Kreuzer - Initial contribution
3738
*
3839
*/
40+
@NonNullByDefault
3941
@Component(scope = ServiceScope.PROTOTYPE, service = TeslaVehicleDiscoveryService.class)
40-
public class TeslaVehicleDiscoveryService extends AbstractThingHandlerDiscoveryService<@NonNull TeslaAccountHandler>
42+
public class TeslaVehicleDiscoveryService extends AbstractThingHandlerDiscoveryService<TeslaAccountHandler>
4143
implements VehicleListener {
4244
private final Logger logger = LoggerFactory.getLogger(TeslaVehicleDiscoveryService.class);
4345

@@ -63,13 +65,13 @@ public void dispose() {
6365
}
6466

6567
@Override
66-
public void vehicleFound(Vehicle vehicle, VehicleConfig vehicleConfig) {
68+
public void vehicleFound(Vehicle vehicle, @Nullable VehicleConfig vehicleConfig) {
6769
ThingTypeUID type = vehicleConfig == null ? TeslaBindingConstants.THING_TYPE_VEHICLE
6870
: vehicleConfig.identifyModel();
6971
if (type != null) {
7072
logger.debug("Found a {} vehicle", type.getId());
7173
ThingUID thingUID = new ThingUID(type, thingHandler.getThing().getUID(), vehicle.vin);
72-
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withLabel(vehicle.display_name)
74+
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withLabel(vehicle.displayName)
7375
.withBridge(thingHandler.getThing().getUID()).withProperty(TeslaBindingConstants.VIN, vehicle.vin)
7476
.build();
7577
thingDiscovered(discoveryResult);

bundles/org.openhab.binding.tesla/src/main/java/org/openhab/binding/tesla/internal/handler/TeslaAccountHandler.java

+58-50
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,14 @@
3131
import javax.ws.rs.core.MediaType;
3232
import javax.ws.rs.core.Response;
3333

34+
import org.eclipse.jdt.annotation.NonNullByDefault;
35+
import org.eclipse.jdt.annotation.Nullable;
3436
import org.openhab.binding.tesla.internal.TeslaBindingConstants;
3537
import org.openhab.binding.tesla.internal.discovery.TeslaVehicleDiscoveryService;
36-
import org.openhab.binding.tesla.internal.protocol.Vehicle;
37-
import org.openhab.binding.tesla.internal.protocol.VehicleConfig;
38-
import org.openhab.binding.tesla.internal.protocol.VehicleData;
39-
import org.openhab.binding.tesla.internal.protocol.sso.TokenResponse;
38+
import org.openhab.binding.tesla.internal.protocol.dto.Vehicle;
39+
import org.openhab.binding.tesla.internal.protocol.dto.VehicleConfig;
40+
import org.openhab.binding.tesla.internal.protocol.dto.VehicleData;
41+
import org.openhab.binding.tesla.internal.protocol.dto.sso.TokenResponse;
4042
import org.openhab.core.io.net.http.HttpClientFactory;
4143
import org.openhab.core.thing.Bridge;
4244
import org.openhab.core.thing.ChannelUID;
@@ -63,6 +65,7 @@
6365
* @author Nicolai Grødum - Adding token based auth
6466
* @author Kai Kreuzer - refactored to use separate vehicle handlers
6567
*/
68+
@NonNullByDefault
6669
public class TeslaAccountHandler extends BaseBridgeHandler {
6770

6871
public static final int API_MAXIMUM_ERRORS_IN_INTERVAL = 3;
@@ -86,17 +89,20 @@ public class TeslaAccountHandler extends BaseBridgeHandler {
8689
private final ThingTypeMigrationService thingTypeMigrationService;
8790

8891
// Threading and Job related variables
92+
@Nullable
8993
protected ScheduledFuture<?> connectJob;
9094

9195
protected long lastTimeStamp;
9296
protected long apiIntervalTimestamp;
9397
protected int apiIntervalErrors;
9498
protected long eventIntervalTimestamp;
9599
protected int eventIntervalErrors;
96-
protected ReentrantLock lock;
100+
101+
protected ReentrantLock lock = new ReentrantLock();
97102

98103
private final Gson gson = new Gson();
99104

105+
@Nullable
100106
private TokenResponse logonToken;
101107
private final Set<VehicleListener> vehicleListeners = new HashSet<>();
102108

@@ -122,31 +128,17 @@ public void initialize() {
122128

123129
updateStatus(ThingStatus.UNKNOWN);
124130

125-
lock = new ReentrantLock();
126-
lock.lock();
127-
128-
try {
129-
if (connectJob == null || connectJob.isCancelled()) {
130-
connectJob = scheduler.scheduleWithFixedDelay(connectRunnable, 0, CONNECT_RETRY_INTERVAL,
131-
TimeUnit.MILLISECONDS);
132-
}
133-
} finally {
134-
lock.unlock();
135-
}
131+
connectJob = scheduler.scheduleWithFixedDelay(connectRunnable, 0, CONNECT_RETRY_INTERVAL,
132+
TimeUnit.MILLISECONDS);
136133
}
137134

138135
@Override
139136
public void dispose() {
140137
logger.debug("Disposing the Tesla account handler for {}", getThing().getUID());
141-
142-
lock.lock();
143-
try {
144-
if (connectJob != null && !connectJob.isCancelled()) {
145-
connectJob.cancel(true);
146-
connectJob = null;
147-
}
148-
} finally {
149-
lock.unlock();
138+
ScheduledFuture<?> connectJob = this.connectJob;
139+
if (connectJob != null && !connectJob.isCancelled()) {
140+
connectJob.cancel(true);
141+
this.connectJob = null;
150142
}
151143
}
152144

@@ -167,19 +159,25 @@ public void handleCommand(ChannelUID channelUID, Command command) {
167159
// we do not have any channels -> nothing to do here
168160
}
169161

170-
public String getAuthHeader() {
171-
if (logonToken != null) {
172-
return "Bearer " + logonToken.access_token;
162+
public @Nullable String getAuthHeader() {
163+
String accessToken = getAccessToken();
164+
if (accessToken != null) {
165+
return "Bearer " + accessToken;
173166
} else {
174167
return null;
175168
}
176169
}
177170

178-
public String getAccessToken() {
179-
return logonToken.access_token;
171+
public @Nullable String getAccessToken() {
172+
TokenResponse logonToken = this.logonToken;
173+
if (logonToken != null) {
174+
return logonToken.accessToken;
175+
} else {
176+
return null;
177+
}
180178
}
181179

182-
protected boolean checkResponse(Response response, boolean immediatelyFail) {
180+
protected boolean checkResponse(@Nullable Response response, boolean immediatelyFail) {
183181
if (response != null && response.getStatus() == 200) {
184182
return true;
185183
} else if (response != null && response.getStatus() == 401) {
@@ -221,17 +219,23 @@ protected Vehicle[] queryVehicles() {
221219

222220
if (!checkResponse(response, true)) {
223221
logger.debug("An error occurred while querying the vehicle");
224-
return null;
222+
return new Vehicle[0];
225223
}
226224

227225
JsonObject jsonObject = JsonParser.parseString(response.readEntity(String.class)).getAsJsonObject();
228226
Vehicle[] vehicleArray = gson.fromJson(jsonObject.getAsJsonArray("response"), Vehicle[].class);
229-
227+
if (vehicleArray == null) {
228+
logger.debug("Response resulted in unexpected null array");
229+
return new Vehicle[0];
230+
}
230231
for (Vehicle vehicle : vehicleArray) {
231232
String responseString = invokeAndParse(vehicle.id, null, null, dataRequestTarget, 0);
232233
VehicleConfig vehicleConfig = null;
233234
if (responseString != null && !responseString.isBlank()) {
234-
vehicleConfig = gson.fromJson(responseString, VehicleData.class).vehicle_config;
235+
VehicleData vehicleData = gson.fromJson(responseString, VehicleData.class);
236+
if (vehicleData != null) {
237+
vehicleConfig = vehicleData.vehicleConfig;
238+
}
235239
}
236240
for (VehicleListener listener : vehicleListeners) {
237241
listener.vehicleFound(vehicle, vehicleConfig);
@@ -251,7 +255,7 @@ protected Vehicle[] queryVehicles() {
251255
logger.debug("Querying the vehicle: VIN {}", vehicle.vin);
252256
String vehicleJSON = gson.toJson(vehicle);
253257
vehicleHandler.parseAndUpdate("queryVehicle", null, vehicleJSON);
254-
logger.trace("Vehicle is id {}/vehicle_id {}/tokens {}", vehicle.id, vehicle.vehicle_id,
258+
logger.trace("Vehicle is id {}/vehicle_id {}/tokens {}", vehicle.id, vehicle.vehicleId,
255259
vehicle.tokens);
256260
}
257261
}
@@ -274,8 +278,8 @@ ThingStatusInfo authenticate() {
274278
logger.debug("Current authentication time {}", DATE_FORMATTER.format(Instant.now()));
275279

276280
if (token != null) {
277-
Instant tokenCreationInstant = Instant.ofEpochMilli(token.created_at * 1000);
278-
Instant tokenExpiresInstant = Instant.ofEpochMilli((token.created_at + token.expires_in) * 1000);
281+
Instant tokenCreationInstant = Instant.ofEpochMilli(token.createdAt * 1000);
282+
Instant tokenExpiresInstant = Instant.ofEpochMilli((token.createdAt + token.expiresIn) * 1000);
279283
logger.debug("Found a request token from {}", DATE_FORMATTER.format(tokenCreationInstant));
280284
logger.debug("Access token expiration time {}", DATE_FORMATTER.format(tokenExpiresInstant));
281285

@@ -306,8 +310,8 @@ ThingStatusInfo authenticate() {
306310
return new ThingStatusInfo(ThingStatus.ONLINE, ThingStatusDetail.NONE, null);
307311
}
308312

309-
protected String invokeAndParse(String vehicleId, String command, String payLoad, WebTarget target,
310-
int noOfretries) {
313+
protected @Nullable String invokeAndParse(@Nullable String vehicleId, @Nullable String command,
314+
@Nullable String payLoad, WebTarget target, int noOfretries) {
311315
logger.debug("Invoking: {}", command);
312316

313317
if (vehicleId != null) {
@@ -316,26 +320,29 @@ protected String invokeAndParse(String vehicleId, String command, String payLoad
316320
if (payLoad != null) {
317321
if (command != null) {
318322
response = target.resolveTemplate("cmd", command).resolveTemplate("vid", vehicleId).request()
319-
.header("Authorization", "Bearer " + logonToken.access_token)
323+
.header("Authorization", getAuthHeader())
320324
.post(Entity.entity(payLoad, MediaType.APPLICATION_JSON_TYPE));
321325
} else {
322326
response = target.resolveTemplate("vid", vehicleId).request()
323-
.header("Authorization", "Bearer " + logonToken.access_token)
327+
.header("Authorization", getAuthHeader())
324328
.post(Entity.entity(payLoad, MediaType.APPLICATION_JSON_TYPE));
325329
}
326330
} else if (command != null) {
327331
response = target.resolveTemplate("cmd", command).resolveTemplate("vid", vehicleId)
328-
.request(MediaType.APPLICATION_JSON_TYPE)
329-
.header("Authorization", "Bearer " + logonToken.access_token).get();
332+
.request(MediaType.APPLICATION_JSON_TYPE).header("Authorization", getAuthHeader()).get();
330333
} else {
331334
response = target.resolveTemplate("vid", vehicleId).request(MediaType.APPLICATION_JSON_TYPE)
332-
.header("Authorization", "Bearer " + logonToken.access_token).get();
335+
.header("Authorization", getAuthHeader()).get();
333336
}
334337

335338
if (!checkResponse(response, false)) {
339+
if (response == null) {
340+
logger.debug(
341+
"An error occurred while communicating with the vehicle during request, the response was null");
342+
return null;
343+
}
336344
logger.debug("An error occurred while communicating with the vehicle during request {}: {}: {}",
337-
command, (response != null) ? response.getStatus() : "",
338-
(response != null) ? response.getStatusInfo().getReasonPhrase() : "No Response");
345+
command, response.getStatus(), response.getStatusInfo().getReasonPhrase());
339346
if (response.getStatus() == 408 && noOfretries > 0) {
340347
try {
341348
// 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
377384
if (authenticationResult.getStatus() == ThingStatus.ONLINE) {
378385
// get a list of vehicles
379386
Response response = productsTarget.request(MediaType.APPLICATION_JSON_TYPE)
380-
.header("Authorization", "Bearer " + logonToken.access_token).get();
387+
.header("Authorization", getAuthHeader()).get();
381388

382389
if (response != null && response.getStatus() == 200 && response.hasEntity()) {
383390
updateStatus(ThingStatus.ONLINE);
@@ -436,11 +443,12 @@ protected class Request implements Runnable {
436443

437444
private TeslaVehicleHandler handler;
438445
private String request;
446+
@Nullable
439447
private String payLoad;
440448
private WebTarget target;
441449
private boolean allowWakeUpForCommands;
442450

443-
public Request(TeslaVehicleHandler handler, String request, String payLoad, WebTarget target,
451+
public Request(TeslaVehicleHandler handler, String request, @Nullable String payLoad, WebTarget target,
444452
boolean allowWakeUpForCommands) {
445453
this.handler = handler;
446454
this.request = request;
@@ -467,8 +475,8 @@ public void run() {
467475
}
468476
}
469477

470-
public Request newRequest(TeslaVehicleHandler teslaVehicleHandler, String command, String payLoad, WebTarget target,
471-
boolean allowWakeUpForCommands) {
478+
public Request newRequest(TeslaVehicleHandler teslaVehicleHandler, String command, @Nullable String payLoad,
479+
WebTarget target, boolean allowWakeUpForCommands) {
472480
return new Request(teslaVehicleHandler, command, payLoad, target, allowWakeUpForCommands);
473481
}
474482

0 commit comments

Comments
 (0)