Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion bundles/org.openhab.binding.chromecast/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ With manual thing configuration the parameter `port` for audio group must be det
|-------------|---------|----------|----------|---------|------------------------------------------------------|
| ipAddress | - | Yes | No | String | The hostname or IP address of the Chromecast device. |
| port | 8009 | No | Yes | Integer | The port where the Chromecast is listening |
| refreshRate | 10 | No | Yes | Integer | The refresh (poll) interval in seconds. |

## Channels

Expand Down
30 changes: 25 additions & 5 deletions bundles/org.openhab.binding.chromecast/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@

<dependencies>
<dependency>
<groupId>de.sfuhrm</groupId>
<artifactId>chromecast-java-api-v2</artifactId>
<version>0.12.3</version>
<groupId>org.digitalmediaserver</groupId>
<artifactId>cast-api</artifactId>
<version>0.1.2-SNAPSHOT</version>
<scope>compile</scope>
<exclusions>
<exclusion>
Expand All @@ -31,10 +31,16 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>annotations</artifactId>
<version>3.0.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-javalite</artifactId>
<version>3.25.5</version>
<artifactId>protobuf-java</artifactId>
<version>2.6.0</version>
<scope>compile</scope>
</dependency>
<dependency>
Expand All @@ -57,4 +63,18 @@
</dependency>
</dependencies>

<repositories>
<repository>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
<id>central-portal-snapshots</id>
<name>Central Portal Snapshots</name>
<url>https://central.sonatype.com/repository/maven-snapshots/</url>
</repository>
</repositories>

</project>
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public class ChromecastBindingConstants {
Stream.of(THING_TYPE_AUDIO, THING_TYPE_AUDIOGROUP, THING_TYPE_CHROMECAST).collect(Collectors.toSet()));

// Config Parameters
public static final String HOST = "ipAddress";
public static final String HOST = "host";
public static final String PORT = "port";
public static final String DEVICE_ID = "deviceId";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@

import java.io.IOException;

import org.digitalmediaserver.cast.Application;
import org.digitalmediaserver.cast.CastDevice;
import org.digitalmediaserver.cast.Media.MediaBuilder;
import org.digitalmediaserver.cast.Media.StreamType;
import org.digitalmediaserver.cast.MediaStatus;
import org.digitalmediaserver.cast.ReceiverStatus;
import org.digitalmediaserver.cast.Session;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.library.types.IncreaseDecreaseType;
Expand All @@ -32,11 +39,6 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import su.litvak.chromecast.api.v2.Application;
import su.litvak.chromecast.api.v2.ChromeCast;
import su.litvak.chromecast.api.v2.MediaStatus;
import su.litvak.chromecast.api.v2.Status;

