31
31
import javax .ws .rs .core .MediaType ;
32
32
import javax .ws .rs .core .Response ;
33
33
34
+ import org .eclipse .jdt .annotation .NonNullByDefault ;
35
+ import org .eclipse .jdt .annotation .Nullable ;
34
36
import org .openhab .binding .tesla .internal .TeslaBindingConstants ;
35
37
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 ;
40
42
import org .openhab .core .io .net .http .HttpClientFactory ;
41
43
import org .openhab .core .thing .Bridge ;
42
44
import org .openhab .core .thing .ChannelUID ;
63
65
* @author Nicolai Grødum - Adding token based auth
64
66
* @author Kai Kreuzer - refactored to use separate vehicle handlers
65
67
*/
68
+ @ NonNullByDefault
66
69
public class TeslaAccountHandler extends BaseBridgeHandler {
67
70
68
71
public static final int API_MAXIMUM_ERRORS_IN_INTERVAL = 3 ;
@@ -86,17 +89,20 @@ public class TeslaAccountHandler extends BaseBridgeHandler {
86
89
private final ThingTypeMigrationService thingTypeMigrationService ;
87
90
88
91
// Threading and Job related variables
92
+ @ Nullable
89
93
protected ScheduledFuture <?> connectJob ;
90
94
91
95
protected long lastTimeStamp ;
92
96
protected long apiIntervalTimestamp ;
93
97
protected int apiIntervalErrors ;
94
98
protected long eventIntervalTimestamp ;
95
99
protected int eventIntervalErrors ;
96
- protected ReentrantLock lock ;
100
+
101
+ protected ReentrantLock lock = new ReentrantLock ();
97
102
98
103
private final Gson gson = new Gson ();
99
104
105
+ @ Nullable
100
106
private TokenResponse logonToken ;
101
107
private final Set <VehicleListener > vehicleListeners = new HashSet <>();
102
108
@@ -122,31 +128,17 @@ public void initialize() {
122
128
123
129
updateStatus (ThingStatus .UNKNOWN );
124
130
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 );
136
133
}
137
134
138
135
@ Override
139
136
public void dispose () {
140
137
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 ;
150
142
}
151
143
}
152
144
@@ -167,19 +159,25 @@ public void handleCommand(ChannelUID channelUID, Command command) {
167
159
// we do not have any channels -> nothing to do here
168
160
}
169
161
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 ;
173
166
} else {
174
167
return null ;
175
168
}
176
169
}
177
170
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
+ }
180
178
}
181
179
182
- protected boolean checkResponse (Response response , boolean immediatelyFail ) {
180
+ protected boolean checkResponse (@ Nullable Response response , boolean immediatelyFail ) {
183
181
if (response != null && response .getStatus () == 200 ) {
184
182
return true ;
185
183
} else if (response != null && response .getStatus () == 401 ) {
@@ -221,17 +219,23 @@ protected Vehicle[] queryVehicles() {
221
219
222
220
if (!checkResponse (response , true )) {
223
221
logger .debug ("An error occurred while querying the vehicle" );
224
- return null ;
222
+ return new Vehicle [ 0 ] ;
225
223
}
226
224
227
225
JsonObject jsonObject = JsonParser .parseString (response .readEntity (String .class )).getAsJsonObject ();
228
226
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
+ }
230
231
for (Vehicle vehicle : vehicleArray ) {
231
232
String responseString = invokeAndParse (vehicle .id , null , null , dataRequestTarget , 0 );
232
233
VehicleConfig vehicleConfig = null ;
233
234
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
+ }
235
239
}
236
240
for (VehicleListener listener : vehicleListeners ) {
237
241
listener .vehicleFound (vehicle , vehicleConfig );
@@ -251,7 +255,7 @@ protected Vehicle[] queryVehicles() {
251
255
logger .debug ("Querying the vehicle: VIN {}" , vehicle .vin );
252
256
String vehicleJSON = gson .toJson (vehicle );
253
257
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 ,
255
259
vehicle .tokens );
256
260
}
257
261
}
@@ -274,8 +278,8 @@ ThingStatusInfo authenticate() {
274
278
logger .debug ("Current authentication time {}" , DATE_FORMATTER .format (Instant .now ()));
275
279
276
280
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 );
279
283
logger .debug ("Found a request token from {}" , DATE_FORMATTER .format (tokenCreationInstant ));
280
284
logger .debug ("Access token expiration time {}" , DATE_FORMATTER .format (tokenExpiresInstant ));
281
285
@@ -306,8 +310,8 @@ ThingStatusInfo authenticate() {
306
310
return new ThingStatusInfo (ThingStatus .ONLINE , ThingStatusDetail .NONE , null );
307
311
}
308
312
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 ) {
311
315
logger .debug ("Invoking: {}" , command );
312
316
313
317
if (vehicleId != null ) {
@@ -316,26 +320,29 @@ protected String invokeAndParse(String vehicleId, String command, String payLoad
316
320
if (payLoad != null ) {
317
321
if (command != null ) {
318
322
response = target .resolveTemplate ("cmd" , command ).resolveTemplate ("vid" , vehicleId ).request ()
319
- .header ("Authorization" , "Bearer " + logonToken . access_token )
323
+ .header ("Authorization" , getAuthHeader () )
320
324
.post (Entity .entity (payLoad , MediaType .APPLICATION_JSON_TYPE ));
321
325
} else {
322
326
response = target .resolveTemplate ("vid" , vehicleId ).request ()
323
- .header ("Authorization" , "Bearer " + logonToken . access_token )
327
+ .header ("Authorization" , getAuthHeader () )
324
328
.post (Entity .entity (payLoad , MediaType .APPLICATION_JSON_TYPE ));
325
329
}
326
330
} else if (command != null ) {
327
331
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 ();
330
333
} else {
331
334
response = target .resolveTemplate ("vid" , vehicleId ).request (MediaType .APPLICATION_JSON_TYPE )
332
- .header ("Authorization" , "Bearer " + logonToken . access_token ).get ();
335
+ .header ("Authorization" , getAuthHeader () ).get ();
333
336
}
334
337
335
338
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
+ }
336
344
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 ());
339
346
if (response .getStatus () == 408 && noOfretries > 0 ) {
340
347
try {
341
348
// 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
377
384
if (authenticationResult .getStatus () == ThingStatus .ONLINE ) {
378
385
// get a list of vehicles
379
386
Response response = productsTarget .request (MediaType .APPLICATION_JSON_TYPE )
380
- .header ("Authorization" , "Bearer " + logonToken . access_token ).get ();
387
+ .header ("Authorization" , getAuthHeader () ).get ();
381
388
382
389
if (response != null && response .getStatus () == 200 && response .hasEntity ()) {
383
390
updateStatus (ThingStatus .ONLINE );
@@ -436,11 +443,12 @@ protected class Request implements Runnable {
436
443
437
444
private TeslaVehicleHandler handler ;
438
445
private String request ;
446
+ @ Nullable
439
447
private String payLoad ;
440
448
private WebTarget target ;
441
449
private boolean allowWakeUpForCommands ;
442
450
443
- public Request (TeslaVehicleHandler handler , String request , String payLoad , WebTarget target ,
451
+ public Request (TeslaVehicleHandler handler , String request , @ Nullable String payLoad , WebTarget target ,
444
452
boolean allowWakeUpForCommands ) {
445
453
this .handler = handler ;
446
454
this .request = request ;
@@ -467,8 +475,8 @@ public void run() {
467
475
}
468
476
}
469
477
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 ) {
472
480
return new Request (teslaVehicleHandler , command , payLoad , target , allowWakeUpForCommands );
473
481
}
474
482
0 commit comments