[chromecast] Fix thing go offline after stop command (#14158)

* Restructure commander
* Improve thing status handling on error

Signed-off-by: lsiepel <leosiepel@gmail.com>
This commit is contained in:
lsiepel 2023-01-07 09:51:20 +01:00 committed by Wouter Born
parent 697b510808
commit 5b8300ad60
No known key found for this signature in database
GPG Key ID: A58E5C231E0E72B5
3 changed files with 67 additions and 44 deletions

View File

@ -52,7 +52,7 @@ public class ChromecastAudioSink {
// in case the audioStream is null, this should be interpreted as a request to end any currently playing // in case the audioStream is null, this should be interpreted as a request to end any currently playing
// stream. // stream.
logger.trace("Stop currently playing stream."); logger.trace("Stop currently playing stream.");
commander.handleStop(OnOffType.ON); commander.handleCloseApp(OnOffType.ON);
} else { } else {
final String url; final String url;
if (audioStream instanceof URLAudioStream) { if (audioStream instanceof URLAudioStream) {

View File

@ -70,7 +70,7 @@ public class ChromecastCommander {
handleControl(command); handleControl(command);
break; break;
case CHANNEL_STOP: case CHANNEL_STOP:
handleStop(command); handleCloseApp(command);
break; break;
case CHANNEL_VOLUME: case CHANNEL_VOLUME:
handleVolume(command); handleVolume(command);
@ -117,7 +117,7 @@ public class ChromecastCommander {
if (mediaStatus != null && mediaStatus.playerState == MediaStatus.PlayerState.IDLE if (mediaStatus != null && mediaStatus.playerState == MediaStatus.PlayerState.IDLE
&& mediaStatus.idleReason != null && mediaStatus.idleReason != null
&& mediaStatus.idleReason != MediaStatus.IdleReason.INTERRUPTED) { && mediaStatus.idleReason != MediaStatus.IdleReason.INTERRUPTED) {
stopMediaPlayerApp(); closeApp(MEDIA_PLAYER);
} }
} }
} catch (IOException ex) { } catch (IOException ex) {
@ -126,6 +126,12 @@ public class ChromecastCommander {
} }
} }
public void handleCloseApp(final Command command) {
if (command == OnOffType.ON) {
closeApp(MEDIA_PLAYER);
}
}
private void handlePlayUri(Command command) { private void handlePlayUri(Command command) {
if (command instanceof StringType) { if (command instanceof StringType) {
playMedia(null, command.toString(), null); playMedia(null, command.toString(), null);
@ -163,7 +169,6 @@ public class ChromecastCommander {
if (command instanceof NextPreviousType) { if (command instanceof NextPreviousType) {
// Next is implemented by seeking to the end of the current media // Next is implemented by seeking to the end of the current media
if (command == NextPreviousType.NEXT) { if (command == NextPreviousType.NEXT) {
Double duration = statusUpdater.getLastDuration(); Double duration = statusUpdater.getLastDuration();
if (duration != null) { if (duration != null) {
chromeCast.seek(duration.doubleValue() - 5); chromeCast.seek(duration.doubleValue() - 5);
@ -182,18 +187,6 @@ public class ChromecastCommander {
} }
} }
public void handleStop(final Command command) {
if (command == OnOffType.ON) {
try {
chromeCast.stopApp();
statusUpdater.updateStatus(ThingStatus.ONLINE);
} catch (final IOException ex) {
logger.debug("{} command failed: {}", command, ex.getMessage());
statusUpdater.updateStatus(ThingStatus.OFFLINE, COMMUNICATION_ERROR, ex.getMessage());
}
}
}
public void handleVolume(final Command command) { public void handleVolume(final Command command) {
if (command instanceof PercentType) { if (command instanceof PercentType) {
setVolumeInternal((PercentType) command); setVolumeInternal((PercentType) command);
@ -229,44 +222,69 @@ public class ChromecastCommander {
} }
} }
public void playMedia(@Nullable String title, @Nullable String url, @Nullable String mimeType) { public void startApp(@Nullable String appId) {
if (appId == null) {
return;
}
try { try {
if (chromeCast.isAppAvailable(MEDIA_PLAYER)) { if (chromeCast.isAppAvailable(appId)) {
if (!chromeCast.isAppRunning(MEDIA_PLAYER)) { if (!chromeCast.isAppRunning(appId)) {
final Application app = chromeCast.launchApp(MEDIA_PLAYER); final Application app = chromeCast.launchApp(appId);
statusUpdater.setAppSessionId(app.sessionId); statusUpdater.setAppSessionId(app.sessionId);
logger.debug("Application launched: {}", app); logger.debug("Application launched: {}", appId);
} }
if (url != null) { } else {
// If the current track is paused, launching a new request results in nothing happening, therefore logger.warn("Failed starting app, app probably not installed. Appid: {}", appId);
// resume current track. }
MediaStatus ms = chromeCast.getMediaStatus(); statusUpdater.updateStatus(ThingStatus.ONLINE);
if (ms != null && MediaStatus.PlayerState.PAUSED == ms.playerState && url.equals(ms.media.url)) { } catch (final IOException e) {
logger.debug("Current stream paused, resuming"); logger.warn("Failed starting app: {}. Message: {}", appId, e.getMessage());
chromeCast.play(); }
} else { }
chromeCast.load(title, null, url, mimeType);
} public void closeApp(@Nullable String appId) {
if (appId == null) {
return;
}
try {
if (chromeCast.isAppRunning(appId)) {
Application app = chromeCast.getRunningApp();
if (app.id.equals(appId) && app.sessionId.equals(statusUpdater.getAppSessionId())) {
chromeCast.stopApp();
logger.debug("Application closed: {}", appId);
}
}
} catch (final IOException e) {
logger.debug("Failed stopping media player app: {} with message: {}", appId, e.getMessage());
}
}
public void playMedia(@Nullable String title, @Nullable String url, @Nullable String mimeType) {
startApp(MEDIA_PLAYER);
try {
if (url != null && chromeCast.isAppRunning(MEDIA_PLAYER)) {
// If the current track is paused, launching a new request results in nothing happening, therefore
// resume current track.
MediaStatus ms = chromeCast.getMediaStatus();
if (ms != null && MediaStatus.PlayerState.PAUSED == ms.playerState && url.equals(ms.media.url)) {
logger.debug("Current stream paused, resuming");
chromeCast.play();
} else {
chromeCast.load(title, null, url, mimeType);
} }
} else { } else {
logger.warn("Missing media player app - cannot process media."); logger.warn("Missing media player app - cannot process media.");
} }
statusUpdater.updateStatus(ThingStatus.ONLINE); statusUpdater.updateStatus(ThingStatus.ONLINE);
} catch (final IOException e) { } catch (final IOException e) {
logger.debug("Failed playing media: {}", e.getMessage()); if ("Unable to load media".equals(e.getMessage())) {
statusUpdater.updateStatus(ThingStatus.OFFLINE, COMMUNICATION_ERROR, e.getMessage()); logger.warn("Unable to load media: {}", url);
} } else {
} logger.debug("Failed playing media: {}", e.getMessage());
statusUpdater.updateStatus(ThingStatus.OFFLINE, COMMUNICATION_ERROR,
private void stopMediaPlayerApp() { "IOException while trying to play media: " + e.getMessage());
try {
Application app = chromeCast.getRunningApp();
if (app.id.equals(MEDIA_PLAYER) && app.sessionId.equals(statusUpdater.getAppSessionId())) {
chromeCast.stopApp();
logger.debug("Media player app stopped");
} }
} catch (final IOException e) {
logger.debug("Failed stopping media player app", e);
} }
} }
} }

View File

@ -56,6 +56,8 @@ public class ChromecastEventReceiver implements ChromeCastSpontaneousEventListen
@Override @Override
public void spontaneousEventReceived(final @NonNullByDefault({}) ChromeCastSpontaneousEvent event) { public void spontaneousEventReceived(final @NonNullByDefault({}) ChromeCastSpontaneousEvent event) {
logger.trace("Received an {} event (class={})", event.getType(), event.getData());
switch (event.getType()) { switch (event.getType()) {
case CLOSE: case CLOSE:
statusUpdater.updateMediaStatus(null); statusUpdater.updateMediaStatus(null);
@ -66,6 +68,9 @@ public class ChromecastEventReceiver implements ChromeCastSpontaneousEventListen
case STATUS: case STATUS:
statusUpdater.processStatusUpdate(event.getData(Status.class)); statusUpdater.processStatusUpdate(event.getData(Status.class));
break; break;
case APPEVENT:
logger.debug("Received an 'APPEVENT' event, ignoring");
break;
case UNKNOWN: case UNKNOWN:
logger.debug("Received an 'UNKNOWN' event (class={})", event.getType().getDataClass()); logger.debug("Received an 'UNKNOWN' event (class={})", event.getType().getDataClass());
break; break;