98_GOOGLECAST.pm 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671
  1. #############################################################
  2. #
  3. # GOOGLECAST.pm (c) by Dominik Karall, 2016-2017
  4. # dominik karall at gmail dot com
  5. # $Id: 98_GOOGLECAST.pm 14884 2017-08-12 21:22:56Z dominik $
  6. #
  7. # FHEM module to communicate with Google Cast devices
  8. # e.g. Chromecast Video, Chromecast Audio, Google Home
  9. #
  10. # Version: 2.0.0
  11. #
  12. #############################################################
  13. #
  14. # v2.0.0 - 20170812
  15. # - CHANGE: renamed to 98_GOOGLECAST.pm
  16. # - CHANGE: removed favoriteName_X attribute, it was never used
  17. # - BUGFIX: updated commandref with further required packages
  18. # - FEATURE: state reading now represents status (online, offline,
  19. # playing, paused, buffering)
  20. # - FEATURE: new readings mediaContentId, mediaCurrentPosition,
  21. # mediaDuration, mediaPlayerState, mediaStreamType
  22. # - BUGFIX: change volume to represent integer values only
  23. #
  24. # v1.0.7 - 20170804
  25. # - BUGFIX: fix reconnection in some cases
  26. #
  27. # v1.0.6 - 20170705
  28. # - BUGFIX: speed up youtube video URL extraction with youtube_dl
  29. # - BUGFIX: fixed one more issue when chromecast offline
  30. # - BUGFIX: improved performance by adding socket to FHEM main loop
  31. #
  32. # v1.0.5 - 20170704
  33. # - BUGFIX: hopefuly fixed the annoying hangs when chromecast offline
  34. # - FEATURE: add presence reading (online/offline)
  35. #
  36. # v1.0.4 - 20170101
  37. # - FEATURE: support all services supported by youtube-dl
  38. # https://github.com/rg3/youtube-dl/blob/master/docs/supportedsites.md
  39. # playlists not yet supported!
  40. # - BUGFIX: support non-blocking chromecast search
  41. #
  42. # v1.0.3 - 20161219
  43. # - FEATURE: support volume
  44. # - FEATURE: add new readings and removed
  45. # castStatus, mediaStatus reading
  46. # - FEATURE: add attribute favoriteURL_[1-5]
  47. # - FEATURE: add playFavorite [1-5] set function
  48. # - FEATURE: retry init chromecast every 10s if not found on startup
  49. # - BUGFIX: support special characters for device name
  50. #
  51. # v1.0.2 - 20161216
  52. # - FEATURE: support play of every mime type which is supported
  53. # by Chromecast (see https://developers.google.com/cast/docs/media)
  54. # including youtube URLs
  55. # - CHANGE: change play* methods to play <url>
  56. # - FEATURE: support very simple .m3u which contain only URL
  57. # - BUGFIX: non-blocking playYoutube
  58. # - BUGFIX: fix play if media player is already running
  59. #
  60. # v1.0.1 - 20161211
  61. # - FEATURE: support playYoutube <youtubelink>
  62. #
  63. # v1.0.0 - 20161015
  64. # - FEATURE: first public release
  65. #
  66. # TODO
  67. # - check spotify integration
  68. # - support youtube playlists
  69. #
  70. # NOTES
  71. # def play_media(self, url, content_type, title=None, thumb=None,
  72. # current_time=0, autoplay=True,
  73. # stream_type=STREAM_TYPE_BUFFERED,
  74. # metadata=None, subtitles=None, subtitles_lang='en-US',
  75. # subtitles_mime='text/vtt', subtitle_id=1):
  76. # """
  77. # Plays media on the Chromecast. Start default media receiver if not
  78. # already started.
  79. # Parameters:
  80. # url: str - url of the media.
  81. # content_type: str - mime type. Example: 'video/mp4'.
  82. # title: str - title of the media.
  83. # thumb: str - thumbnail image url.
  84. # current_time: float - seconds from the beginning of the media
  85. # to start playback.
  86. # autoplay: bool - whether the media will automatically play.
  87. # stream_type: str - describes the type of media artifact as one of the
  88. # following: "NONE", "BUFFERED", "LIVE".
  89. # subtitles: str - url of subtitle file to be shown on chromecast.
  90. # subtitles_lang: str - language for subtitles.
  91. # subtitles_mime: str - mimetype of subtitles.
  92. # subtitle_id: int - id of subtitle to be loaded.
  93. # metadata: dict - media metadata object, one of the following:
  94. # GenericMediaMetadata, MovieMediaMetadata, TvShowMediaMetadata,
  95. # MusicTrackMediaMetadata, PhotoMediaMetadata.
  96. # Docs:
  97. # https://developers.google.com/cast/docs/reference/messages#MediaData
  98. # """
  99. #
  100. #############################################################
  101. package main;
  102. use strict;
  103. use warnings;
  104. use Blocking;
  105. use Encode;
  106. use SetExtensions;
  107. use LWP::UserAgent;
  108. sub GOOGLECAST_Initialize($) {
  109. my ($hash) = @_;
  110. $hash->{DefFn} = 'GOOGLECAST_Define';
  111. $hash->{UndefFn} = 'GOOGLECAST_Undef';
  112. $hash->{GetFn} = 'GOOGLECAST_Get';
  113. $hash->{SetFn} = 'GOOGLECAST_Set';
  114. $hash->{ReadFn} = 'GOOGLECAST_Read';
  115. $hash->{AttrFn} = 'GOOGLECAST_Attribute';
  116. $hash->{AttrList} = "favoriteURL_1 favoriteURL_2 favoriteURL_3 favoriteURL_4 ".
  117. "favoriteURL_5 ".$readingFnAttributes;
  118. Log3 $hash, 3, "GOOGLECAST: GoogleCast v2.0.0";
  119. return undef;
  120. }
  121. sub GOOGLECAST_Define($$) {
  122. my ($hash, $def) = @_;
  123. my @a = split("[ \t]+", $def);
  124. my $name = $a[0];
  125. $hash->{STATE} = "initialized";
  126. if (int(@a) > 3) {
  127. return 'GOOGLECAST: Wrong syntax, must be define <name> GOOGLECAST <device name>';
  128. } elsif(int(@a) == 3) {
  129. Log3 $hash, 3, "GOOGLECAST: $a[2] initializing...";
  130. $hash->{CCNAME} = $a[2];
  131. GOOGLECAST_updateReading($hash, "presence", "offline");
  132. GOOGLECAST_initDevice($hash);
  133. }
  134. return undef;
  135. }
  136. sub GOOGLECAST_findChromecasts {
  137. my ($string) = @_;
  138. my ($name) = split("\\|", $string);
  139. my $result = "$name";
  140. my @ccResult = GOOGLECAST_findChromecastsPython();
  141. foreach my $ref_cc (@ccResult) {
  142. my @cc = @$ref_cc;
  143. $result .= "|CCDEVICE|".$cc[0]."|".$cc[1]."|".$cc[2]."|".$cc[3]."|".$cc[4];
  144. }
  145. Log3 $name, 4, "GOOGLECAST: search result: $result";
  146. return $result;
  147. }
  148. sub GOOGLECAST_initDevice {
  149. my ($hash) = @_;
  150. my $devName = $hash->{CCNAME};
  151. BlockingCall("GOOGLECAST_findChromecasts", $hash->{NAME}, "GOOGLECAST_findChromecastsResult");
  152. return undef;
  153. }
  154. sub GOOGLECAST_findChromecastsResult {
  155. my ($string) = @_;
  156. my ($name, @ccResult) = split("\\|", $string);
  157. my $hash = $main::defs{$name};
  158. my $devName = $hash->{CCNAME};
  159. $hash->{helper}{ccdevice} = "";
  160. for my $i (0..$#ccResult) {
  161. if($ccResult[$i] eq "CCDEVICE" and $ccResult[$i+5] eq $devName) {
  162. Log3 $hash, 4, "GOOGLECAST ($hash->{NAME}): init cast device $devName";
  163. eval {
  164. $hash->{helper}{ccdevice} = GOOGLECAST_createChromecastPython($ccResult[$i+1],$ccResult[$i+2],$ccResult[$i+3],$ccResult[$i+4],$ccResult[$i+5]);
  165. };
  166. if($@) {
  167. $hash->{helper}{ccdevice} = "";
  168. }
  169. Log3 $hash, 4, "GOOGLECAST ($hash->{NAME}): device initialized";
  170. }
  171. }
  172. if($hash->{helper}{ccdevice} eq "") {
  173. Log3 $hash, 4, "GOOGLECAST: $devName not found, retry in 10s.";
  174. InternalTimer(gettimeofday()+10, "GOOGLECAST_initDevice", $hash, 0);
  175. return undef;
  176. }
  177. Log3 $hash, 3, "GOOGLECAST: $devName initialized successfully";
  178. GOOGLECAST_addSocketToMainloop($hash);
  179. GOOGLECAST_checkConnection($hash);
  180. return undef;
  181. }
  182. sub GOOGLECAST_Attribute($$$$) {
  183. my ($mode, $devName, $attrName, $attrValue) = @_;
  184. if($mode eq "set") {
  185. } elsif($mode eq "del") {
  186. }
  187. return undef;
  188. }
  189. sub GOOGLECAST_Set($@) {
  190. my ($hash, $name, @params) = @_;
  191. my $workType = shift(@params);
  192. my $list = "stop:noArg pause:noArg quitApp:noArg play playFavorite:1,2,3,4,5 volume:slider,0,1,100";
  193. # check parameters for set function
  194. if($workType eq "?") {
  195. return SetExtensions($hash, $list, $name, $workType, @params);
  196. }
  197. if($workType eq "stop") {
  198. GOOGLECAST_setStop($hash);
  199. } elsif($workType eq "pause") {
  200. GOOGLECAST_setPause($hash);
  201. } elsif($workType eq "play") {
  202. GOOGLECAST_setPlay($hash, $params[0]);
  203. } elsif($workType eq "playFavorite") {
  204. GOOGLECAST_setPlayFavorite($hash, $params[0]);
  205. } elsif($workType eq "quitApp") {
  206. GOOGLECAST_setQuitApp($hash);
  207. } elsif($workType eq "volume") {
  208. GOOGLECAST_setVolume($hash, $params[0]);
  209. } else {
  210. return SetExtensions($hash, $list, $name, $workType, @params);
  211. }
  212. return undef;
  213. }
  214. ### volume ###
  215. sub GOOGLECAST_setVolume {
  216. my ($hash, $volume) = @_;
  217. $volume = $volume/100;
  218. eval {
  219. $hash->{helper}{ccdevice}->set_volume($volume);
  220. };
  221. }
  222. ### playType ###
  223. sub GOOGLECAST_setPlayType {
  224. my ($hash, $url, $mime) = @_;
  225. eval {
  226. $hash->{helper}{ccdevice}->{media_controller}->play_media($url, $mime);
  227. };
  228. return undef;
  229. }
  230. sub GOOGLECAST_setPlayType_String {
  231. my ($string) = @_;
  232. my ($name, $url, $mime) = split("\\|", $string);
  233. my $hash = $main::defs{$name};
  234. if($mime ne "" && $url ne "") {
  235. GOOGLECAST_setPlayType($hash, $url, $mime);
  236. }
  237. }
  238. ### playMedia ###
  239. sub GOOGLECAST_setPlayMedia {
  240. my ($hash, $url) = @_;
  241. BlockingCall("GOOGLECAST_setPlayMediaBlocking", $hash->{NAME}."|".$url, "GOOGLECAST_setPlayType_String");
  242. return undef;
  243. }
  244. sub GOOGLECAST_setPlayMedia_String {
  245. my ($string) = @_;
  246. my ($name, $videoUrl, $origUrl) = split("\\|", $string);
  247. my $hash = $main::defs{$name};
  248. if($videoUrl ne "") {
  249. GOOGLECAST_setPlayMedia($hash, $videoUrl);
  250. } else {
  251. GOOGLECAST_setPlayMedia($hash, $origUrl);
  252. }
  253. }
  254. sub GOOGLECAST_setPlayMediaBlocking {
  255. my ($string) = @_;
  256. my ($name, $url) = split("\\|", $string);
  257. #$url = "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"
  258. #$url = "http://swr-mp3-m-swr3.akacast.akamaistream.net:80/7/720/137136/v1/gnl.akacast.akamaistream.net/swr-mp3-m-swr3";
  259. my $ua = new LWP::UserAgent(agent => 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.5) Gecko/20060719 Firefox/1.5.0.5');
  260. $ua->max_size(0);
  261. my $resp = $ua->get($url);
  262. my $mime = $resp->header('Content-Type');
  263. if($mime eq "audio/x-mpegurl") {
  264. $mime = "audio/mpeg";
  265. $url = $resp->decoded_content;
  266. $url =~ s/\R//g;
  267. }
  268. return $name."|".$url."|".$mime;
  269. }
  270. ### playYoutue ###
  271. sub GOOGLECAST_setPlayYtDl {
  272. my ($hash, $ytUrl) = @_;
  273. BlockingCall("GOOGLECAST_setPlayYtDlBlocking", $hash->{NAME}."|".$ytUrl, "GOOGLECAST_setPlayMedia_String");
  274. return undef;
  275. }
  276. sub GOOGLECAST_setPlayYtDlBlocking {
  277. my ($string) = @_;
  278. my ($name, $ytUrl) = split("\\|", $string);
  279. my $videoUrl = "";
  280. eval {
  281. $videoUrl = GOOGLECAST_getYTVideoURLPython($ytUrl);
  282. };
  283. return $name."|".$videoUrl."|".$ytUrl;
  284. }
  285. ### stop ###
  286. sub GOOGLECAST_setStop {
  287. my ($hash) = @_;
  288. eval {
  289. $hash->{helper}{ccdevice}->{media_controller}->stop();
  290. };
  291. return undef;
  292. }
  293. ### playFavorite ###
  294. sub GOOGLECAST_setPlayFavorite {
  295. my ($hash, $favoriteNr) = @_;
  296. GOOGLECAST_setPlay($hash, AttrVal($hash->{NAME}, "favoriteURL_".$favoriteNr, ""));
  297. return undef;
  298. }
  299. ### play ###
  300. sub GOOGLECAST_setPlay {
  301. my ($hash, $url) = @_;
  302. if(defined($url)) {
  303. #support streams are listed here
  304. #https://github.com/rg3/youtube-dl/blob/master/docs/supportedsites.md
  305. GOOGLECAST_setPlayYtDl($hash, $url);
  306. } else {
  307. eval {
  308. $hash->{helper}{ccdevice}->{media_controller}->play();
  309. };
  310. }
  311. return undef;
  312. }
  313. ### pause ###
  314. sub GOOGLECAST_setPause {
  315. my ($hash) = @_;
  316. eval {
  317. $hash->{helper}{ccdevice}->{media_controller}->pause();
  318. };
  319. return undef;
  320. }
  321. ### quitApp ###
  322. sub GOOGLECAST_setQuitApp {
  323. my ($hash) = @_;
  324. eval {
  325. $hash->{helper}{ccdevice}->quit_app();
  326. };
  327. return undef;
  328. }
  329. sub GOOGLECAST_Undef($) {
  330. my ($hash) = @_;
  331. #remove internal timer
  332. RemoveInternalTimer($hash);
  333. return undef;
  334. }
  335. sub GOOGLECAST_Get($$) {
  336. return undef;
  337. }
  338. sub GOOGLECAST_updateReading {
  339. my ($hash, $readingName, $value) = @_;
  340. my $oldValue = ReadingsVal($hash->{NAME}, $readingName, "");
  341. if(!defined($value)) {
  342. $value = "";
  343. }
  344. if($oldValue ne $value) {
  345. readingsSingleUpdate($hash, $readingName, $value, 1);
  346. }
  347. }
  348. sub GOOGLECAST_newChash {
  349. my ($hash, $socket, $chash) = @_;
  350. $chash->{TYPE} = $hash->{TYPE};
  351. $chash->{UDN} = -1;
  352. $chash->{NR} = $devcount++;
  353. $chash->{phash} = $hash;
  354. $chash->{PNAME} = $hash->{NAME};
  355. $chash->{CD} = $socket;
  356. $chash->{FD} = $socket->fileno();
  357. #$chash->{PORT} = $socket->sockport if( $socket->sockport );
  358. $chash->{TEMPORARY} = 1;
  359. $attr{$chash->{NAME}}{room} = 'hidden';
  360. $defs{$chash->{NAME}} = $chash;
  361. $selectlist{$chash->{NAME}} = $chash;
  362. }
  363. sub GOOGLECAST_addSocketToMainloop {
  364. my ($hash) = @_;
  365. my $sock;
  366. eval {
  367. $sock = $hash->{helper}{ccdevice}->{socket_client}->get_socket();
  368. };
  369. my $chash = GOOGLECAST_newChash($hash, $sock, {NAME => "GOOGLECAST-".$hash->{NAME}});
  370. return undef;
  371. }
  372. sub GOOGLECAST_checkConnection {
  373. my ($hash) = @_;
  374. eval {
  375. Log3 $hash, 5, "GOOGLECAST ($hash->{NAME}): run_once";
  376. $hash->{helper}{ccdevice}->{socket_client}->run_once();
  377. };
  378. if($@ || !defined($selectlist{"GOOGLECAST-".$hash->{NAME}})) {
  379. Log3 $hash, 4, "GOOGLECAST ($hash->{NAME}): checkConnection, connection failure, reconnect...";
  380. GOOGLECAST_initDevice($hash);
  381. GOOGLECAST_updateReading($hash, "presence", "offline");
  382. return undef;
  383. }
  384. InternalTimer(gettimeofday()+10, "GOOGLECAST_checkConnection", $hash, 0);
  385. return undef;
  386. }
  387. sub GOOGLECAST_Read {
  388. my ($hash) = @_;
  389. my $name = $hash->{NAME};
  390. $hash = $hash->{phash};
  391. eval {
  392. Log3 $hash, 5, "GOOGLECAST ($hash->{NAME}): run_once";
  393. $hash->{helper}{ccdevice}->{socket_client}->run_once();
  394. };
  395. if($@) {
  396. Log3 $hash, 4, "GOOGLECAST ($hash->{NAME}): connection failure, reconnect...";
  397. eval {
  398. delete($selectlist{$name});
  399. };
  400. GOOGLECAST_initDevice($hash);
  401. GOOGLECAST_updateReading($hash, "presence", "offline");
  402. return undef;
  403. }
  404. GOOGLECAST_updateReading($hash, "presence", "online");
  405. GOOGLECAST_updateReading($hash, "name", $hash->{helper}{ccdevice}->{name});
  406. GOOGLECAST_updateReading($hash, "model", $hash->{helper}{ccdevice}->{model_name});
  407. GOOGLECAST_updateReading($hash, "uuid", $hash->{helper}{ccdevice}->{uuid});
  408. GOOGLECAST_updateReading($hash, "castType", $hash->{helper}{ccdevice}->{cast_type});
  409. GOOGLECAST_updateReading($hash, "model", $hash->{helper}{ccdevice}->{model_name});
  410. GOOGLECAST_updateReading($hash, "appId", $hash->{helper}{ccdevice}->{app_id});
  411. GOOGLECAST_updateReading($hash, "appName", $hash->{helper}{ccdevice}->{app_display_name});
  412. GOOGLECAST_updateReading($hash, "idle", $hash->{helper}{ccdevice}->{is_idle});
  413. my $newStatus = $hash->{helper}{ccdevice}->{media_controller}->{status};
  414. if(defined($newStatus)) {
  415. #GOOGLECAST_updateReading($hash, "mediaStatus", $newStatus);
  416. GOOGLECAST_updateReading($hash, "mediaPlayerState", $newStatus->{player_state});
  417. GOOGLECAST_updateReading($hash, "mediaContentId", $newStatus->{content_id});
  418. GOOGLECAST_updateReading($hash, "mediaDuration", $newStatus->{duration});
  419. GOOGLECAST_updateReading($hash, "mediaCurrentPosition", $newStatus->{current_time});
  420. GOOGLECAST_updateReading($hash, "mediaStreamType", $newStatus->{stream_type});
  421. GOOGLECAST_updateReading($hash, "mediaTitle", $newStatus->{title});
  422. GOOGLECAST_updateReading($hash, "mediaSeriesTitle", $newStatus->{series_title});
  423. GOOGLECAST_updateReading($hash, "mediaSeason", $newStatus->{season});
  424. GOOGLECAST_updateReading($hash, "mediaEpisode", $newStatus->{episode});
  425. GOOGLECAST_updateReading($hash, "mediaArtist", $newStatus->{artist});
  426. GOOGLECAST_updateReading($hash, "mediaAlbum", $newStatus->{album_name});
  427. GOOGLECAST_updateReading($hash, "mediaAlbumArtist", $newStatus->{album_artist});
  428. GOOGLECAST_updateReading($hash, "mediaTrack", $newStatus->{track});
  429. if(length($newStatus->{images}) > 0) {
  430. GOOGLECAST_updateReading($hash, "mediaImage", $newStatus->{images}[0]->{url});
  431. } else {
  432. GOOGLECAST_updateReading($hash, "mediaImage", "");
  433. }
  434. }
  435. my $newCastStatus = $hash->{helper}{ccdevice}->{status};
  436. if(defined($newCastStatus)) {
  437. #GOOGLECAST_updateReading($hash, "castStatus", $newCastStatus);
  438. GOOGLECAST_updateReading($hash, "volume", int($newCastStatus->{volume_level}*100));
  439. }
  440. my $curStatus = ReadingsVal($hash->{NAME}, "mediaPlayerState", "UNKNOWN");
  441. if($curStatus eq "PLAYING") {
  442. GOOGLECAST_updateReading($hash, "state", "playing");
  443. } elsif($curStatus eq "BUFFERING") {
  444. GOOGLECAST_updateReading($hash, "state", "buffering");
  445. } elsif($curStatus eq "PAUSED") {
  446. GOOGLECAST_updateReading($hash, "state", "paused");
  447. } else {
  448. GOOGLECAST_updateReading($hash, "state", ReadingsVal($hash->{NAME}, "presence", "offline"));
  449. }
  450. return undef;
  451. }
  452. use Inline Python => <<'PYTHON_CODE_END';
  453. from __future__ import unicode_literals
  454. import pychromecast
  455. import time
  456. import logging
  457. import youtube_dl
  458. def GOOGLECAST_findChromecastsPython():
  459. logging.basicConfig(level=logging.CRITICAL)
  460. return pychromecast.discovery.discover_chromecasts()
  461. def GOOGLECAST_createChromecastPython(ip, port, uuid, model_name, friendly_name):
  462. logging.basicConfig(level=logging.CRITICAL)
  463. return pychromecast._get_chromecast_from_host((ip, int(port), uuid, model_name, friendly_name), blocking=False, timeout=0.1, tries=1, retry_wait=0.1)
  464. def GOOGLECAST_getYTVideoURLPython(yt_url):
  465. ydl = youtube_dl.YoutubeDL({'quiet': '1', 'no_warnings': '1'})
  466. with ydl:
  467. result = ydl.extract_info(
  468. yt_url,
  469. download=False # We just want to extract the info
  470. )
  471. if 'entries' in result:
  472. # Can be a playlist or a list of videos
  473. video = result['entries'][0]
  474. else:
  475. # Just a video
  476. video = result
  477. video_url = video['url']
  478. return video_url
  479. PYTHON_CODE_END
  480. 1;
  481. =pod
  482. =item device
  483. =item summary Easily control your Google Cast devices (Video, Audio, Google Home)
  484. =item summary_DE Einfache Steuerung deiner Google Cast Geräte (Video, Audio, Google Home)
  485. =begin html
  486. <a name="GOOGLECAST"></a>
  487. <h3>GOOGLECAST</h3>
  488. <ul>
  489. GOOGLECAST is used to control your Google Cast device<br><br>
  490. <b>Note</b><br>Following packages are required:
  491. <ul>
  492. <li>sudo apt-get install libwww-perl python-enum34 python-dev libextutils-makemaker-cpanfile-perl python-pip cpanminus</li>
  493. <li>sudo pip install netifaces</li>
  494. <li>sudo pip install enum34</li>
  495. <li>sudo pip install pychromecast</li>
  496. <li>sudo pip install youtube-dl</li>
  497. <li>sudo cpanm Inline::Python</li>
  498. </ul>
  499. <br>
  500. <br>
  501. <a name="GOOGLECASTdefine" id="GOOGLECASTdefine"></a>
  502. <b>Define</b>
  503. <ul>
  504. <code>define &lt;name&gt; GOOGLECAST &lt;name&gt;</code><br>
  505. <br>
  506. Example:
  507. <ul>
  508. <code>define livingroom.chromecast GOOGLECAST livingroom</code><br><br>
  509. Wait a few seconds till presence switches to online...<br><br>
  510. <code>set livingroom.chromecast play https://www.youtube.com/watch?v=YE7VzlLtp-4</code><br>
  511. </ul>
  512. <br>
  513. Following media types are supported:<br>
  514. <a href="https://developers.google.com/cast/docs/media">Supported media formats</a><br>
  515. Play with youtube-dl works for following URLs:<br>
  516. <a href="https://rg3.github.io/youtube-dl/supportedsites.html">Supported youtube-dl sites</a><br>
  517. <br>
  518. </ul>
  519. <br>
  520. <a name="GOOGLECASTset" id="GOOGLECASTset"></a>
  521. <b>Set</b>
  522. <ul>
  523. <code>set &lt;name&gt; &lt;command&gt; [&lt;parameter&gt;]</code><br>
  524. The following commands are defined:<br><br>
  525. <ul>
  526. <li><code><b>play</b> URL</code> &nbsp;&nbsp;-&nbsp;&nbsp; play from URL</li>
  527. <li><code><b>play</b></code> &nbsp;&nbsp;-&nbsp;&nbsp; play, like resume if paused previsously</li>
  528. <li><code><b>playFavorite</b></code> &nbsp;&nbsp;-&nbsp;&nbsp; plays URL from favoriteURL_[1-5]</li>
  529. <li><code><b>stop</b></code> &nbsp;&nbsp;-&nbsp;&nbsp; stop, stops current playback</li>
  530. <li><code><b>pause</b></code> &nbsp;&nbsp;-&nbsp;&nbsp; pause</li>
  531. <li><code><b>quitApp</b></code> &nbsp;&nbsp;-&nbsp;&nbsp; quit current application, like YouTube</li>
  532. </ul>
  533. <br>
  534. </ul>
  535. <a name="GOOGLECASTattr" id="GOOGLECASTattr"></a>
  536. <b>Attributes</b>
  537. <ul>
  538. <li><code><b>favoriteURL_[1-5]</b></code> &nbsp;&nbsp;-&nbsp;&nbsp; save URL to play afterwards with playFavorite [1-5]</li>
  539. </ul>
  540. <br>
  541. <a name="GOOGLECASTget" id="GOOGLECASTget"></a>
  542. <b>Get</b>
  543. <ul>
  544. <code>n/a</code>
  545. </ul>
  546. <br>
  547. </ul>
  548. =end html
  549. =cut