{"id":1238,"date":"2010-02-22T00:07:35","date_gmt":"2010-02-22T00:07:35","guid":{"rendered":"http:\/\/dalelane.co.uk\/blog\/?p=1238"},"modified":"2010-02-25T21:27:45","modified_gmt":"2010-02-25T21:27:45","slug":"using-my-mobile-as-a-tv-remote-control-for-vdr","status":"publish","type":"post","link":"https:\/\/dalelane.co.uk\/blog\/?p=1238","title":{"rendered":"Using my mobile as a TV remote control for vdr"},"content":{"rendered":"<p><a href=\"http:\/\/www.flickr.com\/photos\/dalelane\/4371807302\/\" title=\"remote controls by dalelane, on Flickr\"><img loading=\"lazy\" decoding=\"async\" align=\"left\" style=\"border: thin black solid\" hspace=\"10\" vspace=\"10\" src=\"http:\/\/farm5.static.flickr.com\/4045\/4371807302_0cff961b53_m.jpg\" width=\"240\" height=\"201\" alt=\"remote controls\" \/><\/a>I can never find the TV remote. <\/p>\n<p>Whether it&#8217;s slipped somewhere in or under the sofa, or been hidden by the kids, I&#8217;m often at a loss to find it.  <\/p>\n<p>A mobile phone, on the other hand, is rarely too far from my hand \ud83d\ude42 <\/p>\n<p>So I&#8217;ve hacked together a quick app to put a TV remote control interface on my phone. It&#8217;s definitely a quick-and-dirty chunk of code, but it does now mean that I can work <a class=\"external\" href=\"http:\/\/dalelane.co.uk\/blog\/?p=1228\" target=\"_blank\">my TV<\/a> from my mobile if I can&#8217;t find the real remote. <\/p>\n<p>On the off-chance that this is useful to other vdr users, I wanted to share how I did it.  <\/p>\n<p><!--more--><a href=\"http:\/\/www.flickr.com\/photos\/dalelane\/4371823708\/\" title=\"a mobile remote control - 1 by dalelane, on Flickr\"><img loading=\"lazy\" decoding=\"async\" align=\"right\" border=\"0\" hspace=\"10\" vspace=\"10\" src=\"http:\/\/farm5.static.flickr.com\/4015\/4371823708_ac4316dc12.jpg\" width=\"224\" height=\"336\" alt=\"a mobile remote control - 1\" \/><\/a>I went for a web app rather than native code.  <\/p>\n<p>This was mainly because I&#8217;m switching a lot between <a class=\"external\" href=\"http:\/\/dalelane.co.uk\/blog\/?p=310\" target=\"_blank\">Android<\/a>, <a class=\"external\" href=\"http:\/\/dalelane.co.uk\/blog\/?p=1062\" target=\"_blank\">Palm&#8217;s webOS<\/a> and Windows Mobile at the moment, and didn&#8217;t really want to have to write three separate applications! A web app can be hosted on the same home server that runs vdr for <a class=\"external\" href=\"http:\/\/dalelane.co.uk\/blog\/?p=1228\" target=\"_blank\">my home TV<\/a>, and accessed from any platform. It&#8217;s only hosting internally &#8211; so you have to be connected to my home wifi to access it. But as all my phones have wifi, and the app isn&#8217;t really that useful when I&#8217;m not at home, this isn&#8217;t a problem.  <\/p>\n<p>I&#8217;ve <a class=\"external\" href=\"http:\/\/dalelane.co.uk\/blog\/?p=991\" target=\"_blank\">mentioned before<\/a> that vdr listens on a TCP port for commands, and that the <a class=\"external\" href=\"http:\/\/manpages.ubuntu.com\/manpages\/karmic\/man1\/svdrpsend.1.html\" target=\"_blank\">svdrpsend utility<\/a> provides an easy way to send commands in the correct format.  <\/p>\n<p>To send a remote control command, you run: <\/p>\n<p> <code>svdrpsend HITK &#60;NAME&#62;<\/code> <\/p>\n<p>where <code>&#60;NAME&#62;<\/code> is the name of a remote control key.  <\/p>\n<p><a href=\"http:\/\/www.flickr.com\/photos\/dalelane\/4371823748\/\" title=\"a mobile remote control - 2 by dalelane, on Flickr\"><img loading=\"lazy\" decoding=\"async\" align=\"left\" border=\"0\" hspace=\"10\" vspace=\"10\" src=\"http:\/\/farm5.static.flickr.com\/4066\/4371823748_19fc491fae.jpg\" width=\"224\" height=\"336\" alt=\"a mobile remote control - 2\" \/><\/a>My web app just displays buttons on a web page. Pressing one of the buttons causes the relevant svdrpsend command to be run. <\/p>\n<p>I have used a JavaScript XMLHttpRequest to save reloading the page after each press (useful when repeatedly tapping &#8216;Up&#8217; or &#8216;Down&#8217; to navigate menus). But apart from that, it&#8217;s all pretty straightforward.  <\/p>\n<p>It&#8217;s not as nice an interface as my real remote control. But it makes for a useful backup when I can&#8217;t find the real thing \ud83d\ude42  <\/p>\n<p>The code is below. <\/p>\n<p>I&#8217;m using <a class=\"external\" href=\"http:\/\/www.cherrypy.org\/\" target=\"_blank\">cherrypy<\/a> to host it (for no particular reason than I already had it installed from a previous project).<br \/>\n<br clear=\"all\"\/> <\/p>\n<pre style=\"border: thin solid silver; background-color: #eeeeee; padding: 0.7em; font-size: 1.1em; overflow: auto;\">import cherrypy \r\nimport os.path \r\nfrom subprocess import call \r\n\r\nclass VDRRemote: \r\n    def runcmd(self, cmd=None): \r\n        if (cmd is not None): \r\n            if cmd == \"ONOFF\": \r\n                call(['\/home\/dale\/scripts\/toggle-tv.sh']) \r\n            elif cmd == \"VOLDOWN\": \r\n                call(['svdrpsend', 'HITK', 'Volume-']) \r\n            elif cmd == \"VOLUP\": \r\n                call(['svdrpsend', 'HITK', 'Volume+']) \r\n            elif cmd == \"CHANDOWN\": \r\n                call(['svdrpsend', 'HITK', 'Channel-']) \r\n            elif cmd == \"CHANUP\": \r\n                call(['svdrpsend', 'HITK', 'Channel+']) \r\n            else: \r\n                call(['svdrpsend', 'HITK', cmd]) \r\n\r\n    def index(self): \r\n        return ''' \r\n                &#60;!DOCTYPE html PUBLIC \"-\/\/W3C\/\/DTD XHTML 1.0 Strict\/\/EN\"  \r\n                    \"http:\/\/www.w3.org\/TR\/xhtml1\/DTD\/xhtml1-strict.dtd\"&#62;  \r\n                &#60;html xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\"&#62;  \r\n                &#60;head&#62;  \r\n                    &#60;title&#62;vdr&#60;\/title&#62;  \r\n                    &#60;style type=\"text\/css\" media=\"screen\"&#62;@import \"images\/remote.css\";&#60;\/style&#62; \r\n                    &#60;script&#62; \r\n                        function invokeCmd(param) { \r\n                          var http = new XMLHttpRequest(); \r\n                          http.open(\"GET\", \"\/runcmd?cmd=\" + param, true); \r\n                          http.send(null); \r\n                        } \r\n                    &#60;\/script&#62; \r\n                &#60;\/head&#62;   \r\n                &#60;body&#62;  \r\n                    &#60;div&#62;  \r\n                        &#60;table class=\"btnTable\" selected=\"true\"&#62;         \r\n                            &#60;tr&#62; \r\n                                &#60;td&#62;&#60;a class=\"borderImageBtn blackButton\" href=\"javascript:invokeCmd('ONOFF')\"&#62;On\/Off&#60;\/a&#62;&#60;\/td&#62; \r\n                                &#60;td&#62; &#60;\/td&#62; \r\n                                &#60;td&#62;&#60;a class=\"borderImageBtn blackButton\" href=\"javascript:invokeCmd('Mute')\"&#62;Mute&#60;\/a&#62;&#60;\/td&#62; \r\n                            &#60;\/tr&#62; \r\n                            &#60;tr&#62; \r\n                                &#60;td&#62;&#60;a class=\"borderImageBtn blueButton\" href=\"javascript:invokeCmd('1')\"&#62;1&#60;\/a&#62;&#60;\/td&#62; \r\n                                &#60;td&#62;&#60;a class=\"borderImageBtn blueButton\" href=\"javascript:invokeCmd('2')\"&#62;2&#60;\/a&#62;&#60;\/td&#62; \r\n                                &#60;td&#62;&#60;a class=\"borderImageBtn blueButton\" href=\"javascript:invokeCmd('3')\"&#62;3&#60;\/a&#62;&#60;\/td&#62; \r\n                            &#60;\/tr&#62;  \r\n                            &#60;tr&#62; \r\n                                &#60;td&#62;&#60;a class=\"borderImageBtn blueButton\" href=\"javascript:invokeCmd('4')\"&#62;4&#60;\/a&#62;&#60;\/td&#62; \r\n                                &#60;td&#62;&#60;a class=\"borderImageBtn blueButton\" href=\"javascript:invokeCmd('5')\"&#62;5&#60;\/a&#62;&#60;\/td&#62; \r\n                                &#60;td&#62;&#60;a class=\"borderImageBtn blueButton\" href=\"javascript:invokeCmd('6')\"&#62;6&#60;\/a&#62;&#60;\/td&#62; \r\n                            &#60;\/tr&#62;  \r\n                            &#60;tr&#62; \r\n                                &#60;td&#62;&#60;a class=\"borderImageBtn blueButton\" href=\"javascript:invokeCmd('7')\"&#62;7&#60;\/a&#62;&#60;\/td&#62; \r\n                                &#60;td&#62;&#60;a class=\"borderImageBtn blueButton\" href=\"javascript:invokeCmd('8')\"&#62;8&#60;\/a&#62;&#60;\/td&#62; \r\n                                &#60;td&#62;&#60;a class=\"borderImageBtn blueButton\" href=\"javascript:invokeCmd('9')\"&#62;9&#60;\/a&#62;&#60;\/td&#62; \r\n                            &#60;\/tr&#62;  \r\n                            &#60;tr&#62; \r\n                                &#60;td&#62; &#60;\/td&#62; \r\n                                &#60;td&#62;&#60;a class=\"borderImageBtn blueButton\" href=\"javascript:invokeCmd('0')\"&#62;0&#60;\/a&#62;&#60;\/td&#62; \r\n                                &#60;td&#62; &#60;\/td&#62; \r\n                            &#60;\/tr&#62;              \r\n                            &#60;tr&#62; \r\n                                &#60;td colspan=\"3\"&#62; &#60;\/td&#62; \r\n                            &#60;\/tr&#62; \r\n                            &#60;tr&#62; \r\n                                &#60;td&#62; &#60;\/td&#62; \r\n                                &#60;td&#62;&#60;a class=\"borderImageBtn whiteButton\" href=\"javascript:invokeCmd('Up')\"&#62;&#38;#8593;&#60;\/a&#62;&#60;\/td&#62; \r\n                                &#60;td&#62; &#60;\/td&#62; \r\n                            &#60;\/tr&#62; \r\n                            &#60;tr&#62; \r\n                                &#60;td&#62;&#60;a class=\"borderImageBtn whiteButton\" href=\"javascript:invokeCmd('Left')\"&#62;&#38;#8592;&#60;\/a&#62;&#60;\/td&#62; \r\n                                &#60;td&#62;&#60;a class=\"borderImageBtn whiteButton\" href=\"javascript:invokeCmd('Ok')\"&#62;OK&#60;\/a&#62;&#60;\/td&#62; \r\n                                &#60;td&#62;&#60;a class=\"borderImageBtn whiteButton\" href=\"javascript:invokeCmd('Right')\"&#62;&#38;#8594;&#60;\/a&#62;&#60;\/td&#62; \r\n                            &#60;\/tr&#62;             \r\n                            &#60;tr&#62; \r\n                                &#60;td&#62; &#60;\/td&#62; \r\n                                &#60;td&#62;&#60;a class=\"borderImageBtn whiteButton\" href=\"javascript:invokeCmd('Down');\"&#62;&#38;#8595;&#60;\/a&#62;&#60;\/td&#62; \r\n                                &#60;td&#62; &#60;\/td&#62; \r\n                            &#60;\/tr&#62; \r\n                        &#60;\/table&#62; \r\n                        &#60;table class=\"btnTable\" selected=\"true\"&#62;         \r\n                            &#60;tr&#62; \r\n                                &#60;td&#62;&#60;a class=\"borderImageNarrowBtn blackButton\" href=\"javascript:invokeCmd('MENU')\"&#62;Menu&#60;\/a&#62;&#60;\/td&#62;                 \r\n                                &#60;td&#62;&#60;a class=\"borderImageNarrowBtn blackButton\" href=\"javascript:invokeCmd('Recordings')\"&#62;Rec TV&#60;\/a&#62;&#60;\/td&#62; \r\n                                &#60;td&#62;&#60;a class=\"borderImageNarrowBtn blackButton\" href=\"javascript:invokeCmd('Info')\"&#62;Info&#60;\/a&#62;&#60;\/td&#62; \r\n                                &#60;td&#62;&#60;a class=\"borderImageNarrowBtn blackButton\" href=\"javascript:invokeCmd('Back')\"&#62;Back&#60;\/a&#62;&#60;\/td&#62; \r\n                            &#60;\/tr&#62; \r\n                            &#60;tr&#62; \r\n                                &#60;td&#62;&#60;a class=\"borderImageNarrowBtn blueButton\" href=\"javascript:invokeCmd('Record')\"&#62;Record&#60;\/a&#62;&#60;\/td&#62; \r\n                                &#60;td&#62;&#60;a class=\"borderImageNarrowBtn blueButton\" href=\"javascript:invokeCmd('Play')\"&#62;Play&#60;\/a&#62;&#60;\/td&#62; \r\n                                &#60;td&#62;&#60;a class=\"borderImageNarrowBtn blueButton\" href=\"javascript:invokeCmd('Pause')\"&#62;Pause&#60;\/a&#62;&#60;\/td&#62; \r\n                                &#60;td&#62;&#60;a class=\"borderImageNarrowBtn blueButton\" href=\"javascript:invokeCmd('Stop')\"&#62;Stop&#60;\/a&#62;&#60;\/td&#62; \r\n                            &#60;\/tr&#62; \r\n                            &#60;tr&#62; \r\n                                &#60;td&#62;&#60;a class=\"borderImageNarrowBtn blackLeftButton\" href=\"javascript:invokeCmd('VOLDOWN')\"&#62;Vol &#38;minus;&#60;\/a&#62;&#60;\/td&#62; \r\n                                &#60;td&#62;&#60;a class=\"borderImageNarrowBtn blackRightButton\" href=\"javascript:invokeCmd('VOLUP')\"&#62;Vol +&#60;\/a&#62;&#60;\/td&#62; \r\n                                &#60;td&#62;&#60;a class=\"borderImageNarrowBtn blackLeftButton\" href=\"javascript:invokeCmd('CHANDOWN')\"&#62;Chan &#38;minus;&#60;\/a&#62;&#60;\/td&#62; \r\n                                &#60;td&#62;&#60;a class=\"borderImageNarrowBtn blackRightButton\" href=\"javascript:invokeCmd('CHANUP')\"&#62;Chan +&#60;\/a&#62;&#60;\/td&#62; \r\n                            &#60;\/tr&#62; \r\n                            &#60;tr&#62; \r\n                                &#60;td&#62;&#60;a class=\"borderImageNarrowBtn blackLeftButton\" href=\"javascript:invokeCmd('Green')\"&#62;60 sec&#60;\/a&#62;&#60;\/td&#62; \r\n                                &#60;td&#62;&#60;a class=\"borderImageNarrowBtn blackLeftButton\" href=\"javascript:invokeCmd('FastRew')\"&#62;Rewind&#60;\/a&#62;&#60;\/td&#62; \r\n                                &#60;td&#62;&#60;a class=\"borderImageNarrowBtn blackRightButton\" href=\"javascript:invokeCmd('FastFwd')\"&#62;FFwd&#60;\/a&#62;&#60;\/td&#62; \r\n                                &#60;td&#62;&#60;a class=\"borderImageNarrowBtn blackRightButton\" href=\"javascript:invokeCmd('Yellow')\"&#62;60 sec&#60;\/a&#62;&#60;\/td&#62; \r\n                            &#60;\/tr&#62; \r\n                            &#60;tr&#62; \r\n                                &#60;td&#62;&#60;a class=\"borderImageNarrowBtn redButton\" href=\"javascript:invokeCmd('Red')\"&#62; &#60;\/a&#62;&#60;\/td&#62; \r\n                                &#60;td&#62;&#60;a class=\"borderImageNarrowBtn greenButton\" href=\"javascript:invokeCmd('Green')\"&#62; &#60;\/a&#62;&#60;\/td&#62; \r\n                                &#60;td&#62;&#60;a class=\"borderImageNarrowBtn yellowButton\" href=\"javascript:invokeCmd('Yellow')\"&#62; &#60;\/a&#62;&#60;\/td&#62; \r\n                                &#60;td&#62;&#60;a class=\"borderImageNarrowBtn blueButton\" href=\"javascript:invokeCmd('Blue')\"&#62; &#60;\/a&#62;&#60;\/td&#62; \r\n                            &#60;\/tr&#62;             \r\n                        &#60;\/table&#62; \r\n                    &#60;\/div&#62;  \r\n                  &#60;\/body&#62;  \r\n                &#60;\/html&#62;''' \r\n                 \r\n    index.exposed = True \r\n    runcmd.exposed = True \r\n\r\ncherrypy.server.socket_host=\"0.0.0.0\" \r\n\r\ncurrent_dir = os.path.dirname(os.path.abspath(__file__)) \r\nconf = {'\/images': {'tools.staticdir.on'  : True, \r\n                    'tools.staticdir.dir' : os.path.join(current_dir, 'images')}} \r\n\r\ncherrypy.quickstart(VDRRemote(), config=conf)<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>I can never find the TV remote. Whether it&#8217;s slipped somewhere in or under the sofa, or been hidden by the kids, I&#8217;m often at a loss to find it. A mobile phone, on the other hand, is rarely too far from my hand \ud83d\ude42 So I&#8217;ve hacked together a quick app to put a [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[291,447,46,212,427],"class_list":["post-1238","post","type-post","status-publish","format-standard","hentry","category-code","tag-android","tag-cherrypy","tag-mobile","tag-python","tag-vdr"],"_links":{"self":[{"href":"https:\/\/dalelane.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1238","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dalelane.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dalelane.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dalelane.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/dalelane.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1238"}],"version-history":[{"count":0,"href":"https:\/\/dalelane.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1238\/revisions"}],"wp:attachment":[{"href":"https:\/\/dalelane.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1238"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dalelane.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1238"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dalelane.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1238"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}