/**
* This sends the various commands to the Chromecast.
*
Expand All @@ -46,13 +48,15 @@
public class ChromecastCommander {
private final Logger logger = LoggerFactory.getLogger(ChromecastCommander.class);

private final ChromeCast chromeCast;
private final CastDevice chromeCast;
private final ChromecastScheduler scheduler;
private final ChromecastStatusUpdater statusUpdater;

private static final int VOLUMESTEP = 10;

public ChromecastCommander(ChromeCast chromeCast, ChromecastScheduler scheduler,
private static final String SOURCE = "openHAB";

public ChromecastCommander(CastDevice chromeCast, ChromecastScheduler scheduler,
ChromecastStatusUpdater statusUpdater) {
this.chromeCast = chromeCast;
this.scheduler = scheduler;
Expand All @@ -66,6 +70,11 @@ public void handleCommand(final ChannelUID channelUID, final Command command) {
}

switch (channelUID.getId()) {
case CHANNEL_APP_ID:
if (command instanceof StringType) {
startApp(command.toString());
}
break;
case CHANNEL_CONTROL:
handleControl(command);
break;
Expand Down Expand Up @@ -94,9 +103,9 @@ public void handleRefresh() {
return;
}

Status status;
ReceiverStatus status;
try {
status = chromeCast.getStatus();
status = chromeCast.getReceiverStatus();
statusUpdater.processStatusUpdate(status);

if (status == null) {
Expand All @@ -110,13 +119,19 @@ public void handleRefresh() {
}

try {
if (status != null && status.getRunningApp() != null) {
MediaStatus mediaStatus = chromeCast.getMediaStatus();
if (status != null && status.getRunningApplication() instanceof Application application) {
if (application.getTransportId() == null) {
logger.debug("Running app has no transportId, cannot request media status");
return;
}

Session session = chromeCast.startSession(SOURCE, application);
MediaStatus mediaStatus = session.getMediaStatus();
statusUpdater.updateMediaStatus(mediaStatus);

if (mediaStatus != null && mediaStatus.playerState == MediaStatus.PlayerState.IDLE
&& mediaStatus.idleReason != null
&& mediaStatus.idleReason != MediaStatus.IdleReason.INTERRUPTED) {
if (mediaStatus != null && mediaStatus.getPlayerState() == MediaStatus.PlayerState.IDLE
&& mediaStatus.getIdleReason() != null
&& mediaStatus.getIdleReason() != MediaStatus.IdleReason.INTERRUPTED) {
closeApp(MEDIA_PLAYER);
}
}
Expand All @@ -130,15 +145,15 @@ public void handleCloseApp(final Command command) {
if (command == OnOffType.ON) {
Application app;
try {
app = chromeCast.getRunningApp();
app = chromeCast.getRunningApplication();
} catch (final IOException e) {
logger.info("{} command failed: {}", command, e.getMessage());
statusUpdater.updateStatus(ThingStatus.OFFLINE, COMMUNICATION_ERROR, e.getMessage());
return;
}

if (app != null) {
closeApp(app.id);
closeApp(app.getAppId());
}
}
}
Expand All @@ -151,27 +166,33 @@ private void handlePlayUri(Command command) {

private void handleControl(final Command command) {
try {
Application app = chromeCast.getRunningApp();
Application app = chromeCast.getRunningApplication();
statusUpdater.updateStatus(ThingStatus.ONLINE);
if (app == null) {
logger.debug("{} command ignored because media player app is not running", command);
return;
}

Session session = chromeCast.startSession(SOURCE, app);
MediaStatus mediaStatus = session.getMediaStatus();
logger.debug("mediaStatus {}", mediaStatus);
int mediaSessionId = -1;
if (mediaStatus != null) {
mediaSessionId = mediaStatus.getMediaSessionId();
}

if (command instanceof PlayPauseType playPauseCommand) {
MediaStatus mediaStatus = chromeCast.getMediaStatus();
logger.debug("mediaStatus {}", mediaStatus);
if (mediaStatus == null || mediaStatus.playerState == MediaStatus.PlayerState.IDLE) {
if (mediaStatus == null || mediaStatus.getPlayerState() == MediaStatus.PlayerState.IDLE) {
logger.debug("{} command ignored because media is not loaded", command);
return;
}
if (playPauseCommand == PlayPauseType.PLAY) {
chromeCast.play();
session.play(mediaSessionId, false);
} else if (playPauseCommand == PlayPauseType.PAUSE
&& ((mediaStatus.supportedMediaCommands & 0x00000001) == 0x1)) {
chromeCast.pause();
&& ((mediaStatus.getSupportedMediaCommands() & 0x00000001) == 0x1)) {
session.pause(mediaSessionId, false);
} else {
logger.info("{} command not supported by current media", command);
logger.warn("{} command not supported by current media", command);
}
}

Expand All @@ -180,7 +201,7 @@ private void handleControl(final Command command) {
if (command == NextPreviousType.NEXT) {
Double duration = statusUpdater.getLastDuration();
if (duration != null) {
chromeCast.seek(duration.doubleValue() - 5);
session.seek(mediaSessionId, (duration.doubleValue() - 5), null, false);
} else {
logger.info("{} command failed - unknown media duration", command);
}
Expand Down Expand Up @@ -210,7 +231,7 @@ public void handleVolume(final Command command) {

private void setVolumeInternal(PercentType volume) {
try {
chromeCast.setVolumeByIncrement(volume.floatValue() / 100);
chromeCast.setVolumeLevel(volume.floatValue() / 100);
statusUpdater.updateStatus(ThingStatus.ONLINE);
} catch (final IOException ex) {
logger.debug("Set volume failed: {}", ex.getMessage());
Expand All @@ -222,7 +243,7 @@ private void handleMute(final Command command) {
if (command instanceof OnOffType) {
final boolean mute = command == OnOffType.ON;
try {
chromeCast.setMuted(mute);
chromeCast.setMuteState(mute);
statusUpdater.updateStatus(ThingStatus.ONLINE);
} catch (final IOException ex) {
logger.debug("Mute/unmute volume failed: {}", ex.getMessage());
Expand All @@ -236,10 +257,10 @@ public void startApp(@Nullable String appId) {
return;
}
try {
if (chromeCast.isAppAvailable(appId)) {
if (!chromeCast.isAppRunning(appId)) {
final Application app = chromeCast.launchApp(appId);
statusUpdater.setAppSessionId(app.sessionId);
if (chromeCast.isApplicationAvailable(appId)) {
if (!chromeCast.isApplicationRunning(appId)) {
final ReceiverStatus receiverStatus = chromeCast.launchApplication(appId, true);
statusUpdater.setAppSessionId(receiverStatus.getRunningApplication().getSessionId());
logger.debug("Application launched: {}", appId);
}
} else {
Expand All @@ -257,11 +278,11 @@ public void closeApp(@Nullable String appId) {
}

try {
if (chromeCast.isAppRunning(appId)) {
Application app = chromeCast.getRunningApp();
if (app.id.equals(appId)) {
chromeCast.stopApp();
logger.debug("Application closed: {}", appId);
if (chromeCast.isApplicationAvailable(appId)) {
Application app = chromeCast.getRunningApplication();
if (app.getAppId().equals(MEDIA_PLAYER) && app.getSessionId().equals(statusUpdater.getAppSessionId())) {
chromeCast.stopApplication(app, false);
logger.debug("Media player app stopped");
}
}
} catch (final IOException e) {
Expand All @@ -272,15 +293,18 @@ public void closeApp(@Nullable String appId) {
public void playMedia(@Nullable String title, @Nullable String url, @Nullable String mimeType) {
startApp(MEDIA_PLAYER);
try {
if (url != null && chromeCast.isAppRunning(MEDIA_PLAYER)) {
if (url != null && chromeCast.isApplicationRunning(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)) {
Session session = chromeCast.startSession(SOURCE, chromeCast.getRunningApplication());
MediaStatus ms = session.getMediaStatus();
if (ms != null && MediaStatus.PlayerState.PAUSED == ms.getPlayerState()
&& url.equals(ms.getMedia().getUrl())) {
logger.debug("Current stream paused, resuming");
chromeCast.play();
session.play(ms.getMediaSessionId(), false);
} else {
chromeCast.load(title, null, url, mimeType);
MediaBuilder builder = new MediaBuilder(url, mimeType, StreamType.NONE);
session.load(builder, true, 0.0, false);
}
} else {
logger.warn("Missing media player app - cannot process media.");
Expand All @@ -296,4 +320,15 @@ public void playMedia(@Nullable String title, @Nullable String url, @Nullable St
}
}
}

public void dispose() {
scheduler.destroy();
if (chromeCast.isConnected()) {
try {
chromeCast.disconnect();
} catch (IOException e) {
logger.debug("Failed to disconnect from chromecast: {}", e.getMessage());
}
}
}
}
Loading