13
13
package org .openhab .binding .linktap .internal ;
14
14
15
15
import static org .openhab .binding .linktap .internal .LinkTapBindingConstants .*;
16
+ import static org .openhab .binding .linktap .internal .TransactionProcessor .MAX_COMMAND_RETRIES ;
17
+ import static org .openhab .binding .linktap .protocol .frames .GatewayDeviceResponse .ResultStatus .RET_GATEWAY_BUSY ;
18
+ import static org .openhab .binding .linktap .protocol .frames .GatewayDeviceResponse .ResultStatus .RET_GW_INTERNAL_ERR ;
16
19
import static org .openhab .binding .linktap .protocol .frames .TLGatewayFrame .*;
17
20
import static org .openhab .binding .linktap .protocol .frames .ValidationError .Cause .BUG ;
18
21
import static org .openhab .binding .linktap .protocol .frames .ValidationError .Cause .USER ;
22
+ import static org .openhab .binding .linktap .protocol .http .TransientCommunicationIssueException .TransientExecptionDefinitions .GATEWAY_BUSY ;
19
23
20
24
import java .io .IOException ;
21
25
import java .net .InetAddress ;
@@ -105,6 +109,7 @@ public class LinkTapBridgeHandler extends BaseBridgeHandler {
105
109
private volatile LinkTapBridgeConfiguration config = new LinkTapBridgeConfiguration ();
106
110
private volatile long lastGwCommandRecvTs = 0L ;
107
111
private volatile long lastMdnsScanMillis = -1L ;
112
+ private volatile boolean readZeroDevices = false ;
108
113
109
114
private String bridgeKey = "" ;
110
115
private IHttpClientProvider httpClientProvider ;
@@ -143,7 +148,7 @@ private void startGwPolling() {
143
148
cancelGwPolling ();
144
149
backgroundGwPollingScheduler = scheduler .scheduleWithFixedDelay (() -> {
145
150
if (lastGwCommandRecvTs + 120000 < System .currentTimeMillis ()) {
146
- getGatewayConfiguration ();
151
+ getGatewayConfiguration (false );
147
152
}
148
153
}, 5000 , 120000 , TimeUnit .MILLISECONDS );
149
154
}
@@ -227,7 +232,15 @@ private boolean registerBridge(final LinkTapBridgeHandler ref) {
227
232
return true ;
228
233
}
229
234
230
- public void getGatewayConfiguration () {
235
+ public boolean getGatewayConfigurationFreshCheck () {
236
+ readZeroDevices = false ;
237
+ return getGatewayConfiguration (true );
238
+ }
239
+
240
+ public boolean getGatewayConfiguration (final boolean forceFreshRead ) {
241
+ if (forceFreshRead ) {
242
+ lastGetConfigCache .invalidateValue ();
243
+ }
231
244
String resp = "" ;
232
245
synchronized (getConfigLock ) {
233
246
resp = lastGetConfigCache .getValue ();
@@ -251,13 +264,17 @@ public void getGatewayConfiguration() {
251
264
}
252
265
lastGetConfigCache .putValue (resp );
253
266
}
254
-
255
267
}
256
268
257
269
final GatewayConfigResp gwConfig = LinkTapBindingConstants .GSON .fromJson (resp , GatewayConfigResp .class );
258
270
if (gwConfig == null ) {
259
- return ;
271
+ return false ;
260
272
}
273
+
274
+ if (gwConfig .isRetryableError ()) {
275
+ return false ;
276
+ }
277
+
261
278
currentGwId = gwConfig .gatewayId ;
262
279
263
280
final String version = gwConfig .version ;
@@ -269,7 +286,6 @@ public void getGatewayConfiguration() {
269
286
final Map <String , String > props = editProperties ();
270
287
props .put (BRIDGE_PROP_GW_VER , version );
271
288
updateProperties (props );
272
- return ;
273
289
}
274
290
if (!volUnit .equals (editProperties ().get (BRIDGE_PROP_VOL_UNIT ))) {
275
291
final Map <String , String > props = editProperties ();
@@ -285,6 +301,20 @@ public void getGatewayConfiguration() {
285
301
}
286
302
}
287
303
304
+ // Filter out the processing where we receive just a single response containing no device definitions, ensure
305
+ // this is confirmed by a second poll.
306
+ if (devIds .length == 0 || devNames .length == 0 ) {
307
+ if (!readZeroDevices ) {
308
+ logger .trace ("Detected ZERO devices in Gateway from CMD 16" );
309
+ readZeroDevices = true ;
310
+ lastGetConfigCache .invalidateValue ();
311
+ return false ; // Don't process the potentially incorrect data
312
+ }
313
+ logger .debug ("Confirmed ZERO devices in Gateway from CMD 16" );
314
+ } else {
315
+ readZeroDevices = false ;
316
+ }
317
+
288
318
boolean updatedDeviceInfo = devIds .length != discoveredDevices .size ();
289
319
290
320
for (int i = 0 ; i < devIds .length ; ++i ) {
@@ -298,19 +328,41 @@ public void getGatewayConfiguration() {
298
328
299
329
handlers .forEach (x -> x .handleMetadataRetrieved (this ));
300
330
301
- if (updatedDeviceInfo ) {
302
- this .scheduler .execute (() -> {
303
- for (Thing el : getThing ().getThings ()) {
304
- final ThingHandler th = el .getHandler ();
305
- if (th instanceof IBridgeData bridgeData ) {
331
+ final boolean forceDeviceInit = updatedDeviceInfo ;
332
+ this .scheduler .execute (() -> {
333
+ for (Thing el : getThing ().getThings ()) {
334
+ final ThingHandler th = el .getHandler ();
335
+ if (th instanceof IBridgeData bridgeData ) {
336
+ if (forceDeviceInit || ThingStatus .OFFLINE .equals (th .getThing ().getStatus ())) {
306
337
bridgeData .handleBridgeDataUpdated ();
307
338
}
308
339
}
309
- });
340
+ }
341
+ });
342
+
343
+ return true ;
344
+ }
345
+
346
+ public String sendApiRequest (final TLGatewayFrame request ) {
347
+ int triesLeft = MAX_COMMAND_RETRIES ;
348
+ int retry = 0 ;
349
+ while (triesLeft > 0 ) {
350
+ try {
351
+ return sendSingleApiRequest (request );
352
+ } catch (TransientCommunicationIssueException tcie ) {
353
+ --triesLeft ;
354
+ try {
355
+ Thread .sleep (1000L * retry );
356
+ } catch (InterruptedException ie ) {
357
+ return "" ;
358
+ }
359
+ ++retry ;
360
+ }
310
361
}
362
+ return "" ;
311
363
}
312
364
313
- public String sendApiRequest (final TLGatewayFrame req ) {
365
+ public String sendSingleApiRequest (final TLGatewayFrame req ) throws TransientCommunicationIssueException {
314
366
final UUID uid = UUID .randomUUID ();
315
367
316
368
final WebServerApi api = WebServerApi .getInstance ();
@@ -329,17 +381,26 @@ public String sendApiRequest(final TLGatewayFrame req) {
329
381
logger .debug ("{} = APP BRIDGE -> GW -> Request {}" , uid , reqData );
330
382
final String respData = api .sendRequest (host , reqData );
331
383
logger .debug ("{} = APP BRIDGE -> GW -> Response {}" , uid , respData );
332
- final TLGatewayFrame gwResponseFrame = LinkTapBindingConstants .GSON .fromJson (respData ,
333
- TLGatewayFrame .class );
334
- if (confirmGateway && gwResponseFrame != null && !gwResponseFrame .gatewayId .equals (req .gatewayId )) {
335
- logger .warn ("{}" , getLocalizedText ("warning.response-from-wrong-gw-id" , uid , req .gatewayId ,
336
- gwResponseFrame .gatewayId ));
337
- return "" ;
338
- }
339
- if (gwResponseFrame != null && req .command != gwResponseFrame .command ) {
340
- logger .warn ("{}" ,
341
- getLocalizedText ("warning.incorrect-cmd-resp" , uid , req .command , gwResponseFrame .command ));
342
- return "" ;
384
+ final GatewayDeviceResponse gwResponseFrame = LinkTapBindingConstants .GSON .fromJson (respData ,
385
+ GatewayDeviceResponse .class );
386
+
387
+ if (gwResponseFrame != null ) {
388
+ if (confirmGateway && !gwResponseFrame .gatewayId .equals (req .gatewayId )) {
389
+ logger .warn ("{}" , getLocalizedText ("warning.response-from-wrong-gw-id" , uid , req .gatewayId ,
390
+ gwResponseFrame .gatewayId ));
391
+ return "" ;
392
+ }
393
+
394
+ if (RET_GW_INTERNAL_ERR .equals (gwResponseFrame .getRes ())
395
+ || RET_GATEWAY_BUSY .equals (gwResponseFrame .getRes ())) {
396
+ throw new TransientCommunicationIssueException (GATEWAY_BUSY );
397
+ }
398
+
399
+ if (req .command != gwResponseFrame .command ) {
400
+ logger .warn ("{}" ,
401
+ getLocalizedText ("warning.incorrect-cmd-resp" , uid , req .command , gwResponseFrame .command ));
402
+ return "" ;
403
+ }
343
404
}
344
405
return respData ;
345
406
} catch (NotTapLinkGatewayException e ) {
@@ -385,7 +446,11 @@ private void connect() {
385
446
}
386
447
}
387
448
388
- getGatewayConfiguration ();
449
+ if (!getGatewayConfiguration (true )) {
450
+ logger .debug ("{}" , getLocalizedText ("bridge.info.awaiting-init" ));
451
+ scheduleReconnect ();
452
+ return ;
453
+ }
389
454
390
455
// Update the GW ID -> this bridge lookup
391
456
GW_ID_LOOKUP .registerItem (currentGwId , this , () -> {
@@ -418,13 +483,33 @@ private void connect() {
418
483
final Optional <String > servletEpOpt = (!servletEp .isEmpty ()) ? Optional .of (servletEp ) : Optional .empty ();
419
484
api .configureBridge (hostname , Optional .of (config .enableMDNS ), Optional .of (config .enableJSONComms ),
420
485
servletEpOpt );
421
- updateStatus (ThingStatus .ONLINE );
422
486
if (Thread .currentThread ().isInterrupted ()) {
423
487
return ;
424
488
}
489
+
490
+ // Ensure we have a response with data in if not schedule a reconnect in 15 seconds, theres no reason
491
+ // for a gateway with no devices.
492
+ if (!getGatewayConfigurationFreshCheck ()) {
493
+ logger .debug ("{}" , getLocalizedText ("bridge.info.awaiting-init" ));
494
+ scheduleReconnect ();
495
+ return ;
496
+ }
497
+
498
+ updateStatus (ThingStatus .ONLINE );
425
499
startGwPolling ();
426
500
connectRepair = null ;
427
501
502
+ // Force all child things run their init sequences to ensure they are registered by the
503
+ // device ID.
504
+ this .scheduler .execute (() -> {
505
+ for (Thing el : getThing ().getThings ()) {
506
+ final ThingHandler th = el .getHandler ();
507
+ if (th instanceof IBridgeData bridgeData ) {
508
+ bridgeData .handleBridgeDataUpdated ();
509
+ }
510
+ }
511
+ });
512
+
428
513
final Firmware firmware = new Firmware (getThing ().getProperties ().get (BRIDGE_PROP_GW_VER ));
429
514
if (!firmware .supportsLocalConfig ()) {
430
515
logger .warn ("{}" , getLocalizedText ("warning.fw-update-local-config" , getThing ().getLabel (),
@@ -622,13 +707,17 @@ private void processCommand0(final String request) {
622
707
}
623
708
if (fullScanRequired ) {
624
709
logger .trace ("The configured devices have changed a full scan should be run" );
625
- scheduler .execute (this ::getGatewayConfiguration );
710
+ scheduler .execute (() -> {
711
+ getGatewayConfiguration (true );
712
+ });
626
713
}
627
714
}
628
715
629
716
@ Override
630
717
public void childHandlerDisposed (ThingHandler childHandler , Thing childThing ) {
631
- scheduler .execute (this ::getGatewayConfiguration );
718
+ scheduler .execute (() -> {
719
+ getGatewayConfiguration (false );
720
+ });
632
721
super .childHandlerDisposed (childHandler , childThing );
633
722
}
634
723
}
0 commit comments