Merge pull request #67 from JurajNyiri/vlc_telnet

Add #66: VLC media player Telnet
sonos
Juraj Nyíri 3 years ago committed by GitHub
commit b1bcc4648a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -25,6 +25,7 @@ You can also use Live TV library by specifying its name, usually "Live TV & DVR"
- **kodi**: Entity id of your media_player configured via [Kodi](https://www.home-assistant.io/integrations/kodi/). See [detailed instructions](#kodi). It is also possible to use short declaration with kodi.
- **plexPlayer**: Name or machine ID of your plex client. Use this if you do not have devices above. See [detailed instructions](#all-other-plex-clients). It is required to use detailed declaration with "plexPlayer:" property.
- **cast**: Entity id of your media_player configured via [Google Cast](https://www.home-assistant.io/integrations/cast/). See [detailed instructions](#google-cast). It is also possible to use short declaration with cast.
- **vlcTelnet**: Entity id of your media_player configured via [VLC media player Telnet](https://www.home-assistant.io/integrations/vlc_telnet/). See [detailed instructions](#vlcTelnet). It is also possible to use short declaration with vlcTelnet.
- **input_select**: Entity id of input select you wish to use for selecting media player to play on. State of this entity needs to be entity ID of media player of `androidtv`, `kodi` or `cast`. You can also use this with `plexPlayer`, in that case, provide name or machine ID of your plex client. You can also provide the same string as displayed in entities selection in UI editor for the card (beginning with `plexPlayer |`).
- **input_text**: Entity id of input text you wish to use for selecting media player to play on. State of this entity needs to be entity ID of media player of `androidtv`, `kodi` or `cast`. You can also use this with `plexPlayer`, in that case, provide name or machine ID of your plex client. You can also provide the same string as displayed in entities selection in UI editor for the card (beginning with `plexPlayer |`).
@ -96,6 +97,7 @@ entity:
- media_player.living_room_tv # created by cast integration
- media_player.bedroom_tv # created by cast integration
- media_player.kodi_123456qwe789rty # created by kodi integration
- media_player.vlc_telnet # created by VLC Telnet integration
```
Example of card configuration using detailed definitions:
@ -116,6 +118,7 @@ entity:
androidtv: media_player.living_room_nvidia_shield
plexPlayer: 192.168.13.38
cast: media_player.bedroom_tv
vlcTelnet: media_player.vlc_telnet
```
Complex example using detailed definitions, lists and shared plex server for plexPlayer:
@ -136,6 +139,8 @@ runAfter: script.movie_time
showExtras: true
playTrailer: muted
entity:
vlcTelnet:
- media_player.vlc_telnet
kodi:
- media_player.kodi_bedroom
- media_player.kodi_living_room
@ -291,6 +296,40 @@ Play button is only visible if all the conditions inside Availability section of
❌ Live TV
### VLC media player Telnet
**Difficulty to setup**: Very easy
**Steps**:
- Set up [VLC media player Telnet](https://www.home-assistant.io/integrations/vlc_telnet/) in Home Assistant.
- Use entity_id of media_player provided by VLC media player Telnet integration in card, example: `cast: media_player.vlc_telnet`.
- Save card configuration and make sure the entity is not `unavailable`, if you see play buttons on music tracks configuration was successful.
**Availability**:
- Media player entity cannot be `unavailable`
**Supported**:
✅ Shared Plex servers
❌ Movies
❌ Show
❌ Season
❌ Episodes
❌ Artists
❌ Albums
✅ Tracks
❌ Live TV
### All other plex clients
**Difficulty to setup**: Very Easy to Moderate

@ -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.
Supported are **ALL** Plex clients, some even with enhanced functionality. Kodi with PlexKodiConnect, Android TV and Google Cast is also supported.
Supported are **ALL** Plex clients, some even with enhanced functionality. Kodi with PlexKodiConnect, Android TV, VLC via Telnet and Google Cast is also supported.
Video of the card:

@ -17207,7 +17207,8 @@ const supported = {
kodi: ['movie', 'episode', 'epg'],
androidtv: ['movie', 'show', 'season', 'episode', 'clip', 'track', 'artist', 'album'],
plexPlayer: ['movie', 'show', 'season', 'episode', 'clip', 'track', 'artist', 'album'],
cast: ['movie', 'episode', 'artist', 'album', 'track']
cast: ['movie', 'episode', 'artist', 'album', 'track'],
vlcTelnet: ['track']
};
var bind = function bind(fn, thisArg) {
@ -19508,6 +19509,9 @@ class PlayController {
case 'kodi':
await this.playViaKodi(entity.value, data, data.type);
break;
case 'vlcTelnet':
await this.playViaVLCTelnet(entity.value, data, data.type);
break;
case 'androidtv':
if (lodash.isEqual(data.type, 'epg')) {
const session = `${Math.floor(Date.now() / 1000)}`;
@ -19613,8 +19617,8 @@ class PlayController {
await this.hass.callService(this.runAfter[0], this.runAfter[1], {});
}
};
this.plexPlayerCreateQueue = async (movieID, plex) => {
const url = `${plex.getBasicURL()}/playQueues?type=video&shuffle=0&repeat=0&continuous=1&own=1&uri=server://${await plex.getServerID()}/com.plexapp.plugins.library/library/metadata/${movieID}`;
this.plexPlayerCreateQueue = async (key, plex, type, shuffle = false, repeat = false, continuous = false) => {
const url = `${plex.getBasicURL()}/playQueues?type=${type}&shuffle=${shuffle ? '1' : '0'}&repeat=${repeat ? '1' : '0'}&continuous=${continuous ? '1' : '0'}&own=1&uri=server://${await plex.getServerID()}/com.plexapp.plugins.library${key}`;
const plexResponse = await axios({
method: 'post',
url,
@ -19637,7 +19641,7 @@ class PlayController {
if (lodash.isObject(entity) && !lodash.isNil(entity.plex)) {
plex = entity.plex;
}
const { playQueueID, playQueueSelectedMetadataItemID } = await this.plexPlayerCreateQueue(movieID, this.plex);
const { playQueueID, playQueueSelectedMetadataItemID } = await this.plexPlayerCreateQueue(`/library/metadata/${movieID}`, this.plex, 'video');
let url = plex.getBasicURL();
url += `/player/playback/playMedia`;
url += `?type=video`;
@ -19752,6 +19756,22 @@ class PlayController {
throw Error(`Plex type ${type} is not supported in Kodi.`);
}
};
this.playViaVLCTelnet = async (entityName, data, type) => {
switch (type) {
case 'track':
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: 'music',
// eslint-disable-next-line @typescript-eslint/camelcase
media_content_id: this.plex.authorizeURL(`${this.plex.getBasicURL()}${data.Media[0].Part[0].key}`)
});
break;
default:
console.error(`Type ${type} is not supported on entity ${entityName}.`);
}
};
this.playViaCast = (entityName, mediaLink, contentType = 'video') => {
if (lodash.isEqual(contentType, 'video')) {
this.hass.callService('media_player', 'play_media', {
@ -19966,11 +19986,11 @@ class PlayController {
const entities = this.exportEntity(value, key);
lodash.forEach(entities, entity => {
if (lodash.includes(this.supported[entity.key], data.type)) {
// todo: load info in this.entityStates otherwise this will never work for input selects and templates
if ((entity.key === 'kodi' && this.isKodiSupported(entity.value)) ||
(entity.key === 'androidtv' && this.isAndroidTVSupported(entity.value)) ||
(entity.key === 'plexPlayer' && this.isPlexPlayerSupported(entity.value)) ||
(entity.key === 'cast' && this.isCastSupported(entity.value))) {
(entity.key === 'cast' && this.isCastSupported(entity.value)) ||
(entity.key === 'vlcTelnet' && this.isVLCTelnetSupported(entity.value))) {
service = { key: entity.key, value: entity.value };
return false;
}
@ -20126,6 +20146,12 @@ class PlayController {
}
return false;
};
this.isVLCTelnetSupported = (entityName) => {
return ((this.entityStates[entityName] &&
!lodash.isNil(this.entityStates[entityName].attributes) &&
this.entityStates[entityName].state !== 'unavailable') ||
!lodash.isEqual(this.runBefore, false));
};
this.isCastSupported = (entityName) => {
return ((this.entityStates[entityName] &&
!lodash.isNil(this.entityStates[entityName].attributes) &&
@ -20396,7 +20422,8 @@ class PlexMeetsHomeAssistantEditor extends HTMLElement {
lodash.isEqual(entityRegistry.platform, 'kodi') ||
lodash.isEqual(entityRegistry.platform, 'androidtv') ||
lodash.isEqual(entityRegistry.platform, 'input_select') ||
lodash.isEqual(entityRegistry.platform, 'input_text')) {
lodash.isEqual(entityRegistry.platform, 'input_text') ||
lodash.isEqual(entityRegistry.platform, 'vlc_telnet')) {
const entityName = `${entityRegistry.platform} | ${entityRegistry.entity_id}`;
entities.appendChild(addDropdownItem(entityName));
addedEntityStrings.push(entityName);
@ -22038,11 +22065,15 @@ class PlexMeetsHomeAssistant extends HTMLElement {
realEntityString = entityString.split(' | ')[3];
isPlexPlayer = true;
}
else if (lodash.isPlainObject(entityString)) {
realEntityString = entityString[Object.keys(entityString)[0]];
}
else if (lodash.startsWith(entityString, 'androidtv | ') ||
lodash.startsWith(entityString, 'kodi | ') ||
lodash.startsWith(entityString, 'cast | ') ||
lodash.startsWith(entityString, 'input_select | ') ||
lodash.startsWith(entityString, 'input_text | ')) {
lodash.startsWith(entityString, 'input_text | ') ||
lodash.startsWith(entityString, 'vlc_telnet | ')) {
// eslint-disable-next-line prefer-destructuring
realEntityString = entityString.split(' | ')[1];
isPlexPlayer = false;
@ -22093,7 +22124,15 @@ class PlexMeetsHomeAssistant extends HTMLElement {
}
entityObj.inputText.push(entityInRegister.entity_id);
break;
// pass
case 'vlc_telnet':
if (lodash.isNil(entityObj.vlcTelnet)) {
// eslint-disable-next-line no-param-reassign
entityObj.vlcTelnet = [];
}
entityObj.vlcTelnet.push(entityInRegister.entity_id);
break;
default:
console.error(`Entity ${entityInRegister.entity_id} is not supported.`);
}
}
});

@ -12,7 +12,8 @@ const supported: any = {
kodi: ['movie', 'episode', 'epg'],
androidtv: ['movie', 'show', 'season', 'episode', 'clip', 'track', 'artist', 'album'],
plexPlayer: ['movie', 'show', 'season', 'episode', 'clip', 'track', 'artist', 'album'],
cast: ['movie', 'episode', 'artist', 'album', 'track']
cast: ['movie', 'episode', 'artist', 'album', 'track'],
vlcTelnet: ['track']
};
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.

@ -296,7 +296,8 @@ class PlexMeetsHomeAssistantEditor extends HTMLElement {
_.isEqual(entityRegistry.platform, 'kodi') ||
_.isEqual(entityRegistry.platform, 'androidtv') ||
_.isEqual(entityRegistry.platform, 'input_select') ||
_.isEqual(entityRegistry.platform, 'input_text')
_.isEqual(entityRegistry.platform, 'input_text') ||
_.isEqual(entityRegistry.platform, 'vlc_telnet')
) {
const entityName = `${entityRegistry.platform} | ${entityRegistry.entity_id}`;
entities.appendChild(addDropdownItem(entityName));

@ -156,6 +156,9 @@ class PlayController {
case 'kodi':
await this.playViaKodi(entity.value, data, data.type);
break;
case 'vlcTelnet':
await this.playViaVLCTelnet(entity.value, data, data.type);
break;
case 'androidtv':
if (_.isEqual(data.type, 'epg')) {
const session = `${Math.floor(Date.now() / 1000)}`;
@ -278,8 +281,19 @@ class PlayController {
}
};
private plexPlayerCreateQueue = async (movieID: number, plex: Plex): Promise<Record<string, number>> => {
const url = `${plex.getBasicURL()}/playQueues?type=video&shuffle=0&repeat=0&continuous=1&own=1&uri=server://${await plex.getServerID()}/com.plexapp.plugins.library/library/metadata/${movieID}`;
private plexPlayerCreateQueue = async (
key: string,
plex: Plex,
type: string,
shuffle = false,
repeat = false,
continuous = false
): Promise<Record<string, number>> => {
const url = `${plex.getBasicURL()}/playQueues?type=${type}&shuffle=${shuffle ? '1' : '0'}&repeat=${
repeat ? '1' : '0'
}&continuous=${
continuous ? '1' : '0'
}&own=1&uri=server://${await plex.getServerID()}/com.plexapp.plugins.library${key}`;
const plexResponse = await axios({
method: 'post',
@ -305,7 +319,11 @@ class PlayController {
if (_.isObject(entity) && !_.isNil(entity.plex)) {
plex = entity.plex;
}
const { playQueueID, playQueueSelectedMetadataItemID } = await this.plexPlayerCreateQueue(movieID, this.plex);
const { playQueueID, playQueueSelectedMetadataItemID } = await this.plexPlayerCreateQueue(
`/library/metadata/${movieID}`,
this.plex,
'video'
);
let url = plex.getBasicURL();
url += `/player/playback/playMedia`;
@ -422,6 +440,23 @@ class PlayController {
}
};
private playViaVLCTelnet = async (entityName: string, data: Record<string, any>, type: string): Promise<void> => {
switch (type) {
case 'track':
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: 'music',
// eslint-disable-next-line @typescript-eslint/camelcase
media_content_id: this.plex.authorizeURL(`${this.plex.getBasicURL()}${data.Media[0].Part[0].key}`)
});
break;
default:
console.error(`Type ${type} is not supported on entity ${entityName}.`);
}
};
private playViaCast = (entityName: string, mediaLink: string, contentType = 'video'): void => {
if (_.isEqual(contentType, 'video')) {
this.hass.callService('media_player', 'play_media', {
@ -654,12 +689,12 @@ class PlayController {
_.forEach(entities, entity => {
if (_.includes(this.supported[entity.key], data.type)) {
// todo: load info in this.entityStates otherwise this will never work for input selects and templates
if (
(entity.key === 'kodi' && this.isKodiSupported(entity.value)) ||
(entity.key === 'androidtv' && this.isAndroidTVSupported(entity.value)) ||
(entity.key === 'plexPlayer' && this.isPlexPlayerSupported(entity.value)) ||
(entity.key === 'cast' && this.isCastSupported(entity.value))
(entity.key === 'cast' && this.isCastSupported(entity.value)) ||
(entity.key === 'vlcTelnet' && this.isVLCTelnetSupported(entity.value))
) {
service = { key: entity.key, value: entity.value };
return false;
@ -841,6 +876,15 @@ class PlayController {
return false;
};
private isVLCTelnetSupported = (entityName: string): boolean => {
return (
(this.entityStates[entityName] &&
!_.isNil(this.entityStates[entityName].attributes) &&
this.entityStates[entityName].state !== 'unavailable') ||
!_.isEqual(this.runBefore, false)
);
};
private isCastSupported = (entityName: string): boolean => {
return (
(this.entityStates[entityName] &&

@ -295,12 +295,15 @@ class PlexMeetsHomeAssistant extends HTMLElement {
// eslint-disable-next-line prefer-destructuring
realEntityString = entityString.split(' | ')[3];
isPlexPlayer = true;
} else if (_.isPlainObject(entityString)) {
realEntityString = entityString[(Object.keys(entityString) as Record<string, any>)[0]];
} else if (
_.startsWith(entityString, 'androidtv | ') ||
_.startsWith(entityString, 'kodi | ') ||
_.startsWith(entityString, 'cast | ') ||
_.startsWith(entityString, 'input_select | ') ||
_.startsWith(entityString, 'input_text | ')
_.startsWith(entityString, 'input_text | ') ||
_.startsWith(entityString, 'vlc_telnet | ')
) {
// eslint-disable-next-line prefer-destructuring
realEntityString = entityString.split(' | ')[1];
@ -351,8 +354,15 @@ class PlexMeetsHomeAssistant extends HTMLElement {
}
entityObj.inputText.push(entityInRegister.entity_id);
break;
case 'vlc_telnet':
if (_.isNil(entityObj.vlcTelnet)) {
// eslint-disable-next-line no-param-reassign
entityObj.vlcTelnet = [];
}
entityObj.vlcTelnet.push(entityInRegister.entity_id);
break;
default:
// pass
console.error(`Entity ${entityInRegister.entity_id} is not supported.`);
}
}
});

Loading…
Cancel
Save