1 # -*- coding: utf-8 -*-
2 #===============================================================================
3 # Remote Timer Setup by Homey
5 # This is free software; you can redistribute it and/or modify it under
6 # the terms of the GNU General Public License as published by the Free
7 # Software Foundation; either version 2, or (at your option) any later
10 # Copyright (C) 2009 by nixkoenner@newnigma2.to
16 #===============================================================================
18 from Plugins.Plugin import PluginDescriptor
19 from Screens.Screen import Screen
21 from Components.ActionMap import ActionMap
22 from Components.Button import Button
23 from Components.Label import Label
24 from Components.TimerList import TimerList
26 from Components.ConfigList import ConfigList, ConfigListScreen
27 from Components.config import getConfigListEntry, config, \
28 ConfigSubsection, ConfigText, ConfigIP, ConfigYesNo, \
29 ConfigPassword, ConfigNumber, KEY_LEFT, KEY_RIGHT, KEY_0
31 from Screens.TimerEntry import TimerEntry
32 from Screens.MessageBox import MessageBox
33 from RecordTimer import AFTEREVENT
35 from enigma import eEPGCache
37 from Tools.BoundFunction import boundFunction
39 from twisted.web.client import getPage
40 from xml.etree.cElementTree import fromstring as cElementTree_fromstring
41 from base64 import encodestring
44 #------------------------------------------------------------------------------------------
46 config.plugins.remoteTimer = ConfigSubsection()
47 config.plugins.remoteTimer.httphost = ConfigText(default = "" , fixed_size = False)
48 config.plugins.remoteTimer.httpip = ConfigIP(default = [0, 0, 0, 0])
49 config.plugins.remoteTimer.httpport = ConfigNumber(default = 80)
50 config.plugins.remoteTimer.username = ConfigText(default = "root", fixed_size = False)
51 config.plugins.remoteTimer.password = ConfigPassword(default = "", fixed_size = False)
53 def localGetPage(url):
54 username = config.plugins.remoteTimer.username.value
55 password = config.plugins.remoteTimer.password.value
56 if username and password:
57 basicAuth = encodestring(username + ':' + password)
58 authHeader = "Basic " + basicAuth.strip()
59 headers = {"Authorization": authHeader}
63 return getPage(url, headers = headers)
66 def __init__(self, sref, sname):
70 getServiceName = lambda self: self.sname
72 class RemoteTimerScreen(Screen):
74 <screen position="center,center" size="585,410" title="Remote-Timer digest" >
75 <widget name="text" position="0,10" zPosition="1" size="585,20" font="Regular;20" halign="center" valign="center" />
76 <widget name="timerlist" position="5,40" size="560,275" scrollbarMode="showOnDemand" />
77 <ePixmap name="red" position="5,365" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
78 <widget name="key_red" position="5,365" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
79 <ePixmap name="green" position="150,365" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
80 <widget name="key_green" position="150,365" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
81 <ePixmap name="yellow" position="295,365" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
82 <widget name="key_yellow" position="295,365" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
83 <ePixmap name="blue" position="440,365" zPosition="4" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
84 <widget name="key_blue" position="440,365" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
87 def __init__(self, session):
88 Screen.__init__(self, session)
90 # XXX: any reason not to use the skin from the local screen?
91 # is the info line really that much of a gain to lose a skinned screen...
93 self["actions"] = ActionMap(["SetupActions", "ColorActions"],
95 "green": self.settings,
97 "yellow": self.delete,
101 self["timerlist"] = TimerList([])
102 self["key_green"] = Button(_("Settings"))
103 self["key_blue"] = Button(_("Cleanup"))
104 self["key_yellow"] = Button(_("Delete"))
105 self["key_red"] = Button(_("Cancel"))
106 self["text"] = Label("")
108 remoteip = "%d.%d.%d.%d" % tuple(config.plugins.remoteTimer.httpip.value)
109 self.remoteurl = "%s:%s" % ( remoteip, str(config.plugins.remoteTimer.httpport.value))
111 self.onLayoutFinish.append(self.getInfo)
113 def getInfo(self, *args):
115 info = _("fetching remote data...")
116 url = "http://%s/web/timerlist" % (self.remoteurl)
117 localGetPage(url).addCallback(self._gotPageLoad).addErrback(self.errorLoad)
119 info = _("not configured yet. please do so in the settings.")
120 self["text"].setText(info)
122 def _gotPageLoad(self, data):
123 # XXX: this call is not optimized away so it is easier to extend this functionality to support other kinds of receiver
124 self["timerlist"].l.setList(self.generateTimerE2(data))
125 info = _("finish fetching remote data...")
126 self["text"].setText(info)
128 def errorLoad(self, error):
129 print "[RemoteTimer] errorLoad ERROR:", error.getErrorMessage()
133 url = "http://%s/web/timercleanup?cleanup=true" % (self.remoteurl)
134 localGetPage(url).addCallback(self.getInfo).addErrback(self.errorLoad)
136 print "[RemoteTimer] ERROR Cleanup"
139 sel = self["timerlist"].getCurrent()
142 self.session.openWithCallback(
143 self.deleteTimerConfirmed,
145 _("Do you really want to delete the timer \n%s ?") % sel.name
148 def deleteTimerConfirmed(self, val):
150 sel = self["timerlist"].getCurrent()
153 url = "http://%s/web/timerdelete?sRef=%s&begin=%s&end=%s" % (self.remoteurl, sel.service_ref.sref, sel.begin, sel.end)
154 localGetPage(url).addCallback(self.getInfo).addErrback(self.errorLoad)
157 self.session.open(RemoteTimerSetup)
159 def generateTimerE2(self, data):
161 root = cElementTree_fromstring(data)
163 print "[RemoteTimer] error: %s", e
164 self["text"].setText(_("error parsing incoming data."))
169 sref = str(timer.findtext("e2servicereference", '').encode("utf-8", 'ignore')),
170 sname = str(timer.findtext("e2servicename", 'n/a').encode("utf-8", 'ignore')),
171 name = str(timer.findtext("e2name", '').encode("utf-8", 'ignore')),
172 disabled = int(timer.findtext("e2disabled", 0)),
173 timebegin = int(timer.findtext("e2timebegin", 0)),
174 timeend = int(timer.findtext("e2timeend", 0)),
175 duration = int(timer.findtext("e2duration", 0)),
176 startprepare = int(timer.findtext("e2startprepare", 0)),
177 state = int(timer.findtext("e2state", 0)),
178 repeated = int(timer.findtext("e2repeated", 0)),
179 justplay = int(timer.findtext("e2justplay", 0)),
180 eventId = int(timer.findtext("e2eit", -1)),
181 afterevent = int(timer.findtext("e2afterevent", 0)),
182 dirname = str(timer.findtext("e2dirname", '').encode("utf-8", 'ignore')),
183 description = str(timer.findtext("e2description", '').encode("utf-8", 'ignore'))
187 for timer in root.findall("e2timer")
191 def __init__(self, sref = "", sname = "", name = "", disabled = 0, \
192 timebegin = 0, timeend = 0, duration = 0, startprepare = 0, \
193 state = 0, repeated = 0, justplay = 0, eventId = 0, afterevent = 0, \
194 dirname = "", description = ""):
195 self.service_ref = RemoteService(sref, sname)
197 self.disabled = disabled
198 self.begin = timebegin
200 self.duration = duration
201 self.startprepare = startprepare
203 self.repeated = repeated
204 self.justplay = justplay
205 self.eventId = eventId
206 self.afterevent = afterevent
207 self.dirname = dirname
208 self.description = description
210 class RemoteTimerSetup(Screen, ConfigListScreen):
212 <screen position="center,center" size="560,410" title="Settings" >
213 <widget name="config" position="5,40" size="480,335" scrollbarMode="showOnDemand" />
214 <ePixmap name="red" position="120,280" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
215 <ePixmap name="green" position="320,280" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
216 <widget name="key_red" position="120,280" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
217 <widget name="key_green" position="320,280" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
220 def __init__(self, session):
221 Screen.__init__(self, session)
223 self["SetupActions"] = ActionMap(["SetupActions", "ColorActions"],
227 "green": self.keySave,
230 self["key_red"] = Button(_("Cancel"))
231 self["key_green"] = Button(_("OK"))
233 ConfigListScreen.__init__(self, [
234 getConfigListEntry(_("Remote Timer - Hostname"), config.plugins.remoteTimer.httphost),
235 getConfigListEntry(_("Remote Timer - Network IP"), config.plugins.remoteTimer.httpip),
236 getConfigListEntry(_("Remote Timer - WebIf Port"), config.plugins.remoteTimer.httpport),
237 getConfigListEntry(_("Remote Timer - Username"), config.plugins.remoteTimer.username),
238 getConfigListEntry(_("Remote Timer - Password"), config.plugins.remoteTimer.password),
242 config.plugins.remoteTimer.save()
249 baseTimerEntrySetup = None
250 baseTimerEntryGo = None
253 global baseTimerEntrySetup, baseTimerEntryGo
254 if baseTimerEntrySetup is None:
255 baseTimerEntrySetup = TimerEntry.createSetup
256 if baseTimerEntryGo is None:
257 baseTimerEntryGo = TimerEntry.keyGo
258 TimerEntry.createSetup = createNewnigma2Setup
259 TimerEntry.keyGo = newnigma2KeyGo
261 def createNewnigma2Setup(self, widget):
262 baseTimerEntrySetup(self, widget)
263 self.timerentry_remote = ConfigYesNo()
264 self.list.insert(0, getConfigListEntry(_("Remote Timer"), self.timerentry_remote))
266 # force re-reading the list
267 self[widget].list = self.list
269 def newnigma2SubserviceSelected(self, service):
270 if service is not None:
271 # ouch, this hurts a little
272 service_ref = timerentry_service_ref
273 self.timerentry_service_ref = ServiceReference(service[1])
275 self.timer.eit = None
279 self.timerentry_service_ref = service_ref
282 def newnigma2KeyGo(self):
283 if not self.timerentry_remote.value:
284 baseTimerEntryGo(self)
286 service_ref = self.timerentry_service_ref
287 if self.timer.eit is not None:
288 event = eEPGCache.getInstance().lookupEventId(service_ref.ref, self.timer.eit)
290 n = event.getNumOfLinkageServices()
293 ref = self.session.nav.getCurrentlyPlayingServiceReference()
294 parent = service_ref.ref
297 i = event.getLinkageService(parent, x)
298 if i.toString() == ref.toString():
300 tlist.append((i.getName(), i))
301 self.session.openWithCallback(boundFunction(newnigma2SubserviceSelected, self), ChoiceBox, title=_("Please select a subservice to record..."), list = tlist, selection = selection)
304 parent = service_ref.ref
305 service_ref = ServiceReference(event.getLinkageService(parent, 0))
307 # XXX: this will - without any hassle - ignore the value of repeated
308 begin, end = self.getBeginEnd()
310 # when a timer end is set before the start, add 1 day
314 rt_name = urllib.quote(self.timerentry_name.value.decode('utf8').encode('utf8','ignore'))
315 rt_description = urllib.quote(self.timerentry_description.value.decode('utf8').encode('utf8','ignore'))
316 rt_disabled = 0 # XXX: do we really want to hardcode this? why do we offer this option then?
317 rt_repeated = 0 # XXX: same here
319 if self.timerentry_justplay.value == "zap":
324 # XXX: this one is tricky since we do not know if the remote box offers afterEventAuto so lets just keep it simple for now
326 "deepstandby": AFTEREVENT.DEEPSTANDBY,
327 "standby": AFTEREVENT.STANDBY,
328 }.get(self.timerentry_afterevent.value, AFTEREVENT.NONE)
330 # Add Timer on RemoteBox via WebIf Command
331 # http://192.168.178.20/web/timeradd?sRef=&begin=&end=&name=&description=&disabled=&justplay=&afterevent=&repeated=
332 remoteip = "%d.%d.%d.%d" % tuple(config.plugins.remoteTimer.httpip.value)
333 remoteurl = "http://%s:%s/web/timeradd?sRef=%s&begin=%s&end=%s&name=%s&description=%s&disabled=%s&justplay=%s&afterevent=%s&repeated=%s" % (
335 config.plugins.remoteTimer.httpport.value,
346 print "[RemoteTimer] debug remote", remoteurl
348 defer = localGetPage(remoteurl)
349 defer.addCallback(boundFunction(_gotPageLoad, self.session, self))
350 defer.addErrback(boundFunction(errorLoad, self.session))
352 def _gotPageLoadCb(timerEntry, doClose, *args):
354 timerEntry.keyCancel()
356 def _gotPageLoad(session, timerEntry, html):
357 remoteresponse = parseXml( html)
358 #print "print _gotPageLoad remoteresponse:", remoteresponse
359 # XXX: should be improved...
360 doClose = remoteresponse == "Timer added successfully!"
361 session.openWithCallback(
362 boundFunction(_gotPageLoadCb, timerEntry, doClose),
364 _("Set Timer on Remote DreamBox via WebIf:\n%s") % (remoteresponse),
368 def errorLoad(session, error):
369 #print "[RemoteTimer] errorLoad ERROR:", error
372 _("ERROR - Set Timer on Remote DreamBox via WebIf:\n%s") % (error),
376 def parseXml(string):
378 dom = cElementTree_fromstring(string)
379 entry = dom.findtext('e2statetext')
381 return entry.encode("utf-8", 'ignore')
382 return "No entry in XML from the webserver"
384 return "ERROR XML PARSE"
386 #------------------------------------------------------------------------------------------
388 def autostart(reason, **kwargs):
389 if "session" in kwargs:
390 session = kwargs["session"]
392 if config.plugins.remoteTimer.httpip.value:
395 print "[RemoteTimer] NO remoteTimer.httpip.value"
397 def main(session, **kwargs):
398 session.open(RemoteTimerScreen)
400 def Plugins(**kwargs):
402 PluginDescriptor(name="Remote Timer",description="Remote Timer Setup", where = [ PluginDescriptor.WHERE_PLUGINMENU ], icon="remotetimer.png", fnc = main),
403 PluginDescriptor(name="Remote Timer", where = PluginDescriptor.WHERE_EXTENSIONSMENU, fnc=main),
404 PluginDescriptor(where = PluginDescriptor.WHERE_SESSIONSTART, fnc = autostart)