1 # -*- coding: utf-8 -*-
2 #===============================================================================
7 #==============================
8 from Screens.Screen import Screen
9 from Screens.MessageBox import MessageBox
10 from Screens.NumericalTextInputHelpDialog import NumericalTextInputHelpDialog
11 from Screens.InputBox import InputBox
12 from Screens import Standby
13 from Screens.HelpMenu import HelpableScreen
15 from enigma import eTimer, eSize, ePoint#@UnresolvedImport
16 from enigma import eListboxPythonMultiContent, gFont, RT_HALIGN_LEFT, RT_HALIGN_RIGHT
18 from Components.MenuList import MenuList
19 from Components.ActionMap import ActionMap
20 from Components.Label import Label
21 from Components.Button import Button
22 from Components.Pixmap import Pixmap
23 from Components.config import config, ConfigSubsection, ConfigSelection, ConfigEnableDisable, getConfigListEntry, ConfigText, ConfigInteger
24 from Components.ConfigList import ConfigListScreen
25 from Components.Harddisk import harddiskmanager
27 from Components.config import ConfigPassword
29 ConfigPassword = ConfigText
31 from Plugins.Plugin import PluginDescriptor
32 from Tools import Notifications
33 from Tools.NumericalTextInput import NumericalTextInput
34 from Tools.Directories import resolveFilename, SCOPE_PLUGINS, SCOPE_SKIN_IMAGE, SCOPE_CONFIG, SCOPE_MEDIA
35 from Tools.LoadPixmap import LoadPixmap
37 from twisted.internet import reactor #@UnresolvedImport
38 from twisted.internet.protocol import ReconnectingClientFactory #@UnresolvedImport
39 from twisted.protocols.basic import LineReceiver #@UnresolvedImport
40 from twisted.web.client import getPage #@UnresolvedImport
42 from urllib import urlencode
43 import re, time, os, hashlib, traceback
45 from nrzuname import ReverseLookupAndNotifier, html2unicode
46 import FritzOutlookCSV, FritzLDIF
47 from . import _, debug #@UnresolvedImport
49 from enigma import getDesktop
50 DESKTOP_WIDTH = getDesktop(0).size().width()
51 DESKTOP_HEIGHT = getDesktop(0).size().height()
52 DESKTOP_SKIN = config.skin.primary_skin.value.replace("/skin.xml", "")
53 XXX = 0 # TODO: Platzhalter für fullscreen SD skin
56 # It returns the first value, if HD (1280x720),
57 # the second if SD (720x576),
58 # else something scaled accordingly
59 # if one of the parameters is -1, scale proportionally
66 return scale(y2, y1, 1280, 720, DESKTOP_WIDTH)
72 return scale(y2, y1, 720, 576, DESKTOP_HEIGHT)
73 def scale(y2, y1, x2, x1, x):
74 return (y2 - y1) * (x - x1) / (x2 - x1) + y1
76 my_global_session = None
78 config.plugins.FritzCall = ConfigSubsection()
79 config.plugins.FritzCall.debug = ConfigEnableDisable(default=False)
80 config.plugins.FritzCall.muteOnCall = ConfigEnableDisable(default=False)
81 config.plugins.FritzCall.hostname = ConfigText(default="fritz.box", fixed_size=False)
82 config.plugins.FritzCall.afterStandby = ConfigSelection(choices=[("none", _("show nothing")), ("inList", _("show as list")), ("each", _("show each call"))])
83 config.plugins.FritzCall.filter = ConfigEnableDisable(default=False)
84 config.plugins.FritzCall.filtermsn = ConfigText(default="", fixed_size=False)
85 config.plugins.FritzCall.filtermsn.setUseableChars('0123456789,')
86 config.plugins.FritzCall.showOutgoing = ConfigEnableDisable(default=False)
87 config.plugins.FritzCall.timeout = ConfigInteger(default=15, limits=(0, 60))
88 config.plugins.FritzCall.lookup = ConfigEnableDisable(default=False)
89 config.plugins.FritzCall.internal = ConfigEnableDisable(default=False)
90 config.plugins.FritzCall.fritzphonebook = ConfigEnableDisable(default=False)
91 config.plugins.FritzCall.phonebook = ConfigEnableDisable(default=False)
92 config.plugins.FritzCall.addcallers = ConfigEnableDisable(default=False)
93 config.plugins.FritzCall.enable = ConfigEnableDisable(default=False)
94 config.plugins.FritzCall.password = ConfigPassword(default="", fixed_size=False)
95 config.plugins.FritzCall.extension = ConfigText(default='1', fixed_size=False)
96 config.plugins.FritzCall.extension.setUseableChars('0123456789')
97 config.plugins.FritzCall.showType = ConfigEnableDisable(default=True)
98 config.plugins.FritzCall.showShortcut = ConfigEnableDisable(default=False)
99 config.plugins.FritzCall.showVanity = ConfigEnableDisable(default=False)
100 config.plugins.FritzCall.prefix = ConfigText(default="", fixed_size=False)
101 config.plugins.FritzCall.prefix.setUseableChars('0123456789')
102 config.plugins.FritzCall.fullscreen = ConfigEnableDisable(default=False)
104 mountedDevs= [(resolveFilename(SCOPE_CONFIG), _("Flash") + " (" + resolveFilename(SCOPE_CONFIG) + ")")]
105 if os.path.isdir(resolveFilename(SCOPE_MEDIA, "cf")):
106 mountedDevs.append((resolveFilename(SCOPE_MEDIA, "cf"), _("Compact Flash") + " (" + resolveFilename(SCOPE_MEDIA, "cf") + ")"))
107 if os.path.isdir(resolveFilename(SCOPE_MEDIA, "usb")):
108 mountedDevs.append((resolveFilename(SCOPE_MEDIA, "usb"), _("USB Device") + " (" + resolveFilename(SCOPE_MEDIA, "usb") + ")"))
109 for p in harddiskmanager.getMountedPartitions(True):
110 mp = p.mountpoint[:-1]
112 mountedDevs.append((mp, _(p.description)+ ' (' + mp + ')'))
114 mountedDevs.append((mp, mp))
115 config.plugins.FritzCall.phonebookLocation = ConfigSelection(choices=mountedDevs)
118 ("0049", _("Germany")),
119 ("0031", _("The Netherlands")),
120 ("0033", _("France")),
121 ("0039", _("Italy")),
122 ("0041", _("Switzerland")),
123 ("0043", _("Austria"))
125 config.plugins.FritzCall.country = ConfigSelection(choices=countryCodes)
129 FBF_MISSED_CALLS = "2"
131 fbfCallsChoices = {FBF_ALL_CALLS: _("All calls"),
132 FBF_IN_CALLS: _("Incoming calls"),
133 FBF_MISSED_CALLS: _("Missed calls"),
134 FBF_OUT_CALLS: _("Outgoing calls")
136 config.plugins.FritzCall.fbfCalls = ConfigSelection(choices=fbfCallsChoices)
138 config.plugins.FritzCall.name = ConfigText(default="", fixed_size=False)
139 config.plugins.FritzCall.number = ConfigText(default="", fixed_size=False)
140 config.plugins.FritzCall.number.setUseableChars('0123456789')
147 os.remove("/tmp/FritzDebug.log")
154 avonFileName = resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/avon.dat")
155 if os.path.exists(avonFileName):
156 for line in open(avonFileName):
157 line = line.decode("iso-8859-1").encode('utf-8')
160 parts = line.split(':')
162 avon[parts[0].replace('-','').replace('*','').replace('/','')] = parts[1]
164 def resolveNumberWithAvon(number, countrycode):
165 if not number or number[0]!='0':
168 countrycode = countrycode.replace('00','+')
169 if number[:2] == '00':
170 normNumber = '+' + number[2:]
171 elif number[:1] == '0':
172 normNumber = countrycode + number[1:]
173 else: # this should can not happen, but safety first
176 # debug('normNumer: ' + normNumber)
177 for i in reversed(range(min(10, len(number)))):
178 if avon.has_key(normNumber[:i]):
179 return '[' + avon[normNumber[:i]].strip() + ']'
182 def handleReverseLookupResult(name):
183 found = re.match("NA: ([^;]*);VN: ([^;]*);STR: ([^;]*);HNR: ([^;]*);PLZ: ([^;]*);ORT: ([^;]*)", name)
185 ( name,firstname,street,streetno,zipcode,city ) = (found.group(1),
192 if firstname: name += ' ' + firstname
193 if street or streetno or zipcode or city: name += ', '
194 if street: name += street
195 if streetno: name += ' ' + streetno
196 if (street or streetno) and (zipcode or city): name += ', '
197 if zipcode and city: name += zipcode + ' ' + city
198 elif zipcode: name += zipcode
199 elif city: name += city
202 from xml.dom.minidom import parse
205 callbycallFileName = resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/callbycall_world.xml")
206 if os.path.exists(callbycallFileName):
207 dom = parse(callbycallFileName)
208 for top in dom.getElementsByTagName("callbycalls"):
209 for cbc in top.getElementsByTagName("country"):
210 code = cbc.getAttribute("code").replace("+","00")
211 cbcInfos[code] = cbc.getElementsByTagName("callbycall")
213 debug("[FritzCall] initCbC: callbycallFileName does not exist?!?!")
215 def stripCbCPrefix(number, countrycode):
216 if number and number[:2]!="00" and cbcInfos.has_key(countrycode):
217 for cbc in cbcInfos[countrycode]:
218 if len(cbc.getElementsByTagName("length"))<1 or len(cbc.getElementsByTagName("prefix"))<1:
219 debug("[FritzCall] stripCbCPrefix: entries for " + countrycode + " %s invalid")
221 length = int(cbc.getElementsByTagName("length")[0].childNodes[0].data)
222 prefix = cbc.getElementsByTagName("prefix")[0].childNodes[0].data
223 # if re.match('^'+prefix, number):
224 if number[:len(prefix)] == prefix:
225 return number[length:]
228 class FritzAbout(Screen):
230 def __init__(self, session):
231 textFieldWidth = scaleV(350,250)
232 width = 5 + 150 + 20 + textFieldWidth + 5 + 175 + 5
233 height = 5 + 175 + 5 + 25 + 5
234 # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
236 <screen name="FritzAbout" position="%d,%d" size="%d,%d" title="%s" >
237 <widget name="text" position="175,%d" size="%d,%d" font="Regular;%d" />
238 <ePixmap position="5,37" size="150,110" pixmap="%s" transparent="1" alphatest="blend" />
239 <ePixmap position="%d,5" size="175,175" pixmap="%s" transparent="1" alphatest="blend" />
240 <widget name="url" position="20,185" size="%d,25" font="Regular;%d" />
242 (DESKTOP_WIDTH - width) / 2, (DESKTOP_HEIGHT - height) / 2, # position
243 width, height, # size
244 _("About FritzCall"), # title
245 (height-scaleV(150,130)) / 2, # text vertical position
247 scaleV(150,130), # text height
248 scaleV(24,21), # text font size
249 resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/images/fritz.png"), # 150x110
250 5 + 150 + 5 + textFieldWidth + 5, # qr code horizontal offset
251 resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/images/website.png"), # 175x175
252 width-40, # url width
253 scaleV(24,21) # url font size
255 Screen.__init__(self, session)
256 self["aboutActions"] = ActionMap(["OkCancelActions"],
261 self["text"] = Label(
262 "FritzCall Plugin" + "\n\n" +
263 "$Author$"[1:-2] + "\n" +
264 "$Revision$"[1:-2] + "\n" +
265 "$Date$"[1:23] + "\n"
267 self["url"] = Label("http://wiki.blue-panel.com/index.php/FritzCall")
284 debug("[FritzCallFBF] __init__")
285 self._callScreen = None
286 self._md5LoginTimestamp = None
287 self._md5Sid = '0000000000000000'
288 self._callTimestamp = 0
290 self._callType = config.plugins.FritzCall.fbfCalls.value
291 self.info = None # (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dectActive)
294 def _notify(self, text):
295 debug("[FritzCallFBF] notify: " + text)
296 self._md5LoginTimestamp = None
298 debug("[FritzCallFBF] notify: try to close callScreen")
299 self._callScreen.close()
300 self._callScreen = None
301 Notifications.AddNotification(MessageBox, text, type=MessageBox.TYPE_ERROR, timeout=config.plugins.FritzCall.timeout.value)
303 def _login(self, callback=None):
304 debug("[FritzCallFBF] _login")
306 self._callScreen.updateStatus(_("login"))
307 if self._md5LoginTimestamp and ((time.time() - self._md5LoginTimestamp) < float(9.5*60)) and self._md5Sid != '0000000000000000': # new login after 9.5 minutes inactivity
308 debug("[FritzCallFBF] _login: renew timestamp: " + time.ctime(self._md5LoginTimestamp) + " time: " + time.ctime())
309 self._md5LoginTimestamp = time.time()
312 debug("[FritzCallFBF] _login: not logged in or outdated login")
313 # http://fritz.box/cgi-bin/webcm?getpage=../html/login_sid.xml
314 parms = urlencode({'getpage':'../html/login_sid.xml'})
315 url = "http://%s/cgi-bin/webcm" % (config.plugins.FritzCall.hostname.value)
316 debug("[FritzCallFBF] _login: '" + url + "' parms: '" + parms + "'")
319 headers={'Content-Type': "application/x-www-form-urlencoded", 'Content-Length': str(len(parms))
320 }, postdata=parms).addCallback(lambda x: self._md5Login(callback,x)).addErrback(lambda x:self._oldLogin(callback,x))
322 def _oldLogin(self, callback, error):
323 debug("[FritzCallFBF] _oldLogin: " + repr(error))
324 self._md5LoginTimestamp = None
325 if config.plugins.FritzCall.password.value != "":
326 parms = "login:command/password=%s" % (config.plugins.FritzCall.password.value)
327 url = "http://%s/cgi-bin/webcm" % (config.plugins.FritzCall.hostname.value)
328 debug("[FritzCallFBF] _oldLogin: '" + url + "' parms: '" + parms + "'")
331 agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
332 headers={'Content-Type': "application/x-www-form-urlencoded", 'Content-Length': str(len(parms))
333 }, postdata=parms).addCallback(self._gotPageLogin).addCallback(callback).addErrback(self._errorLogin)
335 debug("[FritzCallFBF] _oldLogin: no password, calling " + repr(callback))
338 def _md5Login(self, callback, sidXml):
339 def buildResponse(challenge, text):
340 debug("[FritzCallFBF] _md5Login7buildResponse: challenge: " + challenge + ' text: ' + text)
341 text = (challenge + '-' + text).decode('utf-8','ignore').encode('utf-16-le')
342 for i in range(len(text)):
343 if ord(text[i]) > 255:
347 debug("[FritzCallFBF] md5Login/buildResponse: " + m.hexdigest())
348 return challenge + '-' + m.hexdigest()
350 debug("[FritzCallFBF] _md5Login")
351 found = re.match('.*<SID>([^<]*)</SID>', sidXml, re.S)
353 self._md5Sid = found.group(1)
354 debug("[FritzCallFBF] _md5Login: SID "+ self._md5Sid)
356 debug("[FritzCallFBF] _md5Login: no sid! That must be an old firmware.")
357 self._oldLogin(callback, 'No error')
360 debug("[FritzCallFBF] _md5Login: renew timestamp: " + time.ctime(self._md5LoginTimestamp) + " time: " + time.ctime())
361 self._md5LoginTimestamp = time.time()
362 if sidXml.find('<iswriteaccess>0</iswriteaccess>') != -1:
363 debug("[FritzCallFBF] _md5Login: logging in")
364 found = re.match('.*<Challenge>([^<]*)</Challenge>', sidXml, re.S)
366 challenge = found.group(1)
367 debug("[FritzCallFBF] _md5Login: challenge " + challenge)
370 debug("[FritzCallFBF] _md5Login: login necessary and no challenge! That is terribly wrong.")
372 'getpage':'../html/de/menus/menu2.html', # 'var:pagename':'home', 'var:menu':'home',
373 'login:command/response': buildResponse(challenge, config.plugins.FritzCall.password.value),
375 url = "http://%s/cgi-bin/webcm" % (config.plugins.FritzCall.hostname.value)
376 debug("[FritzCallFBF] _md5Login: '" + url + "' parms: '" + parms + "'")
379 agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
380 headers={'Content-Type': "application/x-www-form-urlencoded", 'Content-Length': str(len(parms))
381 }, postdata=parms).addCallback(self._gotPageLogin).addCallback(callback).addErrback(self._errorLogin)
382 elif callback: # we assume value 1 here, no login necessary
383 debug("[FritzCallFBF] _md5Login: no login necessary")
386 def _gotPageLogin(self, html):
388 self._callScreen.updateStatus(_("login verification"))
389 debug("[FritzCallFBF] _gotPageLogin: verify login")
390 found = re.match('.*<p class="errorMessage">FEHLER: ([^<]*)</p>', html, re.S)
392 text = _("FRITZ!Box - Error logging in: %s") + found.group(1)
396 self._callScreen.updateStatus(_("login ok"))
398 found = re.match('.*<input type="hidden" name="sid" value="([^\"]*)"', html, re.S)
400 self._md5Sid = found.group(1)
401 debug("[FritzCallFBF] _gotPageLogin: found sid: " + self._md5Sid)
403 def _errorLogin(self, error):
404 debug("[FritzCallFBF] _errorLogin: %s" % (error))
405 text = _("FRITZ!Box - Error logging in: %s") % error
409 if self._md5LoginTimestamp:
410 self._md5LoginTimestamp = None
412 'getpage':'../html/de/menus/menu2.html', # 'var:pagename':'home', 'var:menu':'home',
413 'login:command/logout':'bye bye Fritz'
415 url = "http://%s/cgi-bin/webcm" % (config.plugins.FritzCall.hostname.value)
416 debug("[FritzCallFBF] logout: '" + url + "' parms: '" + parms + "'")
419 agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
420 headers={'Content-Type': "application/x-www-form-urlencoded", 'Content-Length': str(len(parms))
421 }, postdata=parms).addErrback(self._errorLogout)
423 def _errorLogout(self, error):
424 debug("[FritzCallFBF] _errorLogout: %s" % (error))
425 text = _("FRITZ!Box - Error logging out: %s") % error
428 def loadFritzBoxPhonebook(self):
429 debug("[FritzCallFBF] loadFritzBoxPhonebook")
430 if config.plugins.FritzCall.fritzphonebook.value:
431 self._phoneBookID = '0'
432 debug("[FritzCallFBF] loadFritzBoxPhonebook: logging in")
433 self._login(self._loadFritzBoxPhonebook)
435 def _loadFritzBoxPhonebook(self, html):
437 found = re.match('.*<p class="errorMessage">FEHLER: ([^<]*)</p>', html, re.S)
439 self._errorLoad('Login: ' + found.group(1))
442 'getpage':'../html/de/menus/menu2.html',
444 'var:pagename':'fonbuch',
447 'telcfg:settings/Phonebook/Books/Select':self._phoneBookID, # this selects always the first phonbook
449 url = "http://%s/cgi-bin/webcm" % (config.plugins.FritzCall.hostname.value)
450 debug("[FritzCallFBF] _loadFritzBoxPhonebook: '" + url + "' parms: '" + parms + "'")
453 agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
454 headers={'Content-Type': "application/x-www-form-urlencoded", 'Content-Length': str(len(parms))
455 }, postdata=parms).addCallback(self._parseFritzBoxPhonebook).addErrback(self._errorLoad)
457 def _parseFritzBoxPhonebook(self, html):
458 debug("[FritzCallFBF] _parseFritzBoxPhonebook")
459 # if not re.search('document.write\(TrFon\("[^"]+", "[^"]+", "[^"]+", "[^"]+", "[^"]+"\)\)', html):
460 if re.search('document.write\(TrFon1\(\)', html):
461 #===============================================================================
462 # New Style: 7270 (FW 54.04.58, 54.04.63-11941, 54.04.70, 54.04.74-14371, 54.04.76)
463 # 7170 (FW 29.04.70) 22.03.2009
464 # 7141 (FW 40.04.68) 22.03.2009
465 # We expect one line with TrFonName followed by several lines with
466 # TrFonNr(Type,Number,Shortcut,Vanity), which all belong to the name in TrFonName.
467 #===============================================================================
468 found = re.match('.*<input type="hidden" name="telcfg:settings/Phonebook/Books/Name(\d+)" value="[Dd]reambox" id="uiPostPhonebookName\d+" disabled>', html, re.S)
470 phoneBookID = found.group(1)
471 debug("[FritzCallFBF] _parseFritzBoxPhonebook: found dreambox phonebook with id: " + phoneBookID)
472 if self._phoneBookID != phoneBookID:
473 self._phoneBookID = phoneBookID
474 debug("[FritzCallFBF] _parseFritzBoxPhonebook: reload phonebook")
475 self._loadFritzBoxPhonebook(self._phoneBookID) # reload with dreambox phonebook
478 found = re.match('.*<meta http-equiv=content-type content="text/html; charset=([^"]*)">', html, re.S)
480 charset = found.group(1)
481 debug("[FritzCallFBF] _parseFritzBoxPhonebook: found charset: " + charset)
482 html = html2unicode(html.decode(charset)).encode('utf-8') # this looks silly, but has to be
483 else: # this is kind of emergency conversion...
485 html = html2unicode(html.decode('utf-8')).encode('utf-8') # this looks silly, but has to be
486 except UnicodeDecodeError:
487 html = html2unicode(html.decode('iso-8859-1')).encode('utf-8') # this looks silly, but has to be
488 entrymask = re.compile('(TrFonName\("[^"]+", "[^"]+", "[^"]*"\);.*?)document.write\(TrFon1\(\)', re.S)
489 entries = entrymask.finditer(html)
490 for entry in entries:
491 # debug(entry.group(1)
492 # TrFonName (id, name, category)
493 found = re.match('TrFonName\("[^"]*", "([^"]+)", "[^"]*"\);', entry.group(1))
495 name = found.group(1).replace(',','').strip()
498 # TrFonNr (type, rufnr, code, vanity)
499 detailmask = re.compile('TrFonNr\("([^"]*)", "([^"]*)", "([^"]*)", "([^"]*)"\);', re.S)
500 details = detailmask.finditer(entry.group(1))
501 for found in details:
502 thisnumber = found.group(2).strip()
504 debug("[FritzCallFBF] Ignoring entry with empty number for '''%s'''" % (name))
508 type = found.group(1)
509 if config.plugins.FritzCall.showType.value:
511 thisname = thisname + " (" + _("mobile") + ")"
513 thisname = thisname + " (" + _("home") + ")"
515 thisname = thisname + " (" + _("work") + ")"
517 if config.plugins.FritzCall.showShortcut.value and found.group(3):
518 thisname = thisname + ", " + _("Shortcut") + ": " + found.group(3)
519 if config.plugins.FritzCall.showVanity.value and found.group(4):
520 thisname = thisname + ", " + _("Vanity") + ": " + found.group(4)
522 debug("[FritzCallFBF] Adding '''%s''' with '''%s''' from FRITZ!Box Phonebook!" % (thisname.strip(), thisnumber))
523 # Beware: strings in phonebook.phonebook have to be in utf-8!
524 phonebook.phonebook[thisnumber] = thisname
526 elif re.search('document.write\(TrFon\(', html):
527 #===============================================================================
528 # Old Style: 7050 (FW 14.04.33)
529 # We expect one line with TrFon(No,Name,Number,Shortcut,Vanity)
530 # Encoding should be plain Ascii...
531 #===============================================================================
532 entrymask = re.compile('TrFon\("[^"]*", "([^"]*)", "([^"]*)", "([^"]*)", "([^"]*)"\)', re.S)
533 entries = entrymask.finditer(html)
534 for found in entries:
535 name = found.group(1).strip().replace(',','')
536 # debug("[FritzCallFBF] pos: %s name: %s" %(found.group(0),name))
537 thisnumber = found.group(2).strip()
538 if config.plugins.FritzCall.showShortcut.value and found.group(3):
539 name = name + ", " + _("Shortcut") + ": " + found.group(3)
540 if config.plugins.FritzCall.showVanity.value and found.group(4):
541 name = name + ", " + _("Vanity") + ": " + found.group(4)
543 name = html2unicode(unicode(name)).encode('utf-8')
544 debug("[FritzCallFBF] Adding '''%s''' with '''%s''' from FRITZ!Box Phonebook!" % (name, thisnumber))
545 # Beware: strings in phonebook.phonebook have to be in utf-8!
546 phonebook.phonebook[thisnumber] = name
548 debug("[FritzCallFBF] ignoring empty number for %s" % name)
550 elif self._md5Sid=='0000000000000000': # retry, it could be a race condition
551 debug("[FritzCallFBF] _parseFritzBoxPhonebook: retry loading phonebook")
552 self.loadFritzBoxPhonebook()
554 self._notify(_("Could not parse FRITZ!Box Phonebook entry"))
556 def _errorLoad(self, error):
557 debug("[FritzCallFBF] _errorLoad: %s" % (error))
558 text = _("FRITZ!Box - Could not load phonebook: %s") % error
561 def getCalls(self, callScreen, callback, type):
563 # call sequence must be:
565 # - getPage -> _gotPageLogin
566 # - loginCallback (_getCalls)
567 # - getPage -> _getCalls1
568 debug("[FritzCallFBF] getCalls")
569 self._callScreen = callScreen
570 self._callType = type
571 if (time.time() - self._callTimestamp) > 180:
572 debug("[FritzCallFBF] getCalls: outdated data, login and get new ones: " + time.ctime(self._callTimestamp) + " time: " + time.ctime())
573 self._callTimestamp = time.time()
574 self._login(lambda x:self._getCalls(callback,x))
575 elif not self._callList:
576 debug("[FritzCallFBF] getCalls: time is ok, but no callList")
577 self._getCalls1(callback)
579 debug("[FritzCallFBF] getCalls: time is ok, callList is ok")
580 self._gotPageCalls(callback)
582 def _getCalls(self, callback, html):
584 found = re.match('.*<p class="errorMessage">FEHLER: ([^<]*)</p>', html, re.S)
586 self._errorCalls('Login: ' + found.group(1))
589 # we need this to fill Anrufliste.csv
590 # http://repeater1/cgi-bin/webcm?getpage=../html/de/menus/menu2.html&var:lang=de&var:menu=fon&var:pagename=foncalls
592 debug("[FritzCallFBF] _getCalls")
594 found = re.match('.*<p class="errorMessage">FEHLER: ([^<]*)</p>', html, re.S)
596 text = _("FRITZ!Box - Error logging in: %s") + found.group(1)
601 self._callScreen.updateStatus(_("preparing"))
602 parms = urlencode({'getpage':'../html/de/menus/menu2.html', 'var:lang':'de', 'var:pagename':'foncalls', 'var:menu':'fon', 'sid':self._md5Sid})
603 url = "http://%s/cgi-bin/webcm?%s" % (config.plugins.FritzCall.hostname.value, parms)
604 getPage(url).addCallback(lambda x:self._getCalls1(callback)).addErrback(self._errorCalls) #@UnusedVariable
606 def _getCalls1(self, callback):
608 # finally we should have successfully lgged in and filled the csv
610 debug("[FritzCallFBF] _getCalls1")
612 self._callScreen.updateStatus(_("finishing"))
613 parms = urlencode({'getpage':'../html/de/FRITZ!Box_Anrufliste.csv', 'sid':self._md5Sid})
614 url = "http://%s/cgi-bin/webcm?%s" % (config.plugins.FritzCall.hostname.value, parms)
615 getPage(url).addCallback(lambda x:self._gotPageCalls(callback,x)).addErrback(self._errorCalls)
617 def _gotPageCalls(self, callback, csv=""):
618 def _resolveNumber(number):
620 if config.plugins.FritzCall.internal.value and len(number) > 3 and number[0] == "0": number = number[1:]
622 number = stripCbCPrefix(number, config.plugins.FritzCall.country.value)
623 if config.plugins.FritzCall.prefix.value and number and number[0] != '0': # should only happen for outgoing
624 number = config.plugins.FritzCall.prefix.value + number
625 name = phonebook.search(number)
627 found = re.match('(.*?)\n.*', name)
629 name = found.group(1)
632 name = resolveNumberWithAvon(number, config.plugins.FritzCall.country.value)
634 number = number + ' ' + name
636 number = _("UNKNOWN")
637 # if len(number) > 20: number = number[:20]
641 debug("[FritzCallFBF] _gotPageCalls: got csv, setting callList")
643 self._callScreen.updateStatus(_("done"))
644 # check for error: wrong password or password not set... TODO
645 found = re.search('Melden Sie sich mit dem Kennwort der FRITZ!Box an', csv)
647 text = _("You need to set the password of the FRITZ!Box\nin the configuration dialog to display calls\n\nIt could be a communication issue, just try again.")
648 # self.session.open(MessageBox, text, MessageBox.TYPE_ERROR, timeout=config.plugins.FritzCall.timeout.value)
652 csv = csv.decode('iso-8859-1', 'replace').encode('utf-8', 'replace')
653 lines = csv.splitlines()
654 self._callList = lines
656 debug("[FritzCallFBF] _gotPageCalls: got no csv, but have callList")
658 self._callScreen.updateStatus(_("done, using last list"))
659 lines = self._callList
661 debug("[FritzCallFBF] _gotPageCalls: got no csv, no callList, leaving")
666 # Typ;Datum;Name;Rufnummer;Nebenstelle;Eigene Rufnummer;Dauer
667 found = re.match("^(" + self._callType + ");([^;]*);([^;]*);([^;]*);([^;]*);([^;]*);([^;]*)", line)
669 direct = found.group(1)
670 date = found.group(2)
671 length = found.group(7)
672 remote = _resolveNumber(found.group(4))
673 if not remote and direct != FBF_OUT_CALLS and found.group(3):
674 remote = found.group(3)
675 found1 = re.match('Internet: (.*)', found.group(6))
677 here = _resolveNumber(found1.group(1))
679 here = _resolveNumber(found.group(6))
681 number = stripCbCPrefix(found.group(4), config.plugins.FritzCall.country.value)
682 if config.plugins.FritzCall.prefix.value and number and number[0] != '0': # should only happen for outgoing
683 number = config.plugins.FritzCall.prefix.value + number
684 _callList.append((number, date, here, direct, remote, length))
686 # debug("[FritzCallFBF] _gotPageCalls result:\n" + text
689 # debug("[FritzCallFBF] _gotPageCalls call callback with\n" + text
691 self._callScreen = None
693 def _errorCalls(self, error):
694 debug("[FritzCallFBF] _errorCalls: %s" % (error))
695 text = _("FRITZ!Box - Could not load calls: %s") % error
698 def dial(self, number):
699 ''' initiate a call to number '''
701 self._login(self._dial)
703 def _dial(self, html):
705 found = re.match('.*<p class="errorMessage">FEHLER: ([^<]*)</p>', html, re.S)
707 self._errorDial('Login: ' + found.group(1))
709 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
711 'getpage':'../html/de/menus/menu2.html',
712 'var:pagename':'fonbuch',
714 'telcfg:settings/UseClickToDial':'1',
715 'telcfg:settings/DialPort':config.plugins.FritzCall.extension.value,
716 'telcfg:command/Dial':self.number,
719 debug("[FritzCallFBF] dial url: '" + url + "' parms: '" + parms + "'")
722 agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
724 'Content-Type': "application/x-www-form-urlencoded",
725 'Content-Length': str(len(parms))},
726 postdata=parms).addCallback(self._okDial).addErrback(self._errorDial)
728 def _okDial(self, html): #@UnusedVariable
729 debug("[FritzCallFBF] okDial")
731 def _errorDial(self, error):
732 debug("[FritzCallFBF] errorDial: $s" % error)
733 text = _("FRITZ!Box - Dialling failed: %s") % error
736 def changeWLAN(self, statusWLAN):
737 ''' get status info from FBF '''
738 debug("[FritzCallFBF] changeWLAN start")
739 if not statusWLAN or (statusWLAN <> '1' and statusWLAN <> '0'):
741 self.statusWLAN = statusWLAN
742 self._login(self._changeWLAN)
744 def _changeWLAN(self, html):
746 found = re.match('.*<p class="errorMessage">FEHLER: ([^<]*)</p>', html, re.S)
748 self._errorChangeWLAN('Login: ' + found.group(1))
750 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
752 'getpage':'../html/de/menus/menu2.html',
754 'var:pagename':'wlan',
756 'wlan:settings/ap_enabled':str(self.statusWLAN),
759 debug("[FritzCallFBF] changeWLAN url: '" + url + "' parms: '" + parms + "'")
762 agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
764 'Content-Type': "application/x-www-form-urlencoded",
765 'Content-Length': str(len(parms))},
766 postdata=parms).addCallback(self._okChangeWLAN).addErrback(self._errorChangeWLAN)
768 def _okChangeWLAN(self, html): #@UnusedVariable
769 debug("[FritzCallFBF] okDial")
771 def _errorChangeWLAN(self, error):
772 debug("[FritzCallFBF] _errorChangeWLAN: $s" % error)
773 text = _("FRITZ!Box - Failed changing WLAN: %s") % error
776 def changeMailbox(self, whichMailbox):
777 ''' switch mailbox on/off '''
778 debug("[FritzCallFBF] changeMailbox start: " + str(whichMailbox))
779 self.whichMailbox = whichMailbox
780 self._login(self._changeMailbox)
782 def _changeMailbox(self, html):
784 found = re.match('.*<p class="errorMessage">FEHLER: ([^<]*)</p>', html, re.S)
786 self._errorChangeMailbox('Login: ' + found.group(1))
788 debug("[FritzCallFBF] _changeMailbox")
789 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
790 if self.whichMailbox == -1:
792 if self.info[FBF_tamActive][i+1]:
797 'tam:settings/TAM'+str(i)+'/Active':state,
800 debug("[FritzCallFBF] changeMailbox url: '" + url + "' parms: '" + parms + "'")
803 agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
805 'Content-Type': "application/x-www-form-urlencoded",
806 'Content-Length': str(len(parms))},
807 postdata=parms).addCallback(self._okChangeMailbox).addErrback(self._errorChangeMailbox)
808 elif self.whichMailbox > 4:
809 debug("[FritzCallFBF] changeMailbox invalid mailbox number")
811 if self.info[FBF_tamActive][self.whichMailbox+1]:
816 'tam:settings/TAM'+str(self.whichMailbox)+'/Active':state,
819 debug("[FritzCallFBF] changeMailbox url: '" + url + "' parms: '" + parms + "'")
822 agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
824 'Content-Type': "application/x-www-form-urlencoded",
825 'Content-Length': str(len(parms))},
826 postdata=parms).addCallback(self._okChangeMailbox).addErrback(self._errorChangeMailbox)
828 def _okChangeMailbox(self, html): #@UnusedVariable
829 debug("[FritzCallFBF] _okChangeMailbox")
831 def _errorChangeMailbox(self, error):
832 debug("[FritzCallFBF] _errorChangeMailbox: $s" % error)
833 text = _("FRITZ!Box - Failed changing Mailbox: %s") % error
836 def getInfo(self, callback):
837 ''' get status info from FBF '''
838 debug("[FritzCallFBF] getInfo")
839 self._login(lambda x:self._getInfo(callback,x))
841 def _getInfo(self, callback, html):
842 # http://192.168.178.1/cgi-bin/webcm?getpage=../html/de/menus/menu2.html&var:lang=de&var:pagename=home&var:menu=home
843 debug("[FritzCallFBF] _getInfo: verify login")
845 found = re.match('.*<p class="errorMessage">FEHLER: ([^<]*)</p>', html, re.S)
847 self._errorGetInfo('Login: ' + found.group(1))
850 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
852 'getpage':'../html/de/menus/menu2.html',
854 'var:pagename':'home',
858 debug("[FritzCallFBF] _getInfo url: '" + url + "' parms: '" + parms + "'")
861 agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
863 'Content-Type': "application/x-www-form-urlencoded",
864 'Content-Length': str(len(parms))},
865 postdata=parms).addCallback(lambda x:self._okGetInfo(callback,x)).addErrback(self._errorGetInfo)
867 def _okGetInfo(self, callback, html):
870 (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dectActive, faxActive, rufumlActive) = self.info
872 (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dectActive, faxActive, rufumlActive) = (None, None, None, None, None, None, None, None, None)
874 debug("[FritzCallFBF] _okGetInfo/readinfo")
875 found = re.match('.*<table class="tborder" id="tProdukt">\s*<tr>\s*<td style="padding-top:2px;">([^<]*)</td>\s*<td style="padding-top:2px;text-align:right;">\s*([^\s]*)\s*</td>', html, re.S)
877 boxInfo = found.group(1)+ ', ' + found.group(2)
878 boxInfo = boxInfo.replace(' ',' ')
879 # debug("[FritzCallFBF] _okGetInfo Boxinfo: " + boxInfo)
881 found = re.match('.*<p class="ac">([^<]*)</p>', html, re.S)
883 # debug("[FritzCallFBF] _okGetInfo Boxinfo: " + found.group(1))
884 boxInfo = found.group(1)
886 if html.find('home_coninf.txt') != -1:
887 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
889 'getpage':'../html/de/home/home_coninf.txt',
892 # debug("[FritzCallFBF] get coninfo: url: '" + url + "' parms: '" + parms + "'")
895 agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
897 'Content-Type': "application/x-www-form-urlencoded",
898 'Content-Length': str(len(parms))},
899 postdata=parms).addCallback(lambda x:self._okSetConInfo(callback,x)).addErrback(self._errorGetInfo)
901 found = re.match('.*if \(isNaN\(jetzt\)\)\s*return "";\s*var str = "([^"]*)";', html, re.S)
903 # debug("[FritzCallFBF] _okGetInfo Uptime: " + found.group(1))
904 upTime = found.group(1)
906 found = re.match('.*str = g_pppSeit \+"([^<]*)<br>"\+mldIpAdr;', html, re.S)
908 # debug("[FritzCallFBF] _okGetInfo Uptime: " + found.group(1))
909 upTime = found.group(1)
911 found = re.match(".*IpAdrDisplay\('([.\d]+)'\)", html, re.S)
913 # debug("[FritzCallFBF] _okGetInfo IpAdrDisplay: " + found.group(1))
914 ipAddress = found.group(1)
916 if html.find('g_tamActive') != -1:
917 entries = re.compile('if \("(\d)" == "1"\) {\s*g_tamActive \+= 1;\s*}', re.S).finditer(html)
918 tamActive = [0, False, False, False, False, False]
920 for entry in entries:
921 state = entry.group(1)
926 # debug("[FritzCallFBF] _okGetInfo tamActive: " + str(tamActive))
928 if html.find('home_dect.txt') != -1:
929 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
931 'getpage':'../html/de/home/home_dect.txt',
934 # debug("[FritzCallFBF] get coninfo: url: '" + url + "' parms: '" + parms + "'")
937 agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
939 'Content-Type': "application/x-www-form-urlencoded",
940 'Content-Length': str(len(parms))},
941 postdata=parms).addCallback(lambda x:self._okSetDect(callback,x)).addErrback(self._errorGetInfo)
943 if html.find('countDect2') != -1:
944 entries = re.compile('if \("1" == "1"\) countDect2\+\+;', re.S).findall(html)
945 dectActive = len(entries)
946 # debug("[FritzCallFBF] _okGetInfo dectActive: " + str(dectActive))
948 found = re.match('.*var g_intFaxActive = "0";\s*if \("1" != ""\) {\s*g_intFaxActive = "1";\s*}\s*', html, re.S)
951 # debug("[FritzCallFBF] _okGetInfo faxActive")
953 if html.find('cntRufumleitung') != -1:
954 entries = re.compile('mode = "1";\s*ziel = "[^"]+";\s*if \(mode == "1" \|\| ziel != ""\)\s*{\s*g_RufumleitungAktiv = true;', re.S).findall(html)
955 rufumlActive = len(entries)
956 entries = re.compile('if \("([^"]*)"=="([^"]*)"\) isAllIncoming\+\+;', re.S).finditer(html)
958 for entry in entries:
959 # debug("[FritzCallFBF] _okGetInfo rufumlActive add isAllIncoming")
960 if entry.group(1) == entry.group(2): isAllIncoming += 1
961 if isAllIncoming==2 and rufumlActive>0: rufumlActive -= 1
962 # debug("[FritzCallFBF] _okGetInfo rufumlActive: " + str(rufumlActive))
964 # /cgi-bin/webcm?getpage=../html/de/home/home_dsl.txt
965 # { "dsl_carrier_state": "5", "umts_enabled": "0", "ata_mode": "0", "isusbgsm": "", "dsl_ds_nrate": "3130", "dsl_us_nrate": "448", "hint_dsl_no_cable": "0", "wds_enabled": "0", "wds_hop": "0", "isata": "" }
966 if html.find('home_dsl.txt') != -1:
967 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
969 'getpage':'../html/de/home/home_dsl.txt',
972 # debug("[FritzCallFBF] get dsl state: url: '" + url + "' parms: '" + parms + "'")
975 agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
977 'Content-Type': "application/x-www-form-urlencoded",
978 'Content-Length': str(len(parms))},
979 postdata=parms).addCallback(lambda x:self._okSetDslState(callback,x)).addErrback(self._errorGetInfo)
981 found = re.match('.*function DslStateDisplay \(state\){\s*var state = "(\d+)";', html, re.S)
983 # debug("[FritzCallFBF] _okGetInfo DslState: " + found.group(1))
984 dslState = [ found.group(1), None ] # state, speed
985 found = re.match('.*function DslStateDisplay \(state\){\s*var state = "\d+";.*?if \("3130" != "0"\) str = "([^"]*)";', html, re.S)
987 # debug("[FritzCallFBF] _okGetInfo DslSpeed: " + found.group(1).strip())
988 dslState[1] = found.group(1).strip()
990 # /cgi-bin/webcm?getpage=../html/de/home/home_wlan.txt
991 # { "ap_enabled": "1", "active_stations": "0", "encryption": "4", "wireless_stickandsurf_enabled": "0", "is_macfilter_active": "0", "wmm_enabled": "1", "wlan_state": [ "end" ] }
992 if html.find('home_wlan.txt') != -1:
993 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
995 'getpage':'../html/de/home/home_wlan.txt',
998 # debug("[FritzCallFBF] get wlan state: url: '" + url + "' parms: '" + parms + "'")
1001 agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
1003 'Content-Type': "application/x-www-form-urlencoded",
1004 'Content-Length': str(len(parms))},
1005 postdata=parms).addCallback(lambda x:self._okSetWlanState(callback,x)).addErrback(self._errorGetInfo)
1007 found = re.match('.*function WlanStateLed \(state\){.*?return StateLed\("(\d+)"\);\s*}', html, re.S)
1009 # debug("[FritzCallFBF] _okGetInfo WlanState: " + found.group(1))
1010 wlanState = [ found.group(1), 0, 0 ] # state, encryption, number of devices
1011 found = re.match('.*var (?:g_)?encryption = "(\d+)";', html, re.S)
1013 # debug("[FritzCallFBF] _okGetInfo WlanEncrypt: " + found.group(1))
1014 wlanState[1] = found.group(1)
1016 return (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dectActive, faxActive, rufumlActive)
1018 debug("[FritzCallFBF] _okGetInfo")
1019 info = readInfo(html)
1020 debug("[FritzCallFBF] _okGetInfo info: " + str(info))
1025 def _okSetDect(self, callback, html):
1026 # debug("[FritzCallFBF] _okSetDect: " + html)
1027 # found = re.match('.*"connection_status":"(\d+)".*"connection_ip":"([.\d]+)".*"connection_detail":"([^"]+)".*"connection_uptime":"([^"]+)"', html, re.S)
1028 if html.find('"dect_enabled": "1"') != -1:
1029 # debug("[FritzCallFBF] _okSetDect: dect_enabled")
1030 found = re.match('.*"dect_device_list":.*\[([^\]]*)\]', html, re.S)
1032 # debug("[FritzCallFBF] _okSetDect: dect_device_list: %s" %(found.group(1)))
1033 entries = re.compile('"1"', re.S).findall(found.group(1))
1034 dectActive = len(entries)
1035 (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dummy, faxActive, rufumlActive) = self.info
1036 self.info = (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dectActive, faxActive, rufumlActive)
1037 debug("[FritzCallFBF] _okSetDect info: " + str(self.info))
1041 def _okSetConInfo(self, callback, html):
1042 # debug("[FritzCallFBF] _okSetConInfo: " + html)
1043 # found = re.match('.*"connection_status":"(\d+)".*"connection_ip":"([.\d]+)".*"connection_detail":"([^"]+)".*"connection_uptime":"([^"]+)"', html, re.S)
1044 found = re.match('.*"connection_ip": "([.\d]+)".*"connection_uptime": "([^"]+)"', html, re.S)
1046 # debug("[FritzCallFBF] _okSetConInfo: ipAddress: %s upTime: %s" %( found.group(1), found.group(2)))
1047 ipAddress = found.group(1)
1048 upTime = found.group(2)
1049 (boxInfo, dummy, dummy, wlanState, dslState, tamActive, dectActive, faxActive, rufumlActive) = self.info
1050 self.info = (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dectActive, faxActive, rufumlActive)
1051 debug("[FritzCallFBF] _okSetWlanState info: " + str(self.info))
1055 def _okSetWlanState(self, callback, html):
1056 # debug("[FritzCallFBF] _okSetWlanState: " + html)
1057 found = re.match('.*"ap_enabled": "(\d+)"', html, re.S)
1059 # debug("[FritzCallFBF] _okSetWlanState: ap_enabled: " + found.group(1))
1060 wlanState = [ found.group(1), None, None ]
1061 found = re.match('.*"encryption": "(\d+)"', html, re.S)
1063 # debug("[FritzCallFBF] _okSetWlanState: encryption: " + found.group(1))
1064 wlanState[1] = found.group(1)
1065 found = re.match('.*"active_stations": "(\d+)"', html, re.S)
1067 # debug("[FritzCallFBF] _okSetWlanState: active_stations: " + found.group(1))
1068 wlanState[2] = found.group(1)
1069 (boxInfo, upTime, ipAddress, dummy, dslState, tamActive, dectActive, faxActive, rufumlActive) = self.info
1070 self.info = (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dectActive, faxActive, rufumlActive)
1071 debug("[FritzCallFBF] _okSetWlanState info: " + str(self.info))
1075 def _okSetDslState(self, callback, html):
1076 # debug("[FritzCallFBF] _okSetDslState: " + html)
1077 found = re.match('.*"dsl_carrier_state": "(\d+)"', html, re.S)
1079 # debug("[FritzCallFBF] _okSetDslState: dsl_carrier_state: " + found.group(1))
1080 dslState = [ found.group(1), None ]
1081 found = re.match('.*"dsl_ds_nrate": "(\d+)"', html, re.S)
1083 # debug("[FritzCallFBF] _okSetDslState: dsl_ds_nrate: " + found.group(1))
1084 dslState[1] = found.group(1)
1085 found = re.match('.*"dsl_us_nrate": "(\d+)"', html, re.S)
1087 # debug("[FritzCallFBF] _okSetDslState: dsl_us_nrate: " + found.group(1))
1088 dslState[1] = dslState[1] + '/' + found.group(1)
1089 (boxInfo, upTime, ipAddress, wlanState, dummy, tamActive, dectActive, faxActive, rufumlActive) = self.info
1090 self.info = (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dectActive, faxActive, rufumlActive)
1091 debug("[FritzCallFBF] _okSetDslState info: " + str(self.info))
1095 def _errorGetInfo(self, error):
1096 debug("[FritzCallFBF] _errorGetInfo: %s" % (error))
1097 text = _("FRITZ!Box - Error getting status: %s") % error
1099 # linkP = open("/tmp/FritzCall_errorGetInfo.htm", "w")
1100 # linkP.write(error)
1104 self._login(self._reset)
1106 def _reset(self, html):
1107 # POSTDATA=getpage=../html/reboot.html&errorpage=../html/de/menus/menu2.html&var:lang=de&var:pagename=home&var:errorpagename=home&var:menu=home&var:pagemaster=&time:settings/time=1242207340%2C-120&var:tabReset=0&logic:command/reboot=../gateway/commands/saveconfig.html
1109 found = re.match('.*<p class="errorMessage">FEHLER: ([^<]*)</p>', html, re.S)
1111 self._errorReset('Login: ' + found.group(1))
1113 if self._callScreen:
1114 self._callScreen.close()
1115 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
1117 'getpage':'../html/reboot.html',
1119 'var:pagename':'reset',
1120 'var:menu':'system',
1121 'logic:command/reboot':'../gateway/commands/saveconfig.html',
1124 debug("[FritzCallFBF] _reset url: '" + url + "' parms: '" + parms + "'")
1127 agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
1129 'Content-Type': "application/x-www-form-urlencoded",
1130 'Content-Length': str(len(parms))},
1133 def _okReset(self, html): #@UnusedVariable
1134 debug("[FritzCallFBF] _okReset")
1136 def _errorReset(self, error):
1137 debug("[FritzCallFBF] _errorReset: %s" % (error))
1138 text = _("FRITZ!Box - Error resetting: %s") % error
1141 #===============================================================================
1143 # ''' hangup call on port; not used for now '''
1144 # url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
1145 # parms = urlencode({
1146 # 'id':'uiPostForm',
1147 # 'name':'uiPostForm',
1148 # 'login:command/password': config.plugins.FritzCall.password.value,
1149 # 'telcfg:settings/UseClickToDial':'1',
1150 # 'telcfg:settings/DialPort':config.plugins.FritzCall.extension.value,
1151 # 'telcfg:command/Hangup':'',
1152 # 'sid':self._md5Sid
1154 # debug("[FritzCallFBF] hangup url: '" + url + "' parms: '" + parms + "'")
1158 # 'Content-Type': "application/x-www-form-urlencoded",
1159 # 'Content-Length': str(len(parms))},
1161 #===============================================================================
1163 fritzbox = FritzCallFBF()
1165 class FritzMenu(Screen,HelpableScreen):
1166 def __init__(self, session):
1167 fontSize = scaleV(24,21) # indeed this is font size +2
1168 noButtons = 2 # reset, wlan
1170 if not fritzbox or not fritzbox.info: return
1172 if fritzbox.info[FBF_tamActive]:
1173 noButtons += 1 # toggle mailboxes
1174 width = max(DESKTOP_WIDTH - scaleH(500,250), noButtons*140+(noButtons+1)*10)
1175 # boxInfo 2 lines, gap, internet 2 lines, gap, dsl/wlan each 1 line, gap, buttons
1176 height = 5 + 2*fontSize + 10 + 2*fontSize + 10 + 2*fontSize + 10 + 40 + 5
1177 if fritzbox.info[FBF_tamActive] is not None: height += fontSize
1178 if fritzbox.info[FBF_dectActive] is not None: height += fontSize
1179 if fritzbox.info[FBF_faxActive] is not None: height += fontSize
1180 if fritzbox.info[FBF_rufumlActive] is not None: height += fontSize
1181 buttonsGap = (width-noButtons*140)/(noButtons+1)
1182 buttonsVPos = height-40-5
1185 if fritzbox.info[FBF_tamActive] is not None:
1187 <widget name="FBFMailbox" position="%d,%d" size="%d,%d" font="Regular;%d" />
1188 <widget name="mailbox_inactive" pixmap="skin_default/buttons/button_green_off.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
1189 <widget name="mailbox_active" pixmap="skin_default/buttons/button_green.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
1190 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
1191 <widget name="key_yellow" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1193 40, 5+2*fontSize+10+varLinePos*fontSize+10, # position mailbox
1194 width-40-20, fontSize, # size mailbox
1196 20, 5+2*fontSize+10+varLinePos*fontSize+10+(fontSize-16)/2, # position button mailbox
1197 20, 5+2*fontSize+10+varLinePos*fontSize+10+(fontSize-16)/2, # position button mailbox
1198 noButtons*buttonsGap+(noButtons-1)*140, buttonsVPos,
1199 noButtons*buttonsGap+(noButtons-1)*140, buttonsVPos,
1205 if fritzbox.info[FBF_dectActive] is not None:
1207 <widget name="FBFDect" position="%d,%d" size="%d,%d" font="Regular;%d" />
1208 <widget name="dect_inactive" pixmap="skin_default/buttons/button_green_off.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
1209 <widget name="dect_active" pixmap="skin_default/buttons/button_green.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
1211 40, 5+2*fontSize+10+varLinePos*fontSize+10, # position dect
1212 width-40-20, fontSize, # size dect
1214 20, 5+2*fontSize+10+varLinePos*fontSize+10+(fontSize-16)/2, # position button dect
1215 20, 5+2*fontSize+10+varLinePos*fontSize+10+(fontSize-16)/2, # position button dect
1221 if fritzbox.info[FBF_faxActive] is not None:
1223 <widget name="FBFFax" position="%d,%d" size="%d,%d" font="Regular;%d" />
1224 <widget name="fax_inactive" pixmap="skin_default/buttons/button_green_off.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
1225 <widget name="fax_active" pixmap="skin_default/buttons/button_green.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
1227 40, 5+2*fontSize+10+varLinePos*fontSize+10, # position dect
1228 width-40-20, fontSize, # size dect
1230 20, 5+2*fontSize+10+varLinePos*fontSize+10+(fontSize-16)/2, # position button dect
1231 20, 5+2*fontSize+10+varLinePos*fontSize+10+(fontSize-16)/2, # position button dect
1237 if fritzbox.info[FBF_rufumlActive] is not None:
1239 <widget name="FBFRufuml" position="%d,%d" size="%d,%d" font="Regular;%d" />
1240 <widget name="rufuml_inactive" pixmap="skin_default/buttons/button_green_off.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
1241 <widget name="rufuml_active" pixmap="skin_default/buttons/button_green.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
1243 40, 5+2*fontSize+10+varLinePos*fontSize+10, # position dect
1244 width-40-20, fontSize, # size dect
1246 20, 5+2*fontSize+10+varLinePos*fontSize+10+(fontSize-16)/2, # position button dect
1247 20, 5+2*fontSize+10+varLinePos*fontSize+10+(fontSize-16)/2, # position button dect
1254 <screen name="FritzMenu" position="%d,%d" size="%d,%d" title="%s" >
1255 <widget name="FBFInfo" position="%d,%d" size="%d,%d" font="Regular;%d" />
1256 <widget name="FBFInternet" position="%d,%d" size="%d,%d" font="Regular;%d" />
1257 <widget name="internet_inactive" pixmap="skin_default/buttons/button_green_off.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
1258 <widget name="internet_active" pixmap="skin_default/buttons/button_green.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
1259 <widget name="FBFDsl" position="%d,%d" size="%d,%d" font="Regular;%d" />
1260 <widget name="dsl_inactive" pixmap="skin_default/buttons/button_green_off.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
1261 <widget name="dsl_active" pixmap="skin_default/buttons/button_green.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
1262 <widget name="FBFWlan" position="%d,%d" size="%d,%d" font="Regular;%d" />
1263 <widget name="wlan_inactive" pixmap="skin_default/buttons/button_green_off.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
1264 <widget name="wlan_active" pixmap="skin_default/buttons/button_green.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
1269 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
1270 <widget name="key_red" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1271 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
1272 <widget name="key_green" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1274 (DESKTOP_WIDTH - width) / 2, (DESKTOP_HEIGHT - height) / 2, # position
1275 width, height, # size
1276 _("FRITZ!Box Fon Status"),
1277 40, 5, # position info
1278 width-2*40, 2*fontSize, # size info
1280 40, 5+2*fontSize+10, # position internet
1281 width-40, 2*fontSize, # size internet
1283 20, 5+2*fontSize+10+(fontSize-16)/2, # position button internet
1284 20, 5+2*fontSize+10+(fontSize-16)/2, # position button internet
1285 40, 5+2*fontSize+10+2*fontSize+10, # position dsl
1286 width-40-20, fontSize, # size dsl
1288 20, 5+2*fontSize+10+2*fontSize+10+(fontSize-16)/2, # position button dsl
1289 20, 5+2*fontSize+10+2*fontSize+10+(fontSize-16)/2, # position button dsl
1290 40, 5+2*fontSize+10+3*fontSize+10, # position wlan
1291 width-40-20, fontSize, # size wlan
1293 20, 5+2*fontSize+10+3*fontSize+10+(fontSize-16)/2, # position button wlan
1294 20, 5+2*fontSize+10+3*fontSize+10+(fontSize-16)/2, # position button wlan
1299 buttonsGap, buttonsVPos, buttonsGap, buttonsVPos,
1300 buttonsGap+140+buttonsGap, buttonsVPos, buttonsGap+140+buttonsGap, buttonsVPos,
1303 Screen.__init__(self, session)
1304 HelpableScreen.__init__(self)
1305 # TRANSLATORS: keep it short, this is a button
1306 self["key_red"] = Button(_("Reset"))
1307 # TRANSLATORS: keep it short, this is a button
1308 self["key_green"] = Button(_("Toggle WLAN"))
1309 self._mailboxActive = False
1310 if fritzbox.info[FBF_tamActive] is not None:
1311 # TRANSLATORS: keep it short, this is a button
1312 self["key_yellow"] = Button(_("Toggle Mailbox"))
1313 self["menuActions"] = ActionMap(["OkCancelActions", "ColorActions", "NumberActions", "EPGSelectActions"],
1315 "cancel": self._exit,
1318 "green": self._toggleWlan,
1319 "yellow": (lambda: self._toggleMailbox(-1)),
1320 "0": (lambda: self._toggleMailbox(0)),
1321 "1": (lambda: self._toggleMailbox(1)),
1322 "2": (lambda: self._toggleMailbox(2)),
1323 "3": (lambda: self._toggleMailbox(3)),
1324 "4": (lambda: self._toggleMailbox(4)),
1325 "info": self._getInfo,
1327 # TRANSLATORS: keep it short, this is a help text
1328 self.helpList.append((self["menuActions"], "ColorActions", [("yellow", _("Toggle all mailboxes"))]))
1329 # TRANSLATORS: keep it short, this is a help text
1330 self.helpList.append((self["menuActions"], "NumberActions", [("0", _("Toggle 1. mailbox"))]))
1331 # TRANSLATORS: keep it short, this is a help text
1332 self.helpList.append((self["menuActions"], "NumberActions", [("1", _("Toggle 2. mailbox"))]))
1333 # TRANSLATORS: keep it short, this is a help text
1334 self.helpList.append((self["menuActions"], "NumberActions", [("2", _("Toggle 3. mailbox"))]))
1335 # TRANSLATORS: keep it short, this is a help text
1336 self.helpList.append((self["menuActions"], "NumberActions", [("3", _("Toggle 4. mailbox"))]))
1337 # TRANSLATORS: keep it short, this is a help text
1338 self.helpList.append((self["menuActions"], "NumberActions", [("4", _("Toggle 5. mailbox"))]))
1339 self["FBFMailbox"] = Label(_('Mailbox'))
1340 self["mailbox_inactive"] = Pixmap()
1341 self["mailbox_active"] = Pixmap()
1342 self["mailbox_active"].hide()
1344 self["menuActions"] = ActionMap(["OkCancelActions", "ColorActions", "EPGSelectActions"],
1346 "cancel": self._exit,
1348 "green": self._toggleWlan,
1350 "info": self._getInfo,
1353 # TRANSLATORS: keep it short, this is a help text
1354 self.helpList.append((self["menuActions"], "OkCancelActions", [("cancel", _("Quit"))]))
1355 # TRANSLATORS: keep it short, this is a help text
1356 self.helpList.append((self["menuActions"], "OkCancelActions", [("ok", _("Quit"))]))
1357 # TRANSLATORS: keep it short, this is a help text
1358 self.helpList.append((self["menuActions"], "ColorActions", [("green", _("Toggle WLAN"))]))
1359 # TRANSLATORS: keep it short, this is a help text
1360 self.helpList.append((self["menuActions"], "ColorActions", [("red", _("Reset"))]))
1361 # TRANSLATORS: keep it short, this is a help text
1362 self.helpList.append((self["menuActions"], "EPGSelectActions", [("info", _("Refresh status"))]))
1364 self["FBFInfo"] = Label(_('Getting status from FRITZ!Box Fon...'))
1366 self["FBFInternet"] = Label('Internet')
1367 self["internet_inactive"] = Pixmap()
1368 self["internet_active"] = Pixmap()
1369 self["internet_active"].hide()
1371 self["FBFDsl"] = Label('DSL')
1372 self["dsl_inactive"] = Pixmap()
1373 self["dsl_inactive"].hide()
1374 self["dsl_active"] = Pixmap()
1375 self["dsl_active"].hide()
1377 self["FBFWlan"] = Label('WLAN ')
1378 self["wlan_inactive"] = Pixmap()
1379 self["wlan_inactive"].hide()
1380 self["wlan_active"] = Pixmap()
1381 self["wlan_active"].hide()
1382 self._wlanActive = False
1384 if fritzbox.info[FBF_dectActive] is not None:
1385 self["FBFDect"] = Label('DECT')
1386 self["dect_inactive"] = Pixmap()
1387 self["dect_active"] = Pixmap()
1388 self["dect_active"].hide()
1390 if fritzbox.info[FBF_faxActive] is not None:
1391 self["FBFFax"] = Label('Fax')
1392 self["fax_inactive"] = Pixmap()
1393 self["fax_active"] = Pixmap()
1394 self["fax_active"].hide()
1396 if fritzbox.info[FBF_rufumlActive] is not None:
1397 self["FBFRufuml"] = Label(_('Call redirection'))
1398 self["rufuml_inactive"] = Pixmap()
1399 self["rufuml_active"] = Pixmap()
1400 self["rufuml_active"].hide()
1402 self._timer = eTimer()
1403 self._timer.callback.append(self._getInfo)
1404 self.onShown.append(lambda: self._timer.start(5000))
1405 self.onHide.append(lambda: self._timer.stop())
1409 fritzbox.getInfo(self._fillMenu)
1411 def _fillMenu(self, status):
1412 (boxInfo, upTime, ipAddress, wlanState, dslState, tamActive, dectActive, faxActive, rufumlActive) = status
1413 self._wlanActive = (wlanState[0] == '1')
1414 self._mailboxActive = False
1417 self["FBFInfo"].setText(boxInfo.replace(', ', '\n'))
1419 self["FBFInfo"].setText('BoxInfo ' + _('Status not available'))
1423 self["FBFInternet"].setText('Internet ' + _('IP Address:') + ' ' + ipAddress + '\n' + _('Connected since') + ' ' + upTime)
1425 self["FBFInternet"].setText('Internet ' + _('IP Address:') + ' ' + ipAddress)
1426 self["internet_inactive"].hide()
1427 self["internet_active"].show()
1429 self["internet_active"].hide()
1430 self["internet_inactive"].show()
1433 if dslState[0]=='5':
1434 self["dsl_inactive"].hide()
1435 self["dsl_active"].show()
1437 self["FBFDsl"].setText('DSL ' + dslState[1])
1439 self["dsl_active"].hide()
1440 self["dsl_inactive"].show()
1442 self["FBFDsl"].setText('DSL ' + _('Status not available'))
1443 self["dsl_active"].hide()
1444 self["dsl_inactive"].hide()
1447 if wlanState[0]=='1':
1448 self["wlan_inactive"].hide()
1449 self["wlan_active"].show()
1451 if wlanState[1]=='0':
1452 message += ' ' + _('not encrypted')
1454 message += ' ' + _('encrypted')
1456 if wlanState[2]=='0':
1457 message = message + ', ' + _('no device active')
1458 elif wlanState[2]=='1':
1459 message = message + ', ' + _('one device active')
1461 message = message + ', ' + wlanState[2] + ' ' + _('devices active')
1462 self["FBFWlan"].setText(message)
1464 self["wlan_active"].hide()
1465 self["wlan_inactive"].show()
1466 self["FBFWlan"].setText('WLAN')
1468 self["FBFWlan"].setText('WLAN ' + _('Status not available'))
1469 self["wlan_active"].hide()
1470 self["wlan_inactive"].hide()
1472 if fritzbox.info[FBF_tamActive]:
1473 if not tamActive or tamActive[0] == 0:
1474 self._mailboxActive = False
1475 self["mailbox_active"].hide()
1476 self["mailbox_inactive"].show()
1477 self["FBFMailbox"].setText(_('No mailbox active'))
1479 self._mailboxActive = True
1483 message = message + str(i) + ','
1484 message = message[:-1] + ')'
1485 self["mailbox_inactive"].hide()
1486 self["mailbox_active"].show()
1487 if tamActive[0] == 1:
1488 self["FBFMailbox"].setText(_('One mailbox active') + ' ' + message)
1490 self["FBFMailbox"].setText(str(tamActive[0]) + ' ' + _('mailboxes active') + ' ' + message)
1492 if fritzbox.info[FBF_dectActive] and dectActive:
1493 self["dect_inactive"].hide()
1494 self["dect_active"].show()
1496 self["FBFDect"].setText(_('No DECT phone registered'))
1499 self["FBFDect"].setText(_('One DECT phone registered'))
1501 self["FBFDect"].setText(str(dectActive) + ' ' + _('DECT phones registered'))
1503 if fritzbox.info[FBF_faxActive] and faxActive:
1504 self["fax_inactive"].hide()
1505 self["fax_active"].show()
1506 self["FBFFax"].setText(_('Software fax active'))
1508 if fritzbox.info[FBF_rufumlActive] is not None and rufumlActive is not None:
1509 if rufumlActive == 0:
1510 self["rufuml_active"].hide()
1511 self["rufuml_inactive"].show()
1512 self["FBFRufuml"].setText(_('No call redirection active'))
1514 self["rufuml_inactive"].hide()
1515 self["rufuml_active"].show()
1516 if rufumlActive == 1:
1517 self["FBFRufuml"].setText(_('One call redirection active'))
1519 self["FBFRufuml"].setText(str(rufumlActive) + ' ' + _('call redirections active'))
1522 debug("[FritzCallFBF] _fillMenu: " + traceback.format_exc())
1524 def _toggleWlan(self):
1525 if self._wlanActive:
1526 debug("[FritzMenu] toggleWlan off")
1527 fritzbox.changeWLAN('0')
1529 debug("[FritzMenu] toggleWlan off")
1530 fritzbox.changeWLAN('1')
1532 def _toggleMailbox(self, which):
1533 debug("[FritzMenu] toggleMailbox")
1534 if fritzbox.info[FBF_tamActive]:
1535 debug("[FritzMenu] toggleMailbox off")
1536 fritzbox.changeMailbox(which)
1547 class FritzDisplayCalls(Screen, HelpableScreen):
1549 def __init__(self, session, text=""): #@UnusedVariable
1550 if config.plugins.FritzCall.fullscreen.value:
1551 self.width = DESKTOP_WIDTH
1552 self.height = DESKTOP_HEIGHT
1554 if os.path.exists(resolveFilename(SCOPE_SKIN_IMAGE, DESKTOP_SKIN + "/menu/back-main.png")):
1555 backMainPng = DESKTOP_SKIN + "/menu/back-main.png"
1556 elif os.path.exists(resolveFilename(SCOPE_SKIN_IMAGE, "Kerni-HD1/menu/back-main.png")):
1557 backMainPng = "Kerni-HD1/menu/back-main.png"
1558 elif os.path.exists(resolveFilename(SCOPE_SKIN_IMAGE, "Kerni-HD1-picon/menu/back-main.png")):
1559 backMainPng = "Kerni-HD1-picon/menu/back-main.png"
1561 backMainLine = """<ePixmap position="0,0" zPosition="-10" size="%d,%d" pixmap="%s" transparent="1" />""" % (self.width, self.height, backMainPng)
1564 debug("[FritzDisplayCalls] backMainLine: " + backMainLine)
1566 # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
1568 <screen name="FritzDisplayCalls" position="0,0" size="%d,%d" title="%s" flags="wfNoBorder">
1570 <widget source="global.CurrentTime" render="Label" position="%d,%d" size="%d,%d" font="Regular;%d" halign="right" backgroundColor="#0b67a2" transparent="1">
1571 <convert type="ClockToText">Default</convert>
1573 <widget source="global.CurrentTime" render="Label" position="%d,%d" size="%d,%d" font="Regular;%d" halign="right" backgroundColor="#0b67a2" transparent="1">
1574 <convert type="ClockToText">Date</convert>
1576 <eLabel text="%s" position="%d,%d" size="%d,%d" font="Regular;%d" halign="center" backgroundColor="#0b67a2" transparent="1"/>
1578 <widget name="statusbar" position="%d,%d" size="%d,%d" font="Regular;%d" backgroundColor="#353e575e" transparent="1" />
1579 <widget name="entries" position="%d,%d" size="%d,%d" scrollbarMode="showOnDemand" transparent="1" />
1581 <ePixmap pixmap="skin_default/buttons/red.png" position="%d,%d" size="%d,%d" alphatest="on" />
1582 <ePixmap pixmap="skin_default/buttons/green.png" position="%d,%d" size="%d,%d" alphatest="on" />
1583 <ePixmap pixmap="skin_default/buttons/yellow.png" position="%d,%d" size="%d,%d" alphatest="on" />
1584 <ePixmap pixmap="skin_default/buttons/blue.png" position="%d,%d" size="%d,%d" alphatest="on" />
1585 <widget name="key_red" position="%d,%d" size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
1586 <widget name="key_green" position="%d,%d" size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
1587 <widget name="key_yellow" position="%d,%d" size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
1588 <widget name="key_blue" position="%d,%d" size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
1589 <ePixmap position="%d,%d" size="%d,%d" zPosition="2" pixmap="%s" transparent="1" alphatest="blend" />
1591 self.width, self.height, _("Phone calls"),
1593 scaleH(1130, XXX), scaleV(40, XXX), scaleH(80, XXX), scaleV(26, XXX), scaleV(26, XXX), # time
1594 scaleH(890, XXX), scaleV(70, XXX), scaleH(320, XXX), scaleV(22, XXX), scaleV(20, XXX), # date
1595 "FritzCall " + _("Phone calls"), scaleH(500, XXX), scaleV(63, XXX), scaleH(330, XXX), scaleV(30, XXX), scaleV(27, XXX), # eLabel
1596 scaleH(80, XXX), scaleV(150, XXX), scaleH(280, XXX), scaleV(200, XXX), scaleV(22, XXX), # statusbar
1597 scaleH(420, XXX), scaleV(120, XXX), scaleH(790, XXX), scaleV(438, XXX), # entries
1598 scaleH(450, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # red
1599 scaleH(640, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # green
1600 scaleH(830, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # yellow
1601 scaleH(1020, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # blue
1602 scaleH(480, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # red
1603 scaleH(670, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # green
1604 scaleH(860, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # yellow
1605 scaleH(1050, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # blue
1606 scaleH(120, XXX), scaleV(430, XXX), scaleH(150, XXX), scaleV(110, XXX), resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/images/fritz.png") # Fritz Logo size and pixmap
1609 self.width = scaleH(1100, 570)
1610 debug("[FritzDisplayCalls] width: " + str(self.width))
1611 # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
1613 <screen name="FritzDisplayCalls" position="%d,%d" size="%d,%d" title="%s" >
1614 <eLabel position="0,0" size="%d,2" backgroundColor="#aaaaaa" />
1615 <widget name="statusbar" position="%d,%d" size="%d,%d" font="Regular;%d" backgroundColor="#aaaaaa" transparent="1" />
1616 <eLabel position="0,%d" size="%d,2" backgroundColor="#aaaaaa" />
1617 <widget name="entries" position="%d,%d" size="%d,%d" scrollbarMode="showOnDemand" />
1618 <eLabel position="0,%d" size="%d,2" backgroundColor="#aaaaaa" />
1619 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
1620 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
1621 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
1622 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
1623 <widget name="key_red" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;%d" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1624 <widget name="key_green" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;%d" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1625 <widget name="key_yellow" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;%d" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1626 <widget name="key_blue" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;%d" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1628 scaleH(90, 75), scaleV(100, 78), # position
1629 scaleH(1100, 570), scaleV(560, 430), # size
1631 scaleH(1100, 570), # eLabel width
1632 scaleH(40, 5), scaleV(10, 5), # statusbar position
1633 scaleH(1050, 560), scaleV(25, 22), # statusbar size
1634 scaleV(21, 21), # statusbar font size
1635 scaleV(40, 28), # eLabel position vertical
1636 scaleH(1100, 570), # eLabel width
1637 scaleH(40, 5), scaleV(55, 40), # entries position
1638 scaleH(1040, 560), scaleV(458, 340), # entries size
1639 scaleV(518, 390), # eLabel position vertical
1640 scaleH(1100, 570), # eLabel width
1641 scaleH(20, 5), scaleV(525, 395), # widget red
1642 scaleH(290, 145), scaleV(525, 395), # widget green
1643 scaleH(560, 285), scaleV(525, 395), # widget yellow
1644 scaleH(830, 425), scaleV(525, 395), # widget blue
1645 scaleH(20, 5), scaleV(525, 395), scaleV(22, 21), # widget red
1646 scaleH(290, 145), scaleV(525, 395), scaleV(22, 21), # widget green
1647 scaleH(560, 285), scaleV(525, 395), scaleV(22, 21), # widget yellow
1648 scaleH(830, 425), scaleV(525, 395), scaleV(22, 21), # widget blue
1651 Screen.__init__(self, session)
1652 HelpableScreen.__init__(self)
1654 # TRANSLATORS: keep it short, this is a button
1655 self["key_yellow"] = Button(_("All"))
1656 # TRANSLATORS: keep it short, this is a button
1657 self["key_red"] = Button(_("Missed"))
1658 # TRANSLATORS: keep it short, this is a button
1659 self["key_blue"] = Button(_("Incoming"))
1660 # TRANSLATORS: keep it short, this is a button
1661 self["key_green"] = Button(_("Outgoing"))
1663 self["setupActions"] = ActionMap(["OkCancelActions", "ColorActions"],
1665 "yellow": (lambda: self.display(FBF_ALL_CALLS)),
1666 "red": (lambda: self.display(FBF_MISSED_CALLS)),
1667 "blue": (lambda: self.display(FBF_IN_CALLS)),
1668 "green": (lambda: self.display(FBF_OUT_CALLS)),
1670 "ok": self.showEntry, }, - 2)
1672 # TRANSLATORS: keep it short, this is a help text
1673 self.helpList.append((self["setupActions"], "OkCancelActions", [("ok", _("Show details of entry"))]))
1674 # TRANSLATORS: keep it short, this is a help text
1675 self.helpList.append((self["setupActions"], "OkCancelActions", [("cancel", _("Quit"))]))
1676 # TRANSLATORS: keep it short, this is a help text
1677 self.helpList.append((self["setupActions"], "ColorActions", [("yellow", _("Display all calls"))]))
1678 # TRANSLATORS: keep it short, this is a help text
1679 self.helpList.append((self["setupActions"], "ColorActions", [("red", _("Display missed calls"))]))
1680 # TRANSLATORS: keep it short, this is a help text
1681 self.helpList.append((self["setupActions"], "ColorActions", [("blue", _("Display incoming calls"))]))
1682 # TRANSLATORS: keep it short, this is a help text
1683 self.helpList.append((self["setupActions"], "ColorActions", [("green", _("Display outgoing calls"))]))
1685 self["statusbar"] = Label(_("Getting calls from FRITZ!Box..."))
1686 self["entries"] = MenuList([], True, content=eListboxPythonMultiContent)
1687 fontSize = scaleV(22, 18)
1688 fontHeight = scaleV(24, 20)
1689 self["entries"].l.setFont(0, gFont("Regular", fontSize))
1690 self["entries"].l.setItemHeight(fontHeight)
1692 debug("[FritzDisplayCalls] init: '''%s'''" % config.plugins.FritzCall.fbfCalls.value)
1698 def display(self, which=config.plugins.FritzCall.fbfCalls.value):
1699 debug("[FritzDisplayCalls] display")
1700 config.plugins.FritzCall.fbfCalls.value = which
1701 config.plugins.FritzCall.fbfCalls.save()
1702 self.header = fbfCallsChoices[which]
1703 fritzbox.getCalls(self, self.gotCalls, which)
1705 def gotCalls(self, callList):
1706 debug("[FritzDisplayCalls] gotCalls")
1707 self.updateStatus(self.header + " (" + str(len(callList)) + ")")
1708 dateFieldWidth = scaleH(130,100)
1710 lengthFieldWidth = scaleH(45,45)
1711 scrollbarWidth = scaleH(35,35)
1712 if config.plugins.FritzCall.fullscreen.value:
1713 remoteFieldWidth = scaleH(140,100)
1714 fieldWidth = 790 -dateFieldWidth -5 -dirFieldWidth -5 -lengthFieldWidth -5 -remoteFieldWidth -scrollbarWidth -5
1715 fontSize = scaleV(24,20)
1717 remoteFieldWidth = scaleH(160,100)
1718 fieldWidth = self.width -scaleH(60, 5) -dateFieldWidth -5 -dirFieldWidth -5 -lengthFieldWidth -5 -remoteFieldWidth -scrollbarWidth -5
1719 fontSize = scaleV(24,20)
1721 for (number, date, remote, direct, here, length) in callList:
1722 found = re.match("(\d\d.\d\d.)\d\d( \d\d:\d\d)", date)
1723 if found: date = found.group(1) + found.group(2)
1724 if direct == FBF_OUT_CALLS:
1725 dir = LoadPixmap(resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/images/callout.png"))
1726 elif direct == FBF_IN_CALLS:
1727 dir = LoadPixmap(resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/images/callin.png"))
1729 dir = LoadPixmap(resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/images/callinfailed.png"))
1730 # debug("[FritzDisplayCalls] gotCalls: d: %d; f: %d; d: %d; r: %d" %(dateFieldWidth, fieldWidth, dirFieldWidth, remoteFieldWidth))
1731 sortlist.append([number,
1732 (eListboxPythonMultiContent.TYPE_TEXT, 0, 0, dateFieldWidth, fontSize, 0, RT_HALIGN_LEFT, date),
1733 (eListboxPythonMultiContent.TYPE_PIXMAP_ALPHATEST, dateFieldWidth+5, 0, dirFieldWidth, 16, dir),
1734 (eListboxPythonMultiContent.TYPE_TEXT, dateFieldWidth+5+dirFieldWidth+5, 0, fieldWidth, fontSize, 0, RT_HALIGN_LEFT, here),
1735 (eListboxPythonMultiContent.TYPE_TEXT, dateFieldWidth+5+dirFieldWidth+5+fieldWidth+5, 0, lengthFieldWidth, fontSize, 0, RT_HALIGN_LEFT, length),
1736 (eListboxPythonMultiContent.TYPE_TEXT, dateFieldWidth+5+dirFieldWidth+5+fieldWidth+5+lengthFieldWidth+5, 0, remoteFieldWidth, fontSize, 0, RT_HALIGN_RIGHT, remote)
1739 self["entries"].setList(sortlist)
1741 def updateStatus(self, text):
1742 self["statusbar"].setText(_("Getting calls from FRITZ!Box...") + ' ' + text)
1744 def showEntry(self):
1745 debug("[FritzDisplayCalls] showEntry")
1746 cur = self["entries"].getCurrent()
1749 debug("[FritzDisplayCalls] showEntry %s" % (cur[0]))
1751 fullname = phonebook.search(cur[0])
1753 # we have a name for this number
1755 self.session.open(FritzOfferAction, self, number, name)
1758 fullname = resolveNumberWithAvon(number, config.plugins.FritzCall.country.value)
1761 self.session.open(FritzOfferAction, self, number, name)
1763 self.session.open(FritzOfferAction, self, number)
1765 # we do not even have a number...
1766 self.session.open(MessageBox,
1768 type=MessageBox.TYPE_INFO)
1771 class FritzOfferAction(Screen):
1773 def __init__(self, session, parent, number, name=""):
1774 # the layout will completely be recalculated in finishLayout
1775 # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
1777 <screen name="FritzOfferAction" title="%s" >
1778 <widget name="text" size="%d,%d" font="Regular;%d" />
1779 <widget name="FacePixmap" size="%d,%d" alphatest="on" />
1780 <widget name="key_red_p" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
1781 <widget name="key_green_p" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
1782 <widget name="key_yellow_p" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
1783 <widget name="key_red" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1784 <widget name="key_green" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1785 <widget name="key_yellow" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
1788 DESKTOP_WIDTH, DESKTOP_HEIGHT, # set maximum size
1789 scaleH(22,21), # text
1790 DESKTOP_WIDTH, DESKTOP_HEIGHT, # set maximum size
1792 debug("[FritzOfferAction] init: %s, %s" %(number, name))
1793 Screen.__init__(self, session)
1795 # TRANSLATORS: keep it short, this is a button
1796 self["key_red"] = Button(_("Lookup"))
1797 # TRANSLATORS: keep it short, this is a button
1798 self["key_green"] = Button(_("Call"))
1799 # TRANSLATORS: keep it short, this is a button
1800 self["key_yellow"] = Button(_("Save"))
1801 # TRANSLATORS: keep it short, this is a button
1802 # self["key_blue"] = Button(_("Search"))
1804 self["FritzOfferActions"] = ActionMap(["OkCancelActions", "ColorActions"],
1809 "cancel": self.exit,
1810 "ok": self.exit, }, - 2)
1812 self.number = number
1813 self.name = name.replace("\n", ", ")
1814 self["text"] = Label(number + "\n\n" + name.replace(", ", "\n"))
1815 self.parent = parent
1816 self.lookupState = 0
1817 self["key_red_p"] = Pixmap()
1818 self["key_green_p"] = Pixmap()
1819 self["key_yellow_p"] = Pixmap()
1820 self["FacePixmap"] = Pixmap()
1821 self.onLayoutFinish.append(self.finishLayout)
1823 def finishLayout(self):
1824 debug("[FritzCall] FritzOfferAction/finishLayout number: %s/%s" % (self.number, self.name))
1826 picPixmap = LoadPixmap(findFace(self.number, self.name))
1827 if not picPixmap: # that means most probably, that the picture is not 8 bit...
1828 picPixmap = LoadPixmap(resolveFilename(SCOPE_SKIN_IMAGE, "skin_default/icons/input_question.png"))
1829 picSize = picPixmap.size()
1830 self["FacePixmap"].instance.setPixmap(picPixmap)
1833 # recalculate window size
1834 textSize = self["text"].getSize()
1835 textSize = (textSize[0]+20, textSize[1]+20) # don't know, why, but size is too small
1836 debug("[FritzCall] FritzOfferAction/finishLayout textsize: %s/%s" % textSize)
1837 textSize = eSize(*textSize)
1838 width = max(scaleH(620,545), noButtons*145, picSize.width() + textSize.width() + 30)
1839 height = max(picSize.height()+5, textSize.height()+5, scaleV(-1,136)) + 5 + 40 + 5
1840 buttonsGap = (width-noButtons*140)/(noButtons+1)
1841 buttonsVPos = height-40-5
1842 wSize = (width, height)
1843 wSize = eSize(*wSize)
1845 # center the smaller vertically
1846 hGap = (width-picSize.width()-textSize.width())/3
1847 picPos = (hGap,(height-50-picSize.height())/2+5)
1848 textPos = (hGap+picSize.width()+hGap, (height-50-textSize.height())/2+5)
1851 self.instance.resize(wSize)
1853 self["text"].instance.resize(textSize)
1855 self["FacePixmap"].instance.resize(picSize)
1857 buttonPos = (buttonsGap, buttonsVPos)
1858 self["key_red_p"].instance.move(ePoint(*buttonPos))
1859 self["key_red"].instance.move(ePoint(*buttonPos))
1860 buttonPos = (buttonsGap+140+buttonsGap, buttonsVPos)
1861 self["key_green_p"].instance.move(ePoint(*buttonPos))
1862 self["key_green"].instance.move(ePoint(*buttonPos))
1863 buttonPos = (buttonsGap+140+buttonsGap+140+buttonsGap, buttonsVPos)
1864 self["key_yellow_p"].instance.move(ePoint(*buttonPos))
1865 self["key_yellow"].instance.move(ePoint(*buttonPos))
1867 self["text"].instance.move(ePoint(*textPos))
1869 self["FacePixmap"].instance.move(ePoint(*picPos))
1871 self.instance.move(ePoint((DESKTOP_WIDTH-wSize.width())/2, (DESKTOP_HEIGHT-wSize.height())/2))
1873 def setTextAndResize(self, message):
1874 self["text"].instance.resize(eSize(*(DESKTOP_WIDTH,DESKTOP_HEIGHT)))
1875 self["text"].setText(self.number + "\n\n" + message)
1879 phonebookLocation = config.plugins.FritzCall.phonebookLocation.value
1880 if self.lookupState == 0:
1881 self.lookupState = 1
1882 self.setTextAndResize(_("Reverse searching..."))
1883 ReverseLookupAndNotifier(self.number, self.lookedUp, "UTF-8", config.plugins.FritzCall.country.value)
1885 if self.lookupState == 1 and os.path.exists(os.path.join(phonebookLocation, "PhoneBook.csv")):
1886 self.setTextAndResize(_("Searching in Outlook export..."))
1887 self.lookupState = 2
1888 self.lookedUp(self.number, FritzOutlookCSV.findNumber(self.number, os.path.join(phonebookLocation, "PhoneBook.csv"))) #@UndefinedVariable
1891 self.lookupState = 2
1892 if self.lookupState == 2 and os.path.exists(os.path.join(phonebookLocation, "PhoneBook.ldif")):
1893 self.setTextAndResize(_("Searching in LDIF..."))
1894 self.lookupState = 0
1895 FritzLDIF.findNumber(self.number, open(os.path.join(phonebookLocation, "PhoneBook.ldif")), self.lookedUp)
1898 self.lookupState = 0
1901 def lookedUp(self, number, name):
1902 name = handleReverseLookupResult(name)
1904 if self.lookupState == 1:
1905 name = _("No result from reverse lookup")
1906 elif self.lookupState == 2:
1907 name = _("No result from Outlook export")
1909 name = _("No result from LDIF")
1911 self.number = number
1912 debug("[FritzOfferAction] lookedUp: " + str(name.replace(", ", "\n")))
1913 self.setTextAndResize(str(name.replace(", ", "\n")))
1916 debug("[FritzOfferAction] add: %s" %self.number)
1917 fritzbox.dial(self.number)
1921 debug("[FritzOfferAction] add: %s, %s" %(self.number, self.name))
1922 phonebook.FritzDisplayPhonebook(self.session).add(self.parent, self.number, self.name)
1929 class FritzCallPhonebook:
1931 debug("[FritzCallPhonebook] init")
1932 # Beware: strings in phonebook.phonebook have to be in utf-8!
1937 debug("[FritzCallPhonebook] reload")
1938 # Beware: strings in phonebook.phonebook have to be in utf-8!
1941 if not config.plugins.FritzCall.enable.value:
1944 phonebookFilename = os.path.join(config.plugins.FritzCall.phonebookLocation.value, "PhoneBook.txt")
1945 if config.plugins.FritzCall.phonebook.value and os.path.exists(phonebookFilename):
1946 debug("[FritzCallPhonebook] reload: read " + phonebookFilename)
1947 phonebookTxtCorrupt = False
1948 for line in open(phonebookFilename):
1950 # Beware: strings in phonebook.phonebook have to be in utf-8!
1951 line = line.decode("utf-8")
1952 except UnicodeDecodeError: # this is just for the case, somebody wrote latin1 chars into PhoneBook.txt
1954 line = line.decode("iso-8859-1")
1955 debug("[FritzCallPhonebook] Fallback to ISO-8859-1 in %s" % line)
1956 phonebookTxtCorrupt = True
1958 debug("[FritzCallPhonebook] Could not parse internal Phonebook Entry %s" % line)
1959 phonebookTxtCorrupt = True
1960 line = line.encode("utf-8")
1961 if re.match("^\d+#.*$", line):
1963 number, name = line.split("#")
1964 if not self.phonebook.has_key(number):
1965 # Beware: strings in phonebook.phonebook have to be in utf-8!
1966 self.phonebook[number] = name
1967 except ValueError: # how could this possibly happen?!?!
1968 debug("[FritzCallPhonebook] Could not parse internal Phonebook Entry %s" % line)
1969 phonebookTxtCorrupt = True
1971 debug("[FritzCallPhonebook] Could not parse internal Phonebook Entry %s" % line)
1972 phonebookTxtCorrupt = True
1974 if phonebookTxtCorrupt:
1975 # dump phonebook to PhoneBook.txt
1976 debug("[FritzCallPhonebook] dump Phonebook.txt")
1977 os.rename(phonebookFilename, phonebookFilename + ".bck")
1978 fNew = open(phonebookFilename, 'w')
1979 # Beware: strings in phonebook.phonebook are utf-8!
1980 for (number, name) in self.phonebook.iteritems():
1981 # Beware: strings in PhoneBook.txt have to be in utf-8!
1982 fNew.write(number + "#" + name.encode("utf-8"))
1985 #===============================================================================
1987 # # read entries from Outlook export
1989 # # not reliable with coding yet
1991 # # import csv exported from Outlook 2007 with csv(Windows)
1992 # csvFilename = "/tmp/PhoneBook.csv"
1993 # if config.plugins.FritzCall.phonebook.value and os.path.exists(csvFilename):
1995 # readOutlookCSV(csvFilename, self.add)
1996 # os.rename(csvFilename, csvFilename + ".done")
1997 # except ImportError:
1998 # debug("[FritzCallPhonebook] CSV import failed" %line)
1999 #===============================================================================
2002 #===============================================================================
2004 # # read entries from LDIF
2006 # # import ldif exported from Thunderbird 2.0.0.19
2007 # ldifFilename = "/tmp/PhoneBook.ldif"
2008 # if config.plugins.FritzCall.phonebook.value and os.path.exists(ldifFilename):
2010 # parser = MyLDIF(open(ldifFilename), self.add)
2012 # os.rename(ldifFilename, ldifFilename + ".done")
2013 # except ImportError:
2014 # debug("[FritzCallPhonebook] LDIF import failed" %line)
2015 #===============================================================================
2017 if config.plugins.FritzCall.fritzphonebook.value:
2018 fritzbox.loadFritzBoxPhonebook()
2020 if DESKTOP_WIDTH <> 1280 or DESKTOP_HEIGHT <> 720:
2021 config.plugins.FritzCall.fullscreen.value = False
2023 def search(self, number):
2024 # debug("[FritzCallPhonebook] Searching for %s" %number)
2026 if not self.phonebook:
2029 if config.plugins.FritzCall.prefix.value:
2030 prefix = config.plugins.FritzCall.prefix.value
2031 if number[0] != '0':
2032 number = prefix + number
2033 # debug("[FritzCallPhonebook] search: added prefix: %s" %number)
2034 elif number[:len(prefix)] == prefix and self.phonebook.has_key(number[len(prefix):]):
2035 # debug("[FritzCallPhonebook] search: same prefix")
2036 name = self.phonebook[number[len(prefix):]]
2037 # debug("[FritzCallPhonebook] search: result: %s" %name)
2041 if not name and self.phonebook.has_key(number):
2042 name = self.phonebook[number]
2044 return name.replace(", ", "\n").strip()
2046 def add(self, number, name):
2049 @param number: number of entry
2050 @param name: name of entry, has to be in utf-8
2052 debug("[FritzCallPhonebook] add")
2053 name = name.replace("\n", ", ").replace('#','') # this is just for safety reasons. add should only be called with newlines converted into commas
2055 self.phonebook[number] = name;
2056 if number and number <> 0:
2057 if config.plugins.FritzCall.phonebook.value:
2059 name = name.strip() + "\n"
2060 string = "%s#%s" % (number, name)
2061 # Beware: strings in PhoneBook.txt have to be in utf-8!
2062 f = open(os.path.join(config.plugins.FritzCall.phonebookLocation.value, "PhoneBook.txt"), 'a')
2065 debug("[FritzCallPhonebook] added %s with %s to Phonebook.txt" % (number, name.strip()))
2071 def remove(self, number):
2072 if number in self.phonebook:
2073 debug("[FritzCallPhonebook] remove entry in phonebook")
2074 del self.phonebook[number]
2075 if config.plugins.FritzCall.phonebook.value:
2077 phonebookFilename = os.path.join(config.plugins.FritzCall.phonebookLocation.value, "PhoneBook.txt")
2078 debug("[FritzCallPhonebook] remove entry in Phonebook.txt")
2079 fOld = open(phonebookFilename, 'r')
2080 fNew = open(phonebookFilename + str(os.getpid()), 'w')
2081 line = fOld.readline()
2083 if not re.match("^" + number + "#.*$", line):
2085 line = fOld.readline()
2088 os.remove(phonebookFilename)
2089 os.rename(phonebookFilename + str(os.getpid()), phonebookFilename)
2090 debug("[FritzCallPhonebook] removed %s from Phonebook.txt" % number)
2097 class FritzDisplayPhonebook(Screen, HelpableScreen, NumericalTextInput):
2099 def __init__(self, session):
2100 if config.plugins.FritzCall.fullscreen.value:
2101 self.width = DESKTOP_WIDTH
2102 self.height = DESKTOP_HEIGHT
2103 self.entriesWidth = 790
2105 if os.path.exists(resolveFilename(SCOPE_SKIN_IMAGE, DESKTOP_SKIN + "/menu/back-main.png")):
2106 backMainPng = DESKTOP_SKIN + "/menu/back-main.png"
2107 elif os.path.exists(resolveFilename(SCOPE_SKIN_IMAGE, "Kerni-HD1/menu/back-main.png")):
2108 backMainPng = "Kerni-HD1/menu/back-main.png"
2109 elif os.path.exists(resolveFilename(SCOPE_SKIN_IMAGE, "Kerni-HD1-picon/menu/back-main.png")):
2110 backMainPng = "Kerni-HD1-picon/menu/back-main.png"
2112 backMainLine = """<ePixmap position="0,0" zPosition="-10" size="%d,%d" pixmap="%s" transparent="1" />""" % (self.width, self.height, backMainPng)
2115 debug("[FritzDisplayPhonebook] backMainLine: " + backMainLine)
2117 # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
2119 <screen name="FritzdisplayPhonebook" position="0,0" size="%d,%d" title="%s" flags="wfNoBorder">
2121 <widget source="global.CurrentTime" render="Label" position="%d,%d" size="%d,%d" font="Regular;%d" halign="right" backgroundColor="#0b67a2" transparent="1">
2122 <convert type="ClockToText">Default</convert>
2124 <widget source="global.CurrentTime" render="Label" position="%d,%d" size="%d,%d" font="Regular;%d" halign="right" backgroundColor="#0b67a2" transparent="1">
2125 <convert type="ClockToText">Date</convert>
2127 <eLabel text="%s" position="%d,%d" size="%d,%d" font="Regular;%d" halign="center" backgroundColor="#0b67a2" transparent="1"/>
2129 <widget name="entries" position="%d,%d" size="%d,%d" scrollbarMode="showOnDemand" transparent="1" />
2131 <ePixmap pixmap="skin_default/buttons/red.png" position="%d,%d" size="%d,%d" alphatest="on" />
2132 <ePixmap pixmap="skin_default/buttons/green.png" position="%d,%d" size="%d,%d" alphatest="on" />
2133 <ePixmap pixmap="skin_default/buttons/yellow.png" position="%d,%d" size="%d,%d" alphatest="on" />
2134 <ePixmap pixmap="skin_default/buttons/blue.png" position="%d,%d" size="%d,%d" alphatest="on" />
2135 <widget name="key_red" position="%d,%d" size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
2136 <widget name="key_green" position="%d,%d" size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
2137 <widget name="key_yellow" position="%d,%d" size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
2138 <widget name="key_blue" position="%d,%d" size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
2139 <ePixmap position="%d,%d" size="%d,%d" zPosition="2" pixmap="%s" transparent="1" alphatest="blend" />
2141 self.width, self.height, _("Phonebook"),
2143 scaleH(1130, XXX), scaleV(40, XXX), scaleH(80, XXX), scaleV(26, XXX), scaleV(26, XXX), # time
2144 scaleH(890, XXX), scaleV(70, XXX), scaleH(320, XXX), scaleV(22, XXX), scaleV(20, XXX), # date
2145 "FritzCall " + _("Phonebook"), scaleH(80, XXX), scaleV(63, XXX), scaleH(300, XXX), scaleV(30, XXX), scaleV(27, XXX), # eLabel
2146 scaleH(420, XXX), scaleV(120, XXX), scaleH(self.entriesWidth, XXX), scaleV(438, XXX), # entries
2147 scaleH(450, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # red
2148 scaleH(640, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # green
2149 scaleH(830, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # yellow
2150 scaleH(1020, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # blue
2151 scaleH(480, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # red
2152 scaleH(670, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # green
2153 scaleH(860, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # yellow
2154 scaleH(1050, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # blue
2155 scaleH(120, XXX), scaleV(430, XXX), scaleH(150, XXX), scaleV(110, XXX), resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/images/fritz.png") # Fritz Logo size and pixmap
2158 self.width = scaleH(1100, 570)
2159 self.entriesWidth = scaleH(1040, 560)
2160 debug("[FritzDisplayPhonebook] width: " + str(self.width))
2161 # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
2163 <screen name="FritzDisplayPhonebook" position="%d,%d" size="%d,%d" title="%s" >
2164 <eLabel position="0,0" size="%d,2" backgroundColor="#aaaaaa" />
2165 <widget name="entries" position="%d,%d" size="%d,%d" scrollbarMode="showOnDemand" />
2166 <eLabel position="0,%d" size="%d,2" backgroundColor="#aaaaaa" />
2167 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
2168 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
2169 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
2170 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
2171 <widget name="key_red" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;%d" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
2172 <widget name="key_green" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;%d" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
2173 <widget name="key_yellow" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;%d" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
2174 <widget name="key_blue" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;%d" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
2176 scaleH(90, 75), scaleV(100, 73), # position
2177 scaleH(1100, 570), scaleV(560, 430), # size
2179 scaleH(1100, 570), # eLabel width
2180 scaleH(40, 5), scaleV(20, 5), # entries position
2181 self.entriesWidth, scaleV(488, 380), # entries size
2182 scaleV(518, 390), # eLabel position vertical
2183 scaleH(1100, 570), # eLabel width
2184 scaleH(20, 5), scaleV(525, 395), # ePixmap red
2185 scaleH(290, 145), scaleV(525, 395), # ePixmap green
2186 scaleH(560, 285), scaleV(525, 395), # ePixmap yellow
2187 scaleH(830, 425), scaleV(525, 395), # ePixmap blue
2188 scaleH(20, 5), scaleV(525, 395), scaleV(22, 21), # widget red
2189 scaleH(290, 145), scaleV(525, 395), scaleV(22, 21), # widget green
2190 scaleH(560, 285), scaleV(525, 395), scaleV(22, 21), # widget yellow
2191 scaleH(830, 425), scaleV(525, 395), scaleV(22, 21), # widget blue
2194 Screen.__init__(self, session)
2195 NumericalTextInput.__init__(self)
2196 HelpableScreen.__init__(self)
2198 # TRANSLATORS: keep it short, this is a button
2199 self["key_red"] = Button(_("Delete"))
2200 # TRANSLATORS: keep it short, this is a button
2201 self["key_green"] = Button(_("New"))
2202 # TRANSLATORS: keep it short, this is a button
2203 self["key_yellow"] = Button(_("Edit"))
2204 # TRANSLATORS: keep it short, this is a button
2205 self["key_blue"] = Button(_("Search"))
2207 self["setupActions"] = ActionMap(["OkCancelActions", "ColorActions"],
2211 "yellow": self.edit,
2212 "blue": self.search,
2213 "cancel": self.exit,
2214 "ok": self.showEntry, }, - 2)
2216 # TRANSLATORS: keep it short, this is a help text
2217 self.helpList.append((self["setupActions"], "OkCancelActions", [("ok", _("Show details of entry"))]))
2218 # TRANSLATORS: keep it short, this is a help text
2219 self.helpList.append((self["setupActions"], "OkCancelActions", [("cancel", _("Quit"))]))
2220 # TRANSLATORS: keep it short, this is a help text
2221 self.helpList.append((self["setupActions"], "ColorActions", [("red", _("Delete entry"))]))
2222 # TRANSLATORS: keep it short, this is a help text
2223 self.helpList.append((self["setupActions"], "ColorActions", [("green", _("Add entry to phonebook"))]))
2224 # TRANSLATORS: keep it short, this is a help text
2225 self.helpList.append((self["setupActions"], "ColorActions", [("yellow", _("Edit selected entry"))]))
2226 # TRANSLATORS: keep it short, this is a help text
2227 self.helpList.append((self["setupActions"], "ColorActions", [("blue", _("Search (case insensitive)"))]))
2229 self["entries"] = MenuList([], True, content=eListboxPythonMultiContent)
2230 fontSize = scaleV(22, 18)
2231 fontHeight = scaleV(24, 20)
2232 self["entries"].l.setFont(0, gFont("Regular", fontSize))
2233 self["entries"].l.setItemHeight(fontHeight)
2234 debug("[FritzCallPhonebook] displayPhonebook init")
2237 def display(self, filter=""):
2238 debug("[FritzCallPhonebook] displayPhonebook/display")
2240 # Beware: strings in phonebook.phonebook are utf-8!
2241 sortlistHelp = sorted((name.lower(), name, number) for (number, name) in phonebook.phonebook.iteritems())
2242 for (low, name, number) in sortlistHelp:
2243 if number == "01234567890":
2246 low = low.decode("utf-8")
2247 except (UnicodeDecodeError, UnicodeEncodeError): # this should definitely not happen
2249 low = low.decode("iso-8859-1")
2251 debug("[FritzCallPhonebook] displayPhonebook/display: corrupt phonebook entry for %s" % number)
2252 # self.session.open(MessageBox, _("Corrupt phonebook entry\nfor number %s\nDeleting.") %number, type = MessageBox.TYPE_ERROR)
2253 phonebook.remove(number)
2257 filter = filter.lower()
2258 if low.find(filter) == - 1:
2260 name = name.strip().decode("utf-8")
2261 number = number.strip().decode("utf-8")
2262 found = re.match("([^,]*),.*", name) # strip address information from the name part
2264 shortname = found.group(1)
2267 numberFieldWidth = scaleV(200,150)
2268 fieldWidth = self.entriesWidth -5 -numberFieldWidth -10 -scaleH(90,45)
2269 number = number.encode("utf-8", "replace")
2270 name = name.encode("utf-8", "replace")
2271 shortname = shortname.encode('utf-8', 'replace')
2272 self.sortlist.append([(number, name),
2273 (eListboxPythonMultiContent.TYPE_TEXT, 0, 0, fieldWidth, scaleH(24,20), 0, RT_HALIGN_LEFT, shortname),
2274 (eListboxPythonMultiContent.TYPE_TEXT, fieldWidth +5, 0, numberFieldWidth, scaleH(24,20), 0, RT_HALIGN_LEFT, number)
2277 self["entries"].setList(self.sortlist)
2279 def showEntry(self):
2280 cur = self["entries"].getCurrent()
2282 debug("[FritzCallPhonebook] displayPhonebook/showEntry (%s,%s)" % (cur[0][0], cur[0][1]))
2285 self.session.open(FritzOfferAction, self, number, name)
2288 cur = self["entries"].getCurrent()
2290 debug("[FritzCallPhonebook] displayPhonebook/delete " + cur[0][0])
2291 self.session.openWithCallback(
2292 self.deleteConfirmed,
2294 _("Do you really want to delete entry for\n\n%(number)s\n\n%(name)s?")
2295 % { 'number':str(cur[0][0]), 'name':str(cur[0][1]).replace(", ", "\n") }
2298 self.session.open(MessageBox, _("No entry selected"), MessageBox.TYPE_INFO)
2300 def deleteConfirmed(self, ret):
2301 debug("[FritzCallPhonebook] displayPhonebook/deleteConfirmed")
2303 # if ret: delete number from sortlist, delete number from phonebook.phonebook and write it to disk
2305 cur = self["entries"].getCurrent()
2308 # delete number from sortlist, delete number from phonebook.phonebook and write it to disk
2309 debug("[FritzCallPhonebook] displayPhonebook/deleteConfirmed: remove " + cur[0][0])
2310 phonebook.remove(cur[0][0])
2313 # self.session.open(MessageBox, _("Not deleted."), MessageBox.TYPE_INFO)
2315 self.session.open(MessageBox, _("No entry selected"), MessageBox.TYPE_INFO)
2317 def add(self, parent=None, number="", name=""):
2318 class addScreen(Screen, ConfigListScreen):
2319 '''ConfiglistScreen with two ConfigTexts for Name and Number'''
2321 def __init__(self, session, parent, number="", name=""):
2323 # setup screen with two ConfigText and OK and ABORT button
2326 width = max(scaleH(-1,570), noButtons*140)
2327 height = scaleV(-1,100) # = 5 + 126 + 40 + 5; 6 lines of text possible
2328 buttonsGap = (width-noButtons*140)/(noButtons+1)
2329 buttonsVPos = height-40-5
2330 # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
2332 <screen position="%d,%d" size="%d,%d" title="%s" >
2333 <widget name="config" position="5,5" size="%d,%d" scrollbarMode="showOnDemand" />
2334 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
2335 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
2336 <widget name="key_red" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
2337 <widget name="key_green" position="%d,%d" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
2339 (DESKTOP_WIDTH - width) / 2,
2340 (DESKTOP_HEIGHT - height) / 2,
2343 _("Add entry to phonebook"),
2345 height - 5 - 40 - 5,
2346 buttonsGap, buttonsVPos,
2347 buttonsGap+140+buttonsGap, buttonsVPos,
2348 buttonsGap, buttonsVPos,
2349 buttonsGap+140+buttonsGap, buttonsVPos,
2351 Screen.__init__(self, session)
2352 self.session = session
2353 self.parent = parent
2354 # TRANSLATORS: keep it short, this is a button
2355 self["key_red"] = Button(_("Cancel"))
2356 # TRANSLATORS: keep it short, this is a button
2357 self["key_green"] = Button(_("OK"))
2358 self["setupActions"] = ActionMap(["SetupActions", "ColorActions"],
2360 "cancel": self.cancel,