Add: Google cast support

pull/16/head
Juraj Nyíri 4 years ago
parent 5f34fae23f
commit 66ef70bdb1

@ -2,7 +2,7 @@
Custom Home Assistant card which integrates plex into Home Assistant and makes it possible to launch movies or tv shows on TV with a simple click. Custom Home Assistant card which integrates plex into Home Assistant and makes it possible to launch movies or tv shows on TV with a simple click.
Supported are **ALL** Plex clients, some even with enhanced functionality. Kodi with PlexKodiConnect is also supported. Supported are **ALL** Plex clients, some even with enhanced functionality. Kodi with PlexKodiConnect and Google Cast is also supported.
Video of the card: Video of the card:
@ -53,6 +53,7 @@ _Available special libraries:_
- **androidtv**: Entity id of your media_player configured via [Android TV](https://www.home-assistant.io/integrations/androidtv/). See [detailed instructions](https://github.com/JurajNyiri/PlexMeetsHomeAssistant#android-tv-or-fire-tv). - **androidtv**: Entity id of your media_player configured via [Android TV](https://www.home-assistant.io/integrations/androidtv/). See [detailed instructions](https://github.com/JurajNyiri/PlexMeetsHomeAssistant#android-tv-or-fire-tv).
- **kodi**: Entity id of your media_player configured via [Kodi](https://www.home-assistant.io/integrations/kodi/). See [detailed instructions](https://github.com/JurajNyiri/PlexMeetsHomeAssistant#kodi). - **kodi**: Entity id of your media_player configured via [Kodi](https://www.home-assistant.io/integrations/kodi/). See [detailed instructions](https://github.com/JurajNyiri/PlexMeetsHomeAssistant#kodi).
- **plexPlayer**: Name or machine ID of your plex client. Use this if you do not have devices above. See [detailed instructions](https://github.com/JurajNyiri/PlexMeetsHomeAssistant#all-other-plex-clients). - **plexPlayer**: Name or machine ID of your plex client. Use this if you do not have devices above. See [detailed instructions](https://github.com/JurajNyiri/PlexMeetsHomeAssistant#all-other-plex-clients).
- **cast**: Entity id of your media_player configured via [Google Cast](https://www.home-assistant.io/integrations/cast/). See [detailed instructions](https://github.com/JurajNyiri/PlexMeetsHomeAssistant#google-cast).
Example of card configuration: Example of card configuration:
@ -69,6 +70,7 @@ entity:
kodi: media_player.kodi_123456qwe789rty kodi: media_player.kodi_123456qwe789rty
androidtv: media_player.living_room_nvidia_shield androidtv: media_player.living_room_nvidia_shield
plexPlayer: 192.168.13.38 plexPlayer: 192.168.13.38
cast: media_player.bedroom_tv
``` ```
Example using lists: Example using lists:
@ -93,11 +95,14 @@ entity:
plexPlayer: plexPlayer:
- TV 2020 - TV 2020
- 192.168.13.50 - 192.168.13.50
cast:
- media_player.bedroom_tv
``` ```
In this example, it will try to first play via kodi, in bedroom. If that kodi is unavailable or off, it tries in living room kodi. In this example, it will try to first play via kodi, in bedroom. If that kodi is unavailable or off, it tries in living room kodi.
If that fails, it moves on to android tvs, starting with living room, continuing with bedroom and ending with kitchen. If that fails, it moves on to android tvs, starting with living room, continuing with bedroom and ending with kitchen.
Finally, if a possible player still has not been found (all kodis and shields are off) it tries to play via plexPlayer, trying TV 2020 and if not found, IP 192.168.13.50. Next, if a possible player still has not been found (all kodis and shields are off) it tries to play via plexPlayer, trying TV 2020 and if not found, IP 192.168.13.50.
Finally, it tries to cast into media_player.bedroom_tv.
## Detailed configuration instructions for end devices ## Detailed configuration instructions for end devices
@ -175,6 +180,30 @@ Play button is only visible if all the conditions inside Availability section of
✅ Episodes ✅ Episodes
### Google Cast
**Difficulty to setup**: Very easy
**Steps**:
- Set up [Google Cast](https://www.home-assistant.io/integrations/cast/) in Home Assistant.
- Use entity_id of media_player provided by Google Cast integration in card, example: `cast: media_player.bedroom_tv`.
- Save card configuration and make sure the entity is not `unavailable`, if you see play buttons on movies or individual episodes configuration was successful.
**Availability**:
- Media player entity cannot be `unavailable`
**Supported play types**:
✅ Movies
❌ Show
❌ Season
✅ Episodes
### All other plex clients ### All other plex clients
**Difficulty to setup**: Very Easy to Moderate **Difficulty to setup**: Very Easy to Moderate

@ -17206,7 +17206,8 @@ const CSS_STYLE = {
const supported = { const supported = {
kodi: ['movie', 'episode'], kodi: ['movie', 'episode'],
androidtv: ['movie', 'show', 'season', 'episode', 'clip'], androidtv: ['movie', 'show', 'season', 'episode', 'clip'],
plexPlayer: ['movie', 'show', 'season', 'episode', 'clip'] plexPlayer: ['movie', 'show', 'season', 'episode', 'clip'],
cast: ['movie', 'episode']
}; };
var bind = function bind(fn, thisArg) { var bind = function bind(fn, thisArg) {
@ -18901,6 +18902,9 @@ class PlayController {
case 'plexPlayer': case 'plexPlayer':
await this.playViaPlexPlayer(entity.value, data.key.split('/')[3]); await this.playViaPlexPlayer(entity.value, data.key.split('/')[3]);
break; break;
case 'cast':
this.playViaCast(entity.value, data.Media[0].Part[0].key);
break;
default: default:
throw Error(`No service available to play ${data.title}!`); throw Error(`No service available to play ${data.title}!`);
} }
@ -18990,6 +18994,16 @@ class PlayController {
throw Error(`Plex type ${type} is not supported in Kodi.`); throw Error(`Plex type ${type} is not supported in Kodi.`);
} }
}; };
this.playViaCast = (entityName, mediaLink) => {
this.hass.callService('media_player', 'play_media', {
// eslint-disable-next-line @typescript-eslint/camelcase
entity_id: entityName,
// eslint-disable-next-line @typescript-eslint/camelcase
media_content_type: 'video',
// eslint-disable-next-line @typescript-eslint/camelcase
media_content_id: this.plex.authorizeURL(`${this.plex.getBasicURL()}${mediaLink}`)
});
};
this.playViaAndroidTV = async (entityName, mediaID, instantPlay = false) => { this.playViaAndroidTV = async (entityName, mediaID, instantPlay = false) => {
const serverID = await this.plex.getServerID(); const serverID = await this.plex.getServerID();
let command = `am start`; let command = `am start`;
@ -19018,7 +19032,8 @@ class PlayController {
if (lodash.includes(this.supported[key], data.type)) { if (lodash.includes(this.supported[key], data.type)) {
if ((key === 'kodi' && this.isKodiSupported(entity)) || if ((key === 'kodi' && this.isKodiSupported(entity)) ||
(key === 'androidtv' && this.isAndroidTVSupported(entity)) || (key === 'androidtv' && this.isAndroidTVSupported(entity)) ||
(key === 'plexPlayer' && this.isPlexPlayerSupported(entity))) { (key === 'plexPlayer' && this.isPlexPlayerSupported(entity)) ||
(key === 'cast' && this.isCastSupported(entity))) {
service = { key, value: entity }; service = { key, value: entity };
return false; return false;
} }
@ -19028,7 +19043,8 @@ class PlayController {
else if (lodash.includes(this.supported[key], data.type)) { else if (lodash.includes(this.supported[key], data.type)) {
if ((key === 'kodi' && this.isKodiSupported(entityVal)) || if ((key === 'kodi' && this.isKodiSupported(entityVal)) ||
(key === 'androidtv' && this.isAndroidTVSupported(entityVal)) || (key === 'androidtv' && this.isAndroidTVSupported(entityVal)) ||
(key === 'plexPlayer' && this.isPlexPlayerSupported(entityVal))) { (key === 'plexPlayer' && this.isPlexPlayerSupported(entityVal)) ||
(key === 'cast' && this.isCastSupported(entityVal))) {
service = { key, value: entityVal }; service = { key, value: entityVal };
return false; return false;
} }
@ -19071,6 +19087,11 @@ class PlayController {
} }
return false; return false;
}; };
this.isCastSupported = (entityName) => {
return (this.hass.states[entityName] &&
!lodash.isNil(this.hass.states[entityName].attributes) &&
this.hass.states[entityName].state !== 'unavailable');
};
this.isAndroidTVSupported = (entityName) => { this.isAndroidTVSupported = (entityName) => {
return (this.hass.states[entityName] && return (this.hass.states[entityName] &&
!lodash.isEqual(this.hass.states[entityName].state, 'off') && !lodash.isEqual(this.hass.states[entityName].state, 'off') &&

@ -11,7 +11,8 @@ const CSS_STYLE = {
const supported: any = { const supported: any = {
kodi: ['movie', 'episode'], kodi: ['movie', 'episode'],
androidtv: ['movie', 'show', 'season', 'episode', 'clip'], androidtv: ['movie', 'show', 'season', 'episode', 'clip'],
plexPlayer: ['movie', 'show', 'season', 'episode', 'clip'] plexPlayer: ['movie', 'show', 'season', 'episode', 'clip'],
cast: ['movie', 'episode']
}; };
const LOREM_IPSUM = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec semper risus vitae aliquet interdum. Nulla facilisi. Pellentesque viverra sagittis lorem eget aliquet. Cras vehicula, purus vel consectetur mattis, ipsum arcu ullamcorper mi, id viverra purus ex eu dolor. Integer vehicula lacinia sem convallis iaculis. Nulla fermentum erat interdum, efficitur felis in, mollis neque. Vivamus luctus metus eget nisl pellentesque, placerat elementum magna eleifend. const LOREM_IPSUM = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec semper risus vitae aliquet interdum. Nulla facilisi. Pellentesque viverra sagittis lorem eget aliquet. Cras vehicula, purus vel consectetur mattis, ipsum arcu ullamcorper mi, id viverra purus ex eu dolor. Integer vehicula lacinia sem convallis iaculis. Nulla fermentum erat interdum, efficitur felis in, mollis neque. Vivamus luctus metus eget nisl pellentesque, placerat elementum magna eleifend.

@ -97,6 +97,9 @@ class PlayController {
case 'plexPlayer': case 'plexPlayer':
await this.playViaPlexPlayer(entity.value, data.key.split('/')[3]); await this.playViaPlexPlayer(entity.value, data.key.split('/')[3]);
break; break;
case 'cast':
this.playViaCast(entity.value, data.Media[0].Part[0].key);
break;
default: default:
throw Error(`No service available to play ${data.title}!`); throw Error(`No service available to play ${data.title}!`);
} }
@ -197,6 +200,17 @@ class PlayController {
} }
}; };
private playViaCast = (entityName: string, mediaLink: string): void => {
this.hass.callService('media_player', 'play_media', {
// eslint-disable-next-line @typescript-eslint/camelcase
entity_id: entityName,
// eslint-disable-next-line @typescript-eslint/camelcase
media_content_type: 'video',
// eslint-disable-next-line @typescript-eslint/camelcase
media_content_id: this.plex.authorizeURL(`${this.plex.getBasicURL()}${mediaLink}`)
});
};
private playViaAndroidTV = async (entityName: string, mediaID: number, instantPlay = false): Promise<void> => { private playViaAndroidTV = async (entityName: string, mediaID: number, instantPlay = false): Promise<void> => {
const serverID = await this.plex.getServerID(); const serverID = await this.plex.getServerID();
let command = `am start`; let command = `am start`;
@ -230,7 +244,8 @@ class PlayController {
if ( if (
(key === 'kodi' && this.isKodiSupported(entity)) || (key === 'kodi' && this.isKodiSupported(entity)) ||
(key === 'androidtv' && this.isAndroidTVSupported(entity)) || (key === 'androidtv' && this.isAndroidTVSupported(entity)) ||
(key === 'plexPlayer' && this.isPlexPlayerSupported(entity)) (key === 'plexPlayer' && this.isPlexPlayerSupported(entity)) ||
(key === 'cast' && this.isCastSupported(entity))
) { ) {
service = { key, value: entity }; service = { key, value: entity };
return false; return false;
@ -241,7 +256,8 @@ class PlayController {
if ( if (
(key === 'kodi' && this.isKodiSupported(entityVal)) || (key === 'kodi' && this.isKodiSupported(entityVal)) ||
(key === 'androidtv' && this.isAndroidTVSupported(entityVal)) || (key === 'androidtv' && this.isAndroidTVSupported(entityVal)) ||
(key === 'plexPlayer' && this.isPlexPlayerSupported(entityVal)) (key === 'plexPlayer' && this.isPlexPlayerSupported(entityVal)) ||
(key === 'cast' && this.isCastSupported(entityVal))
) { ) {
service = { key, value: entityVal }; service = { key, value: entityVal };
return false; return false;
@ -294,6 +310,14 @@ class PlayController {
return false; return false;
}; };
private isCastSupported = (entityName: string): boolean => {
return (
this.hass.states[entityName] &&
!_.isNil(this.hass.states[entityName].attributes) &&
this.hass.states[entityName].state !== 'unavailable'
);
};
private isAndroidTVSupported = (entityName: string): boolean => { private isAndroidTVSupported = (entityName: string): boolean => {
return ( return (
this.hass.states[entityName] && this.hass.states[entityName] &&

Loading…
Cancel
Save