263 lines
8.3 KiB
Python
263 lines
8.3 KiB
Python
import pychromecast
|
|
|
|
from platypush.plugins import Plugin, action
|
|
|
|
|
|
class MediaChromecastPlugin(Plugin):
|
|
"""
|
|
Plugin to interact with Chromecast devices
|
|
|
|
Requires:
|
|
|
|
* **pychromecast** (``pip install pychromecast``)
|
|
"""
|
|
|
|
STREAM_TYPE_UNKNOWN = "UNKNOWN"
|
|
STREAM_TYPE_BUFFERED = "BUFFERED"
|
|
STREAM_TYPE_LIVE = "LIVE"
|
|
|
|
def __init__(self, chromecast=None, *args, **kwargs):
|
|
"""
|
|
:param chromecast: Default Chromecast to cast to if no name is specified
|
|
:type chromecast: str
|
|
"""
|
|
|
|
super().__init__(*args, **kwargs)
|
|
|
|
self.chromecast = chromecast
|
|
self.chromecasts = {}
|
|
|
|
|
|
@action
|
|
def get_chromecasts(self):
|
|
"""
|
|
Get the list of Chromecast devices
|
|
"""
|
|
|
|
return [ {
|
|
'type': cc.cast_type,
|
|
'name': cc.name,
|
|
'manufacturer': cc.device.manufacturer,
|
|
'model_name': cc.model_name,
|
|
'uuid': str(cc.device.uuid),
|
|
'address': cc.host,
|
|
'port': cc.port,
|
|
|
|
'status': ({
|
|
'app': {
|
|
'id': cc.app_id,
|
|
'name': cc.app_display_name,
|
|
},
|
|
|
|
'is_active_input': cc.status.is_active_input,
|
|
'is_stand_by': cc.status.is_stand_by,
|
|
'is_idle': cc.is_idle,
|
|
'namespaces': cc.status.namespaces,
|
|
'volume': round(100*cc.status.volume_level, 2),
|
|
'muted': cc.status.volume_muted,
|
|
} if cc.status else {}),
|
|
} for cc in pychromecast.get_chromecasts() ]
|
|
|
|
|
|
def _get_chromecast(self, chromecast=None):
|
|
if not chromecast:
|
|
if not self.chromecast:
|
|
raise RuntimeError('No Chromecast specified nor default Chromecast configured')
|
|
chromecast = self.chromecast
|
|
|
|
|
|
if chromecast not in self.chromecasts:
|
|
chromecasts = pychromecast.get_chromecasts()
|
|
cast = next(cc for cc in pychromecast.get_chromecasts()
|
|
if cc.device.friendly_name == chromecast)
|
|
self.chromecasts[chromecast] = cast
|
|
else:
|
|
cast = self.chromecasts[chromecast]
|
|
|
|
if not cast:
|
|
raise RuntimeError('No such Chromecast: {}'.format(chromecast))
|
|
|
|
return cast
|
|
|
|
@action
|
|
def cast_media(self, media, content_type, chromecast=None, title=None,
|
|
image_url=None, autoplay=True, current_time=0,
|
|
stream_type=STREAM_TYPE_BUFFERED, subtitles=None,
|
|
subtitles_lang='en-US', subtitles_mime='text/vtt',
|
|
subtitle_id=1):
|
|
"""
|
|
Cast media to a visible Chromecast
|
|
|
|
:param media: Media to cast
|
|
:type media: str
|
|
|
|
:param content_type: Content type
|
|
:type content_type: str
|
|
|
|
:param chromecast: Chromecast to cast to. If none is specified, then the default configured Chromecast will be used.
|
|
:type chromecast: str
|
|
|
|
:param title: Optional title
|
|
:type title: str
|
|
|
|
:param image_url: URL of the image to use for the thumbnail
|
|
:type image_url: str
|
|
|
|
:param autoplay: Set it to false if you don't want the content to start playing immediately (default: true)
|
|
:type autoplay: bool
|
|
|
|
:param current_time: Time to start the playback in seconds (default: 0)
|
|
:type current_time: int
|
|
|
|
:param stream_type: Type of stream to cast. Can be BUFFERED (default), LIVE or UNKNOWN
|
|
:type stream_type: str
|
|
|
|
:param subtitles: URL of the subtitles to be shown
|
|
:type subtitles: str
|
|
|
|
:param subtitles_lang: Subtitles language (default: en-US)
|
|
:type subtitles_lang: str
|
|
|
|
:param subtitles_mime: Subtitles MIME type (default: text/vtt)
|
|
:type subtitles_mime: str
|
|
|
|
:param subtitle_id: ID of the subtitles to be loaded (default: 1)
|
|
:type subtitle_id: int
|
|
"""
|
|
|
|
cast = self._get_chromecast(chromecast)
|
|
cast.wait()
|
|
mc = cast.media_controller
|
|
mc.play_media(media, content_type, title=title, thumb=image_url,
|
|
current_time=current_time, autoplay=autoplay,
|
|
stream_type=stream_type, subtitles=subtitles,
|
|
subtitles_lang=subtitles_lang, subtitles_mime=subtitles_mime,
|
|
subtitle_id=subtitle_id)
|
|
|
|
mc.block_until_active()
|
|
|
|
@action
|
|
def disconnect(self, chromecast=None, timeout=None, blocking=True):
|
|
"""
|
|
Disconnect a Chromecast and wait for it to terminate
|
|
|
|
:param chromecast: Chromecast to cast to. If none is specified, then the default configured Chromecast will be used.
|
|
:type chromecast: str
|
|
|
|
:param timeout: Number of seconds to wait for disconnection (default: None: block until termination)
|
|
:type timeout: float
|
|
|
|
:param blocking: If set (default), then the code will wait until disconnection, otherwise it will return immediately.
|
|
:type blocking: bool
|
|
"""
|
|
|
|
cast = self._get_chromecast(chromecast)
|
|
cast.disconnect(timeout=timeout, blocking=blocking)
|
|
|
|
@action
|
|
def join(self, chromecast=None, timeout=None, blocking=True):
|
|
"""
|
|
Blocks the thread until the Chromecast connection is terminated.
|
|
|
|
:param chromecast: Chromecast to cast to. If none is specified, then the default configured Chromecast will be used.
|
|
:type chromecast: str
|
|
|
|
:param timeout: Number of seconds to wait for disconnection (default: None: block until termination)
|
|
:type timeout: float
|
|
|
|
:param blocking: If set (default), then the code will wait until disconnection, otherwise it will return immediately.
|
|
:type blocking: bool
|
|
"""
|
|
|
|
cast = self._get_chromecast(chromecast)
|
|
cast.join(timeout=timeout, blocking=blocking)
|
|
|
|
@action
|
|
def quit_app(self, chromecast=None):
|
|
"""
|
|
Exits the current app on the Chromecast
|
|
|
|
:param chromecast: Chromecast to cast to. If none is specified, then the default configured Chromecast will be used.
|
|
:type chromecast: str
|
|
"""
|
|
|
|
cast = self._get_chromecast(chromecast)
|
|
cast.quit_app()
|
|
|
|
@action
|
|
def reboot(self, chromecast=None):
|
|
"""
|
|
Reboots the Chromecast
|
|
|
|
:param chromecast: Chromecast to cast to. If none is specified, then the default configured Chromecast will be used.
|
|
:type chromecast: str
|
|
"""
|
|
|
|
cast = self._get_chromecast(chromecast)
|
|
cast.reboot()
|
|
|
|
@action
|
|
def set_volume(self, volume, chromecast=None):
|
|
"""
|
|
Set the Chromecast volume
|
|
|
|
:param volume: Volume to be set, between 0 and 100
|
|
:type volume: float
|
|
|
|
:param chromecast: Chromecast to cast to. If none is specified, then the default configured Chromecast will be used.
|
|
:type chromecast: str
|
|
"""
|
|
|
|
cast = self._get_chromecast(chromecast)
|
|
cast.set_volume(volume/100)
|
|
|
|
@action
|
|
def volume_up(self, chromecast=None, delta=10):
|
|
"""
|
|
Turn up the Chromecast volume by 10% or delta.
|
|
|
|
:param chromecast: Chromecast to cast to. If none is specified, then the default configured Chromecast will be used.
|
|
:type chromecast: str
|
|
|
|
:param delta: Volume increment between 0 and 100 (default: 100%)
|
|
:type delta: float
|
|
"""
|
|
|
|
cast = self._get_chromecast(chromecast)
|
|
delta /= 100
|
|
cast.volume_up(min(delta, 1))
|
|
|
|
|
|
@action
|
|
def volume_down(self, chromecast=None, delta=10):
|
|
"""
|
|
Turn down the Chromecast volume by 10% or delta.
|
|
|
|
:param chromecast: Chromecast to cast to. If none is specified, then the default configured Chromecast will be used.
|
|
:type chromecast: str
|
|
|
|
:param delta: Volume decrement between 0 and 100 (default: 100%)
|
|
:type delta: float
|
|
"""
|
|
|
|
cast = self._get_chromecast(chromecast)
|
|
delta /= 100
|
|
cast.volume_down(max(delta, 0))
|
|
|
|
|
|
@action
|
|
def toggle_mute(self, chromecast=None):
|
|
"""
|
|
Toggle the mute status on the Chromecast
|
|
|
|
:param chromecast: Chromecast to cast to. If none is specified, then the default configured Chromecast will be used.
|
|
:type chromecast: str
|
|
"""
|
|
|
|
cast = self._get_chromecast(chromecast)
|
|
cast.set_volume_muted(not cast.status.volume_muted)
|
|
|
|
|
|
# vim:sw=4:ts=4:et:
|
|
|