1 # -*- coding: utf-8 -*-
2 from enigma import eTimer
4 from Screens.Screen import Screen
5 from Screens.MessageBox import MessageBox
7 from Components.ActionMap import ActionMap
8 from Components.Label import Label
9 from Components.config import config, ConfigSubsection, ConfigSelection, ConfigIP, ConfigEnableDisable, getConfigListEntry, ConfigText, ConfigInteger
10 from Components.ConfigList import ConfigList, ConfigListScreen
12 from Plugins.Plugin import PluginDescriptor
13 from Tools import Notifications
15 from twisted.internet import reactor
16 from twisted.internet.protocol import ReconnectingClientFactory
17 from twisted.protocols.basic import LineReceiver
18 from twisted.web.client import getPage
19 from twisted.web2.client.http import HTTPClientProtocol, ClientRequest
21 from os import path as os_path
22 from urllib import urlencode
26 my_global_session = None
28 config.plugins.FritzCall = ConfigSubsection()
29 config.plugins.FritzCall.enable = ConfigEnableDisable(default = False)
30 config.plugins.FritzCall.hostname = ConfigIP(default = [192, 168, 178, 1])
31 config.plugins.FritzCall.filter = ConfigEnableDisable(default = False)
32 config.plugins.FritzCall.filtermsn = ConfigText(default = "", fixed_size = False)
33 config.plugins.FritzCall.showOutgoing = ConfigEnableDisable(default = False)
34 config.plugins.FritzCall.timeout = ConfigInteger(default = 15, limits = (0,60))
35 config.plugins.FritzCall.lookup = ConfigEnableDisable(default = False)
36 config.plugins.FritzCall.internal = ConfigEnableDisable(default = False)
37 config.plugins.FritzCall.fritzphonebook = ConfigEnableDisable(default = False)
38 config.plugins.FritzCall.phonebook = ConfigEnableDisable(default = False)
39 config.plugins.FritzCall.addcallers = ConfigEnableDisable(default = False)
40 config.plugins.FritzCall.phonebookLocation = ConfigSelection(choices = [("/media/usb/PhoneBook.txt", _("USB Stick")), ("/media/cf/PhoneBook.txt", _("CF Drive")), ("/media/hdd/PhoneBook.txt", _("Harddisk"))])
41 config.plugins.FritzCall.password = ConfigText(default = "", fixed_size = False)
42 config.plugins.FritzCall.prefix = ConfigText(default = "", fixed_size = False)
44 class FritzCallPhonebook:
49 def notify(self, text):
50 Notifications.AddNotification(MessageBox, text, type=MessageBox.TYPE_ERROR, timeout=config.plugins.FritzCall.timeout.value)
54 f = open(config.plugins.FritzCall.phonebookLocation.value, 'w')
55 f.write("01234567890#Name, Street, Location (Keep the Spaces!!!)\n");
61 def error(self, error):
62 if self.event == "LOGIN":
63 text = _("Fritz!Box Login failed! - Error: %s") %error
65 elif self.event == "LOAD":
66 text = _("Could not load phonebook from Fritz!Box - Error: %s") %error
69 def loadFritzBoxPhonebook(self):
70 print "[FritzCallPhonebook] loadFritzBoxPhonebook"
73 host = "%d.%d.%d.%d" %tuple(config.plugins.FritzCall.hostname.value)
74 uri = "/cgi-bin/webcm"# % tuple(config.plugins.FritzCall.hostname.value)
75 parms = urlencode({'getpage':'../html/de/menus/menu2.html', 'var:lang':'de','var:pagename':'fonbuch','var:menu':'fon'})
77 url = "http://%s%s?%s" %(host, uri, parms)
79 getPage(url).addCallback(self._gotPage).addErrback(self.error)
81 def parseFritzBoxPhonebook(self, html):
82 found = re.match('.*<table id="tList".*?</tr>\n(.*?)</table>', html, re.S)
85 table = found.group(1)
86 text = re.sub("<.*?>", "", table)
87 text = text.split('\n')
90 if line.strip() != "":
92 line = line.replace("\"", "")
93 line = line.split(", ")
96 name = name.replace("ß", "?").replace("ä", "?").replace("ö", "?").replace("ü", "?").replace("Ä", "?").replace("Ö", "?").replace("Ü", "?")
97 print "[FritzCallPhonebook] Adding '''%s''' with '''%s''' from Fritz!Box Phonebook!" %(name, number)
98 self.phonebook[number.strip()] = name.strip()
101 print "[FritzCallPhonebook] Could not parse Fritz!Box Phonebook entry"
103 def _gotPage(self, html):
104 # print "[FritzCallPhonebook] _gotPage"
105 # workaround: exceptions in gotPage-callback were ignored
107 if self.event == "LOGIN":
108 self.verifyLogin(html)
109 if self.event == "LOAD":
110 self.parseFritzBoxPhonebook(html)
112 import traceback, sys
113 traceback.print_exc(file=sys.stdout)
117 print "[FritzCallPhonebook] Login"
120 host = "%d.%d.%d.%d" %tuple(config.plugins.FritzCall.hostname.value)
121 uri = "/cgi-bin/webcm"
122 parms = "login:command/password=%s" %(config.plugins.FritzCall.password.value)
123 url = "http://%s%s" %(host, uri)
125 getPage(url, method="POST", headers = {'Content-Type': "application/x-www-form-urlencoded",'Content-Length': str(len(parms))}, postdata=parms).addCallback(self._gotPage).addErrback(self.error)
127 def verifyLogin(self, html):
128 # print "[FritzCallPhonebook] verifyLogin - html: %s" %html
130 found = re.match('.*<p class="errorMessage">FEHLER: Das angegebene Kennwort', html, re.S)
132 self.loadFritzBoxPhonebook()
134 text = _("Fritz!Box Login failed! - Wrong Password!")
138 # print "[FritzCallPhonebook] reload"
139 self.phonebook.clear()
141 if not os_path.exists(config.plugins.FritzCall.phonebookLocation.value):
148 for line in open(config.plugins.FritzCall.phonebookLocation.value):
150 number, name = line.split("#")
151 if not self.phonebook.has_key(number):
152 self.phonebook[number] = name
154 print "[FritzCallPhonebook] Could not parse internal Phonebook Entry %s" %line
156 if config.plugins.FritzCall.fritzphonebook.value:
157 if config.plugins.FritzCall.password.value != "":
160 self.loadFritzBoxPhonebook()
162 def search(self, number):
163 # print "[FritzCallPhonebook] Searching for %s" %number
165 if config.plugins.FritzCall.phonebook.value:
166 if self.phonebook.has_key(number):
167 name = self.phonebook[number].replace(", ", "\n")
170 def add(self, number, name):
171 # print "[FritzCallPhonebook] add"
172 if config.plugins.FritzCall.phonebook.value and config.plugins.FritzCall.addcallers.value:
174 f = open(config.plugins.FritzCall.phonebookLocation.value, 'a')
175 name = name.strip() + "\n"
176 string = "%s#%s" %(number, name)
177 self.phonebook[number] = name;
185 phonebook = FritzCallPhonebook()
187 class FritzCallSetup(ConfigListScreen, Screen):
189 <screen position="100,90" size="550,420" title="FritzCall Setup" >
190 <widget name="config" position="20,10" size="510,300" scrollbarMode="showOnDemand" />
191 <widget name="consideration" position="20,320" font="Regular;20" halign="center" size="510,50" />
194 def __init__(self, session, args = None):
196 Screen.__init__(self, session)
198 self["consideration"] = Label(_("You need to enable the monitoring on your Fritz!Box by dialing #96*5*!"))
201 self["setupActions"] = ActionMap(["SetupActions"],
204 "cancel": self.cancel,
208 ConfigListScreen.__init__(self, self.list)
213 ConfigListScreen.keyLeft(self)
217 ConfigListScreen.keyRight(self)
220 def createSetup(self):
222 self.list.append(getConfigListEntry(_("Call monitoring"), config.plugins.FritzCall.enable))
223 if config.plugins.FritzCall.enable.value:
224 self.list.append(getConfigListEntry(_("Fritz!Box FON IP address"), config.plugins.FritzCall.hostname))
226 self.list.append(getConfigListEntry(_("Show Calls for specific MSN"), config.plugins.FritzCall.filter))
227 if config.plugins.FritzCall.filter.value:
228 self.list.append(getConfigListEntry(_("MSN to show"), config.plugins.FritzCall.filtermsn))
230 self.list.append(getConfigListEntry(_("Show Outgoing Calls"), config.plugins.FritzCall.showOutgoing))
231 self.list.append(getConfigListEntry(_("Timeout for Call Notifications (seconds)"), config.plugins.FritzCall.timeout))
232 self.list.append(getConfigListEntry(_("Reverse Lookup Caller ID (DE only)"), config.plugins.FritzCall.lookup))
234 self.list.append(getConfigListEntry(_("Read PhoneBook from Fritz!Box"), config.plugins.FritzCall.fritzphonebook))
235 if config.plugins.FritzCall.fritzphonebook.value:
236 self.list.append(getConfigListEntry(_("Password Accessing Fritz!Box"), config.plugins.FritzCall.password))
238 self.list.append(getConfigListEntry(_("Use internal PhoneBook"), config.plugins.FritzCall.phonebook))
239 if config.plugins.FritzCall.phonebook.value:
240 self.list.append(getConfigListEntry(_("PhoneBook Location"), config.plugins.FritzCall.phonebookLocation))
241 self.list.append(getConfigListEntry(_("Automatically add new Caller to PhoneBook"), config.plugins.FritzCall.addcallers))
243 self.list.append(getConfigListEntry(_("Strip Leading 0"), config.plugins.FritzCall.internal))
244 self.list.append(getConfigListEntry(_("Prefix for Outgoing Calls"), config.plugins.FritzCall.prefix))
246 self["config"].list = self.list
247 self["config"].l.setList(self.list)
250 # print "[FritzCallSetup] save"
251 for x in self["config"].list:
253 if fritz_call is not None:
256 if config.plugins.FritzCall.phonebook.value:
257 if not os_path.exists(config.plugins.FritzCall.phonebookLocation.value):
258 if not phonebook.create():
259 Notifications.AddNotification(MessageBox, _("Can't create PhoneBook.txt"), type=MessageBox.TYPE_INFO, timeout=config.plugins.FritzCall.timeout.value)
261 print "[FritzCallSetup] called phonebook.reload()"
267 # print "[FritzCallSetup] cancel"
268 for x in self["config"].list:
272 class FritzProtocol(LineReceiver):
274 # print "[FritzProtocol] __init__"
277 def resetValues(self):
278 # print "[FritzProtocol] resetValues"
284 def notify(self, text, timeout=config.plugins.FritzCall.timeout.value):
285 Notifications.AddNotification(MessageBox, text, type=MessageBox.TYPE_INFO, timeout=timeout)
287 def handleIncoming(self):
288 # print "[FritzProtocol] handle Incoming!"
290 text = _("Incoming Call ")
291 if self.caller is not None:
292 text += _("on %s from\n---------------------------------------------\n%s\n%s\n---------------------------------------------\nto: %s") % (self.date, self.number, self.caller, self.phone)
294 text += _("on %s from\n---------------------------------------------\n%s (UNKNOWN)\n---------------------------------------------\nto: %s") % (self.date, self.number, self.phone)
299 def handleOutgoing(self):
300 # print "[FritzProtocol] handle Outgoing!"
301 text = _("Outgoing Call ")
302 if(self.caller is not None):
303 text += _("on %s to\n---------------------------------------------\n%s\n%s\n---------------------------------------------\nfrom: %s") % (self.date, self.number, self.caller, self.phone)
305 text += _("on %s to\n---------------------------------------------\n%s (UNKNOWN)\n\n---------------------------------------------\nfrom: %s") % (self.date, self.number, self.phone)#
310 def handleEvent(self):
311 # print "[FritzProtocol] handleEvent!"
312 if self.event == "RING":
313 self.handleIncoming()
314 elif self.event == "CALL":
315 self.handleOutgoing()
317 def handleEventOnError(self, error):
318 # print "[FritzProtocol] handleEventOnError - Error :%s" %error
321 def _gotPage(self, data):
322 # print "[FritzProtocol] _gotPage"
326 import traceback, sys
327 traceback.print_exc(file=sys.stdout)
331 def gotPage(self, html):
332 # print "[FritzProtocol] gotPage"
333 f = open("/tmp/reverseLookup.html", "w")
336 found = re.match('.*<td.*?class="cel-data border.*?>(.*?)</td>', html, re.S)
338 td = found.group(1) # group(1) is the content of (.*?) in our pattern
339 text = re.sub("<.*?>", "", td) # remove tags and their content
340 text = text.split("\n")
342 #wee need to strip the values as there a lots of whitespaces
343 name = text[2].strip()
344 address = text[8].replace(" ", " ").replace(", ", "\n").strip();
345 # print "[FritzProtocol] Reverse lookup succeeded:\nName: %s\n\nAddress: %s" %(name, address)
347 self.caller = "%s\n%s" %(name, address)
349 #Autoadd to PhoneBook.txt if enabled
350 if config.plugins.FritzCall.addcallers.value and self.event == "RING":
351 phonebook.add(self.number, self.caller.replace("\n", ", "))
353 # print "[FritzProtocol] Reverse lookup without result!"
357 def reverseLookup(self):
358 # print "[FritzProtocol] reverse Lookup!"
359 url = "http://www.dasoertliche.de/?form_name=search_inv&ph=%s" %self.number
360 getPage(url,method="GET").addCallback(self._gotPage).addErrback(self.handleEventOnError)
362 def lineReceived(self, line):
363 # print "[FritzProtocol] lineReceived"
364 #15.07.06 00:38:54;CALL;1;4;<provider>;<callee>;
365 #15.07.06 00:38:58;DISCONNECT;1;0;
366 #15.07.06 00:39:22;RING;0;<caller>;<outgoing msn>;
367 #15.07.06 00:39:27;DISCONNECT;0;0;
370 (self.date, self.event) = a[0:2]
373 if self.event == "RING":
376 if not config.plugins.FritzCall.filter.value or config.plugins.FritzCall.filtermsn.value == phone:
377 phonename = phonebook.search(phone)
378 if phonename is not None:
379 self.phone = "%s (%s)" %(phone, phonename)
383 if config.plugins.FritzCall.internal.value and a[3][0]=="0" and len(a[3]) > 3:
384 self.number = a[3][1:]
388 self.caller = phonebook.search(self.number)
389 if self.caller is None:
390 if config.plugins.FritzCall.lookup.value:
398 elif config.plugins.FritzCall.showOutgoing.value and self.event == "CALL":
401 if not config.plugins.FritzCall.filter.value or config.plugins.FritzCall.filtermsn.value == self.phone:
403 if config.plugins.FritzCall.internal.value and a[5][0]=="0" and len(a[3]) > 3:
404 self.number = a[5][1:]
408 self.caller = phonebook.search(self.number)
410 if self.number[0] != '0':
411 self.number = config.plugins.FritzCall.prefix.value + self.number
413 if self.caller is None:
414 if config.plugins.FritzCall.lookup.value:
420 class FritzClientFactory(ReconnectingClientFactory):
425 self.hangup_ok = False
427 def startedConnecting(self, connector):
428 Notifications.AddNotification(MessageBox, _("Connecting to Fritz!Box..."), type=MessageBox.TYPE_INFO, timeout=2)
430 def buildProtocol(self, addr):
431 Notifications.AddNotification(MessageBox, _("Connected to Fritz!Box!"), type=MessageBox.TYPE_INFO, timeout=4)
433 return FritzProtocol()
435 def clientConnectionLost(self, connector, reason):
436 if not self.hangup_ok:
437 Notifications.AddNotification(MessageBox, _("Connection to Fritz!Box! lost\n (%s)\nretrying...") % reason.getErrorMessage(), type=MessageBox.TYPE_INFO, timeout=config.plugins.FritzCall.timeout.value)
438 ReconnectingClientFactory.clientConnectionLost(self, connector, reason)
440 def clientConnectionFailed(self, connector, reason):
441 Notifications.AddNotification(MessageBox, _("Connecting to Fritz!Box failed\n (%s)\nretrying...") % reason.getErrorMessage(), type=MessageBox.TYPE_INFO, timeout=config.plugins.FritzCall.timeout.value)
442 ReconnectingClientFactory.clientConnectionFailed(self, connector, reason)
452 if config.plugins.FritzCall.enable.value:
453 f = FritzClientFactory()
454 self.d = (f, reactor.connectTCP("%d.%d.%d.%d" % tuple(config.plugins.FritzCall.hostname.value), 1012, f))
460 if self.d is not None:
461 self.d[0].hangup_ok = True
462 self.d[0].stopTrying()
463 self.d[1].disconnect()
467 session.open(FritzCallSetup)
471 def autostart(reason, **kwargs):
474 # ouch, this is a hack
475 if kwargs.has_key("session"):
476 global my_global_session
477 my_global_session = kwargs["session"]
480 print "[Fritz!Call] - Autostart"
482 fritz_call = FritzCall()
484 fritz_call.shutdown()
487 def Plugins(**kwargs):
488 return [ PluginDescriptor(name="FritzCall", description="Display Fritzbox-Fon calls on screen", where = PluginDescriptor.WHERE_PLUGINMENU, fnc=main),
489 PluginDescriptor(where = [PluginDescriptor.WHERE_SESSIONSTART, PluginDescriptor.WHERE_AUTOSTART], fnc = autostart) ]