Initialize connection to devices asynchronously (#9228)
Signed-off-by: Christoph Weitkamp <github@christophweitkamp.de>pull/10456/head
parent
13a58b9458
commit
1822f77b07
|
@ -13,12 +13,11 @@
|
||||||
package org.openhab.binding.chromecast.internal.handler;
|
package org.openhab.binding.chromecast.internal.handler;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Set;
|
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.NonNullByDefault;
|
||||||
import org.eclipse.jdt.annotation.Nullable;
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
@ -60,12 +59,11 @@ import su.litvak.chromecast.api.v2.ChromeCast;
|
||||||
*/
|
*/
|
||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
public class ChromecastHandler extends BaseThingHandler implements AudioSink {
|
public class ChromecastHandler extends BaseThingHandler implements AudioSink {
|
||||||
|
|
||||||
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 final Logger logger = LoggerFactory.getLogger(ChromecastHandler.class);
|
private final Logger logger = LoggerFactory.getLogger(ChromecastHandler.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 AudioHTTPServer audioHTTPServer;
|
private final AudioHTTPServer audioHTTPServer;
|
||||||
private final @Nullable String callbackUrl;
|
private final @Nullable String callbackUrl;
|
||||||
|
|
||||||
|
@ -92,12 +90,14 @@ public class ChromecastHandler extends BaseThingHandler implements AudioSink {
|
||||||
ChromecastConfig config = getConfigAs(ChromecastConfig.class);
|
ChromecastConfig config = getConfigAs(ChromecastConfig.class);
|
||||||
|
|
||||||
final String ipAddress = config.ipAddress;
|
final String ipAddress = config.ipAddress;
|
||||||
if (ipAddress == null || ipAddress.isEmpty()) {
|
if (ipAddress == null || ipAddress.isBlank()) {
|
||||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR,
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR,
|
||||||
"Cannot connect to Chromecast. IP address is not valid or missing.");
|
"Cannot connect to Chromecast. IP address is not valid or missing.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateStatus(ThingStatus.UNKNOWN);
|
||||||
|
|
||||||
Coordinator localCoordinator = coordinator;
|
Coordinator localCoordinator = coordinator;
|
||||||
if (localCoordinator != null && (!localCoordinator.chromeCast.getAddress().equals(ipAddress)
|
if (localCoordinator != null && (!localCoordinator.chromeCast.getAddress().equals(ipAddress)
|
||||||
|| (localCoordinator.chromeCast.getPort() != config.port))) {
|
|| (localCoordinator.chromeCast.getPort() != config.port))) {
|
||||||
|
@ -109,8 +109,14 @@ public class ChromecastHandler extends BaseThingHandler implements AudioSink {
|
||||||
ChromeCast chromecast = new ChromeCast(ipAddress, config.port);
|
ChromeCast chromecast = new ChromeCast(ipAddress, config.port);
|
||||||
localCoordinator = new Coordinator(this, thing, chromecast, config.refreshRate, audioHTTPServer,
|
localCoordinator = new Coordinator(this, thing, chromecast, config.refreshRate, audioHTTPServer,
|
||||||
callbackUrl);
|
callbackUrl);
|
||||||
localCoordinator.initialize();
|
|
||||||
coordinator = localCoordinator;
|
coordinator = localCoordinator;
|
||||||
|
|
||||||
|
scheduler.submit(() -> {
|
||||||
|
Coordinator c = coordinator;
|
||||||
|
if (c != null) {
|
||||||
|
c.initialize();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,7 +217,7 @@ public class ChromecastHandler extends BaseThingHandler implements AudioSink {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<Class<? extends ThingHandlerService>> getServices() {
|
public Collection<Class<? extends ThingHandlerService>> getServices() {
|
||||||
return Collections.singletonList(ChromecastActions.class);
|
return List.of(ChromecastActions.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean playURL(String url, @Nullable String mediaType) {
|
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 ChromecastStatusUpdater statusUpdater;
|
||||||
private final ChromecastScheduler scheduler;
|
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,
|
private Coordinator(ChromecastHandler handler, Thing thing, ChromeCast chromeCast, long refreshRate,
|
||||||
AudioHTTPServer audioHttpServer, @Nullable String callbackURL) {
|
AudioHTTPServer audioHttpServer, @Nullable String callbackURL) {
|
||||||
this.chromeCast = chromeCast;
|
this.chromeCast = chromeCast;
|
||||||
|
@ -249,30 +268,52 @@ public class ChromecastHandler extends BaseThingHandler implements AudioSink {
|
||||||
}
|
}
|
||||||
|
|
||||||
void initialize() {
|
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.registerListener(eventReceiver);
|
||||||
chromeCast.registerConnectionListener(eventReceiver);
|
chromeCast.registerConnectionListener(eventReceiver);
|
||||||
|
|
||||||
this.connect();
|
connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
void destroy() {
|
void destroy() {
|
||||||
|
connectionState = ConnectionState.DISCONNECTING;
|
||||||
|
|
||||||
chromeCast.unregisterConnectionListener(eventReceiver);
|
chromeCast.unregisterConnectionListener(eventReceiver);
|
||||||
chromeCast.unregisterListener(eventReceiver);
|
chromeCast.unregisterListener(eventReceiver);
|
||||||
|
|
||||||
try {
|
|
||||||
scheduler.destroy();
|
scheduler.destroy();
|
||||||
|
|
||||||
|
try {
|
||||||
chromeCast.disconnect();
|
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() {
|
private void connect() {
|
||||||
try {
|
try {
|
||||||
chromeCast.connect();
|
chromeCast.connect();
|
||||||
|
|
||||||
statusUpdater.updateMediaStatus(null);
|
statusUpdater.updateMediaStatus(null);
|
||||||
statusUpdater.updateStatus(ThingStatus.ONLINE);
|
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,
|
statusUpdater.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
|
||||||
e.getMessage());
|
e.getMessage());
|
||||||
scheduler.scheduleConnect();
|
scheduler.scheduleConnect();
|
||||||
|
|
Loading…
Reference in New Issue