]> git.basschouten.com Git - openhab-addons.git/commitdiff
Initialize connection to devices asynchronously (#9228)
authorChristoph Weitkamp <github@christophweitkamp.de>
Fri, 9 Apr 2021 19:55:00 +0000 (21:55 +0200)
committerGitHub <noreply@github.com>
Fri, 9 Apr 2021 19:55:00 +0000 (21:55 +0200)
Signed-off-by: Christoph Weitkamp <github@christophweitkamp.de>
bundles/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/handler/ChromecastHandler.java

index b68ea53779e2e20586396ebc8b4d379a7cfbeec0..d3d56200860123b209ed6cac9817a7b7171bcb11 100644 (file)
 package org.openhab.binding.chromecast.internal.handler;
 
 import java.io.IOException;
+import java.security.GeneralSecurityException;
 import java.util.Collection;
-import java.util.Collections;
+import java.util.List;
 import java.util.Locale;
 import java.util.Set;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
@@ -60,12 +59,11 @@ import su.litvak.chromecast.api.v2.ChromeCast;
  */
 @NonNullByDefault
 public class ChromecastHandler extends BaseThingHandler implements AudioSink {
+    private final Logger logger = LoggerFactory.getLogger(ChromecastHandler.class);
 
-    private static final Set<AudioFormat> SUPPORTED_FORMATS = Collections
-            .unmodifiableSet(Stream.of(AudioFormat.MP3, AudioFormat.WAV).collect(Collectors.toSet()));
-    private static final Set<Class<? extends AudioStream>> SUPPORTED_STREAMS = Collections.singleton(AudioStream.class);
+    private static final Set<AudioFormat> SUPPORTED_FORMATS = Set.of(AudioFormat.MP3, AudioFormat.WAV);
+    private static final Set<Class<? extends AudioStream>> SUPPORTED_STREAMS = Set.of(AudioStream.class);
 
-    private final Logger logger = LoggerFactory.getLogger(ChromecastHandler.class);
     private final AudioHTTPServer audioHTTPServer;
     private final @Nullable String callbackUrl;
 
@@ -92,12 +90,14 @@ public class ChromecastHandler extends BaseThingHandler implements AudioSink {
         ChromecastConfig config = getConfigAs(ChromecastConfig.class);
 
         final String ipAddress = config.ipAddress;
-        if (ipAddress == null || ipAddress.isEmpty()) {
+        if (ipAddress == null || ipAddress.isBlank()) {
             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR,
                     "Cannot connect to Chromecast. IP address is not valid or missing.");
             return;
         }
 
+        updateStatus(ThingStatus.UNKNOWN);
+
         Coordinator localCoordinator = coordinator;
         if (localCoordinator != null && (!localCoordinator.chromeCast.getAddress().equals(ipAddress)
                 || (localCoordinator.chromeCast.getPort() != config.port))) {
@@ -109,8 +109,14 @@ public class ChromecastHandler extends BaseThingHandler implements AudioSink {
             ChromeCast chromecast = new ChromeCast(ipAddress, config.port);
             localCoordinator = new Coordinator(this, thing, chromecast, config.refreshRate, audioHTTPServer,
                     callbackUrl);
-            localCoordinator.initialize();
             coordinator = localCoordinator;
+
+            scheduler.submit(() -> {
+                Coordinator c = coordinator;
+                if (c != null) {
+                    c.initialize();
+                }
+            });
         }
     }
 
@@ -211,7 +217,7 @@ public class ChromecastHandler extends BaseThingHandler implements AudioSink {
 
     @Override
     public Collection<Class<? extends ThingHandlerService>> getServices() {
-        return Collections.singletonList(ChromecastActions.class);
+        return List.of(ChromecastActions.class);
     }
 
     public boolean playURL(String url, @Nullable String mediaType) {
@@ -235,6 +241,19 @@ public class ChromecastHandler extends BaseThingHandler implements AudioSink {
         private final ChromecastStatusUpdater statusUpdater;
         private final ChromecastScheduler scheduler;
 
+        /**
+         * used internally to represent the connection state
+         */
+        private enum ConnectionState {
+            UNKNOWN,
+            CONNECTING,
+            CONNECTED,
+            DISCONNECTING,
+            DISCONNECTED
+        }
+
+        private ConnectionState connectionState = ConnectionState.UNKNOWN;
+
         private Coordinator(ChromecastHandler handler, Thing thing, ChromeCast chromeCast, long refreshRate,
                 AudioHTTPServer audioHttpServer, @Nullable String callbackURL) {
             this.chromeCast = chromeCast;
@@ -249,30 +268,52 @@ public class ChromecastHandler extends BaseThingHandler implements AudioSink {
         }
 
         void initialize() {
+            if (connectionState == ConnectionState.CONNECTED) {
+                logger.debug("Already connected");
+                return;
+            } else if (connectionState == ConnectionState.CONNECTING) {
+                logger.debug("Already connecting");
+                return;
+            } else if (connectionState == ConnectionState.DISCONNECTING) {
+                logger.warn("Trying to re-connect while still disconnecting");
+                return;
+            }
+            connectionState = ConnectionState.CONNECTING;
+
             chromeCast.registerListener(eventReceiver);
             chromeCast.registerConnectionListener(eventReceiver);
 
-            this.connect();
+            connect();
         }
 
         void destroy() {
+            connectionState = ConnectionState.DISCONNECTING;
+
             chromeCast.unregisterConnectionListener(eventReceiver);
             chromeCast.unregisterListener(eventReceiver);
 
+            scheduler.destroy();
+
             try {
-                scheduler.destroy();
                 chromeCast.disconnect();
-            } catch (final IOException ex) {
-                logger.debug("Disconnect failed: {}", ex.getMessage());
+
+                connectionState = ConnectionState.DISCONNECTED;
+            } catch (final IOException e) {
+                logger.debug("Disconnect failed: {}", e.getMessage());
+                connectionState = ConnectionState.UNKNOWN;
             }
         }
 
         private void connect() {
             try {
                 chromeCast.connect();
+
                 statusUpdater.updateMediaStatus(null);
                 statusUpdater.updateStatus(ThingStatus.ONLINE);
-            } catch (final Exception e) {
+
+                connectionState = ConnectionState.CONNECTED;
+            } catch (final IOException | GeneralSecurityException e) {
+                logger.debug("Connect failed, trying to reconnect: {}", e.getMessage());
                 statusUpdater.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
                         e.getMessage());
                 scheduler.scheduleConnect();