NEW: Support for upcoming FBF firmwares
[enigma2-plugins.git] / fritzcall / src / plugin.py
1 # -*- coding: utf-8 -*-
2 #===============================================================================
3 # $Author$
4 # $Revision$
5 # $Date$
6 # $Id$
7 #==============================
8 from Screens.Screen import Screen #@UnresolvedImport
9 from Screens.MessageBox import MessageBox #@UnresolvedImport
10 from Screens.NumericalTextInputHelpDialog import NumericalTextInputHelpDialog #@UnresolvedImport
11 from Screens.InputBox import InputBox #@UnresolvedImport
12 from Screens import Standby #@UnresolvedImport
13 from Screens.HelpMenu import HelpableScreen #@UnresolvedImport
14
15 from enigma import eTimer, eListboxPythonMultiContent, gFont, RT_HALIGN_LEFT, RT_HALIGN_RIGHT #@UnresolvedImport
16
17 from Components.MenuList import MenuList #@UnresolvedImport
18 from Components.ActionMap import ActionMap #@UnresolvedImport
19 from Components.Label import Label #@UnresolvedImport
20 from Components.Button import Button #@UnresolvedImport
21 from Components.Pixmap import Pixmap #@UnresolvedImport
22 from Components.config import config, ConfigSubsection, ConfigSelection, ConfigEnableDisable, getConfigListEntry, ConfigText, ConfigInteger #@UnresolvedImport
23 try:
24         from Components.config import ConfigPassword
25 except ImportError:
26         ConfigPassword = ConfigText
27 from Components.ConfigList import ConfigListScreen #@UnresolvedImport
28
29 from Plugins.Plugin import PluginDescriptor #@UnresolvedImport
30 from Tools import Notifications #@UnresolvedImport
31 from Tools.NumericalTextInput import NumericalTextInput #@UnresolvedImport
32 from Tools.Directories import resolveFilename, SCOPE_PLUGINS, SCOPE_SKIN_IMAGE #@UnresolvedImport
33 from Tools.LoadPixmap import LoadPixmap #@UnresolvedImport
34
35 from twisted.internet import reactor #@UnresolvedImport
36 from twisted.internet.protocol import ReconnectingClientFactory #@UnresolvedImport
37 from twisted.protocols.basic import LineReceiver #@UnresolvedImport
38 from twisted.web.client import getPage #@UnresolvedImport
39
40 from urllib import urlencode 
41 import re, time, os, hashlib, traceback
42
43 from nrzuname import ReverseLookupAndNotifier, html2unicode
44 import FritzOutlookCSV, FritzLDIF
45 from . import _, debug #@UnresolvedImport
46
47 from enigma import getDesktop #@UnresolvedImport
48 DESKTOP_WIDTH = getDesktop(0).size().width()
49 DESKTOP_HEIGHT = getDesktop(0).size().height()
50 DESKTOP_SKIN = config.skin.primary_skin.value.replace("/skin.xml", "")
51 XXX = 0 # TODO: Platzhalter für fullscreen SD skin
52 #
53 # this is pure magic.
54 # It returns the first value, if HD (1280x720),
55 # the second if SD (720x576),
56 # else something scaled accordingly
57 # if one of the parameters is -1, scale proportionally
58 #
59 def scaleH(y2, y1):
60         if y2 == -1:
61                 y2 = y1/720*1280
62         elif y1 == -1:
63                 y1 = y2/1280*720
64         return scale(y2, y1, 1280, 720, DESKTOP_WIDTH)
65 def scaleV(y2, y1):
66         if y2 == -1:
67                 y2 = y1/576*720
68         elif y1 == -1:
69                 y1 = y2/720*576
70         return scale(y2, y1, 720, 576, DESKTOP_HEIGHT)
71 def scale(y2, y1, x2, x1, x):
72         return (y2 - y1) * (x - x1) / (x2 - x1) + y1
73
74 my_global_session = None
75
76 config.plugins.FritzCall = ConfigSubsection()
77 config.plugins.FritzCall.enable = ConfigEnableDisable(default=False)
78 config.plugins.FritzCall.muteOnCall = ConfigEnableDisable(default=False)
79 config.plugins.FritzCall.hostname = ConfigText(default="fritz.box", fixed_size=False)
80 config.plugins.FritzCall.afterStandby = ConfigSelection(choices=[("none", _("show nothing")), ("inList", _("show as list")), ("each", _("show each call"))])
81 config.plugins.FritzCall.filter = ConfigEnableDisable(default=False)
82 config.plugins.FritzCall.filtermsn = ConfigText(default="", fixed_size=False)
83 config.plugins.FritzCall.filtermsn.setUseableChars('0123456789,')
84 config.plugins.FritzCall.showOutgoing = ConfigEnableDisable(default=False)
85 config.plugins.FritzCall.timeout = ConfigInteger(default=15, limits=(0, 60))
86 config.plugins.FritzCall.lookup = ConfigEnableDisable(default=False)
87 config.plugins.FritzCall.internal = ConfigEnableDisable(default=False)
88 config.plugins.FritzCall.fritzphonebook = ConfigEnableDisable(default=False)
89 config.plugins.FritzCall.phonebook = ConfigEnableDisable(default=False)
90 config.plugins.FritzCall.addcallers = ConfigEnableDisable(default=False)
91 config.plugins.FritzCall.phonebookLocation = ConfigSelection(choices=[("/etc/enigma2", _("Flash")), ("/media/usb", _("USB Stick")), ("/media/cf", _("CF Drive")), ("/media/hdd", _("Harddisk"))])
92 config.plugins.FritzCall.password = ConfigPassword(default="", fixed_size=False)
93 config.plugins.FritzCall.extension = ConfigText(default='1', fixed_size=False)
94 config.plugins.FritzCall.extension.setUseableChars('0123456789')
95 config.plugins.FritzCall.showType = ConfigEnableDisable(default=True)
96 config.plugins.FritzCall.showShortcut = ConfigEnableDisable(default=False)
97 config.plugins.FritzCall.showVanity = ConfigEnableDisable(default=False)
98 config.plugins.FritzCall.prefix = ConfigText(default="", fixed_size=False)
99 config.plugins.FritzCall.prefix.setUseableChars('0123456789')
100 config.plugins.FritzCall.fullscreen = ConfigEnableDisable(default=False)
101 config.plugins.FritzCall.debug = ConfigEnableDisable(default=False)
102
103 countryCodes = [
104         ("0049", _("Germany")),
105         ("0031", _("The Netherlands")),
106         ("0033", _("France")),
107         ("0039", _("Italy")),
108         ("0041", _("Switzerland")),
109         ("0043", _("Austria"))
110         ]
111 config.plugins.FritzCall.country = ConfigSelection(choices=countryCodes)
112
113 FBF_ALL_CALLS = "."
114 FBF_IN_CALLS = "1"
115 FBF_MISSED_CALLS = "2"
116 FBF_OUT_CALLS = "3"
117 fbfCallsChoices = {FBF_ALL_CALLS: _("All calls"),
118                                    FBF_IN_CALLS: _("Incoming calls"),
119                                    FBF_MISSED_CALLS: _("Missed calls"),
120                                    FBF_OUT_CALLS: _("Outgoing calls")
121                                    }
122 config.plugins.FritzCall.fbfCalls = ConfigSelection(choices=fbfCallsChoices)
123
124 config.plugins.FritzCall.name = ConfigText(default="", fixed_size=False)
125 config.plugins.FritzCall.number = ConfigText(default="", fixed_size=False)
126 config.plugins.FritzCall.number.setUseableChars('0123456789')
127
128 def initDebug():
129         try:
130                 os.remove("/tmp/FritzDebug.log")
131         except:
132                 pass
133
134 class FritzAbout(Screen):
135
136         def __init__(self, session):
137                 textFieldWidth = scaleV(350,250)
138                 width = 5 + 150 + 20 + textFieldWidth + 5 + 175 + 5
139                 height = 5 + 175 + 5 + 25 + 5
140                 # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
141                 self.skin = """
142                         <screen name="FritzAbout" position="%d,%d" size="%d,%d" title="%s" >
143                                 <widget name="text" position="175,%d" size="%d,%d" font="Regular;%d" />
144                                 <ePixmap position="5,37" size="150,110" pixmap="%s" transparent="1" alphatest="blend" />
145                                 <ePixmap position="%d,5" size="175,175" pixmap="%s" transparent="1" alphatest="blend" />
146                                 <widget name="url" position="20,185" size="%d,25" font="Regular;%d" />
147                         </screen>""" % (
148                                                         (DESKTOP_WIDTH - width) / 2, (DESKTOP_HEIGHT - height) / 2, # position
149                                                         width, height, # size
150                                                         _("About FritzCall"), # title
151                                                         (height-scaleV(150,130)) / 2, # text vertical position
152                                                         textFieldWidth,
153                                                         scaleV(150,130), # text height
154                                                         scaleV(24,21), # text font size
155                                                         resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/images/fritz.png"), # 150x110
156                                                         5 + 150 + 5 + textFieldWidth + 5, # qr code horizontal offset
157                                                         resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/images/website.png"), # 175x175
158                                                         width-40, # url width
159                                                         scaleV(24,21) # url font size
160                                                         )
161                 Screen.__init__(self, session)
162                 self["aboutActions"] = ActionMap(["OkCancelActions"],
163                 {
164                 "cancel": self.exit,
165                 "ok": self.exit,
166                 }, -2)
167                 self["text"] = Label(
168                                                         "FritzCall Plugin" + "\n\n" +
169                                                         "$Author$"[1:-2] + "\n" +
170                                                         "$Revision$"[1:-2] + "\n" + 
171                                                         "$Date$"[1:23] + "\n"
172                                                         )
173                 self["url"] = Label("http://wiki.blue-panel.com/index.php/FritzCall")
174                 
175         def exit(self):
176                 self.close()
177
178 class FritzCallFBF:
179         def __init__(self, init=True):
180                 debug("[FritzCallFBF] __init__")
181                 self._callScreen = None
182                 self._md5LoginTimestamp = None
183                 self._md5Sid = '0000000000000000'
184                 self._callCallback = None
185                 self._loginCallback = None
186                 self._timestampCalls = 0
187                 self._callList = []
188                 self._callType = config.plugins.FritzCall.fbfCalls.value
189                 self.hasMailbox = None # Holds (no of mailboxes, state0, state1, state2, state3, state4)
190                 self.hasDect = False
191                 if init:
192                         self.getInfo(None, self._setProperties)
193
194         def _setProperties(self, status):
195                 (boxInfo, upTime, ipAddress, wlanState, wlanEncrypt, dslState, dslSpeed, tamActive, dectActive) = status #@UnusedVariable
196                 self.boxInfo = boxInfo
197                 self.hasMailbox = tamActive
198                 self.hasDect = dectActive != None
199                 debug("[FritzCallFBF] _setProperties: " + str(boxInfo))
200
201         def _notify(self, text):
202                 debug("[FritzCallFBF] notify: " + text)
203                 self._md5LoginTimestamp = None
204                 if self._callScreen:
205                         debug("[FritzCallFBF] notify: try to close callScreen")
206                         self._callScreen.close()
207                         self._callScreen = None
208                 Notifications.AddNotification(MessageBox, text, type=MessageBox.TYPE_ERROR, timeout=config.plugins.FritzCall.timeout.value)
209                         
210         def _login(self, callback=None):
211                 debug("[FritzCallFBF] _login")
212                 if self._callScreen:
213                         self._callScreen.updateStatus(_("login"))
214                 if self._md5LoginTimestamp and ((time.time() - self._md5LoginTimestamp) < float(9.5*60)): # new login after 9.5 minutes inactivity 
215                         debug("[FritzCallFBF] _login: renew timestamp: " + str(self._md5LoginTimestamp) + " time: " + str(time.time()))
216                         self._md5LoginTimestamp = time.time()
217                         callback()
218                 else:
219                         debug("[FritzCallFBF] _login: not logged in or outdated login, renew: timestamp: " + str(self._md5LoginTimestamp) + " time: " + str(time.time()))
220                         self._loginCallback = callback
221                         # http://fritz.box/cgi-bin/webcm?getpage=../html/login_sid.xml
222                         parms = urlencode({'getpage':'../html/login_sid.xml'})
223                         url = "http://%s/cgi-bin/webcm" % (config.plugins.FritzCall.hostname.value)
224                         debug("[FritzCallFBF] _login: '" + url + "' parms: '" + parms + "'")
225                         getPage(url,
226                                 method="POST",
227                                 headers={'Content-Type': "application/x-www-form-urlencoded", 'Content-Length': str(len(parms))
228                                                 }, postdata=parms).addCallback(self._md5Login).addErrback(self._oldLogin)
229
230         def _oldLogin(self, error): 
231                 debug("[FritzCallFBF] _oldLogin: " + repr(error))
232                 if config.plugins.FritzCall.password.value != "":
233                         parms = "login:command/password=%s" % (config.plugins.FritzCall.password.value)
234                         url = "http://%s/cgi-bin/webcm" % (config.plugins.FritzCall.hostname.value)
235                         debug("[FritzCallFBF] _oldLogin: '" + url + "' parms: '" + parms + "'")
236                         getPage(url,
237                                 method="POST",
238                                 agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
239                                 headers={'Content-Type': "application/x-www-form-urlencoded", 'Content-Length': str(len(parms))
240                                                 }, postdata=parms).addCallback(self._gotPageLogin).addCallback(self._loginCallback).addErrback(self._errorLogin)
241                 elif self._loginCallback:
242                         debug("[FritzCallFBF] _oldLogin: no password, calling " + repr(self._loginCallback))
243                         self._loginCallback()
244
245         def _md5Login(self, sidXml):
246                 def buildResponse(challenge, text):
247                         debug("[FritzCallFBF] _md5Login7buildResponse: challenge: " + challenge + ' text: ' + text)
248                         text = (challenge + '-' + text).decode('utf-8','ignore').encode('utf-16-le')
249                         for i in range(len(text)):
250                                 if ord(text[i]) > 255:
251                                         text[i] = '.'
252                         m = hashlib.md5()
253                         m.update(text)
254                         debug("[FritzCallFBF] md5Login/buildResponse: " + m.hexdigest())
255                         return challenge + '-' + m.hexdigest()
256
257                 debug("[FritzCallFBF] _md5Login")
258                 found = re.match('.*<SID>([^<]*)</SID>', sidXml, re.S)
259                 if found:
260                         self._md5Sid = found.group(1)
261                         debug("[FritzCallFBF] _md5Login: SID "+ self._md5Sid)
262                 else:
263                         debug("[FritzCallFBF] _md5Login: no sid! That must be an old firmware.")
264                         self._oldLogin('No error')
265                         return
266
267                 if sidXml.find('<iswriteaccess>0</iswriteaccess>') != -1:
268                         debug("[FritzCallFBF] _md5Login: logging in")
269                         found = re.match('.*<Challenge>([^<]*)</Challenge>', sidXml, re.S)
270                         if found:
271                                 challenge = found.group(1)
272                                 debug("[FritzCallFBF] _md5Login: challenge " + challenge)
273                         else:
274                                 challenge = None
275                                 debug("[FritzCallFBF] _md5Login: login necessary and no challenge! That is terribly wrong.")
276                         debug("[FritzCallFBF] _md5Login: renew timestamp: " + str(time.time()))
277                         self._md5LoginTimestamp = time.time()
278                         parms = urlencode({
279                                                         'getpage':'../html/de/menus/menu2.html', # 'var:pagename':'home', 'var:menu':'home', 
280                                                         'login:command/response': buildResponse(challenge, config.plugins.FritzCall.password.value),
281                                                         })
282                         url = "http://%s/cgi-bin/webcm" % (config.plugins.FritzCall.hostname.value)
283                         debug("[FritzCallFBF] _md5Login: '" + url + "' parms: '" + parms + "'")
284                         getPage(url,
285                                 method="POST",
286                                 agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
287                                 headers={'Content-Type': "application/x-www-form-urlencoded", 'Content-Length': str(len(parms))
288                                                 }, postdata=parms).addCallback(self._gotPageLogin).addCallback(self._loginCallback).addErrback(self._errorLogin)
289                 else: # we assume value 1 here, no login necessary
290                         debug("[FritzCallFBF] _md5Login: no login necessary: " + str(time.time()))
291                         self._md5LoginTimestamp = time.time()
292                         self._loginCallback()
293
294         def _gotPageLogin(self, html):
295                 if self._callScreen:
296                         self._callScreen.updateStatus(_("login verification"))
297                 debug("[FritzCallFBF] _gotPageLogin: verify login")
298                 found = re.match('.*<p class="errorMessage">FEHLER:&nbsp;([^<]*)</p>', html, re.S)
299                 if found:
300                         text = _("FRITZ!Box - Error logging in: %s") + found.group(1)
301                         self._notify(text)
302                 else:
303                         if self._callScreen:
304                                 self._callScreen.updateStatus(_("login ok"))
305
306                 found = re.match('.*<input type="hidden" name="sid" value="([^\"]*)"', html, re.S)
307                 if found:
308                         self._md5Sid = found.group(1)
309                         debug("[FritzCallFBF] _gotPageLogin: found sid: " + self._md5Sid)
310
311         def _errorLogin(self, error):
312                 debug("[FritzCallFBF] _errorLogin: %s" % (error))
313                 text = _("FRITZ!Box - Error logging in: %s") % error
314                 self._notify(text)
315
316         def _logout(self):
317                 if self._md5LoginTimestamp:
318                         self._md5LoginTimestamp = None
319                         parms = urlencode({
320                                                         'getpage':'../html/de/menus/menu2.html', # 'var:pagename':'home', 'var:menu':'home', 
321                                                         'login:command/logout':'bye bye Fritz'
322                                                         })
323                         url = "http://%s/cgi-bin/webcm" % (config.plugins.FritzCall.hostname.value)
324                         debug("[FritzCallFBF] logout: '" + url + "' parms: '" + parms + "'")
325                         getPage(url,
326                                 method="POST",
327                                 agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
328                                 headers={'Content-Type': "application/x-www-form-urlencoded", 'Content-Length': str(len(parms))
329                                                 }, postdata=parms).addErrback(self._errorLogout)
330
331         def _errorLogout(self, error):
332                 debug("[FritzCallFBF] _errorLogout: %s" % (error))
333                 text = _("FRITZ!Box - Error logging out: %s") % error
334                 self._notify(text)
335
336         def loadFritzBoxPhonebook(self):
337                 debug("[FritzCallFBF] loadFritzBoxPhonebook")
338                 if config.plugins.FritzCall.fritzphonebook.value:
339                         debug("[FritzCallFBF] loadFritzBoxPhonebook: logging in")
340                         self._login(self._loadFritzBoxPhonebook)
341
342         def _loadFritzBoxPhonebook(self, html=None):
343                         parms = urlencode({
344                                                         'getpage':'../html/de/menus/menu2.html',
345                                                         'var:lang':'de',
346                                                         'var:pagename':'fonbuch',
347                                                         'var:menu':'fon',
348                                                         'sid':self._md5Sid,
349                                                         'telcfg:settings/Phonebook/Books/Select':'0', # this selects always the first phonbook
350                                                         })
351                         url = "http://%s/cgi-bin/webcm" % (config.plugins.FritzCall.hostname.value)
352                         debug("[FritzCallFBF] _loadFritzBoxPhonebook: '" + url + "' parms: '" + parms + "'")
353                         getPage(url,
354                                 method="POST",
355                                 agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
356                                 headers={'Content-Type': "application/x-www-form-urlencoded", 'Content-Length': str(len(parms))
357                                                 }, postdata=parms).addCallback(self._parseFritzBoxPhonebook).addErrback(self._errorLoad)
358
359         def _parseFritzBoxPhonebook(self, html):
360                 debug("[FritzCallFBF] _parseFritzBoxPhonebook")
361                 self._logout()
362                 if re.search('TrFonName', html):
363                         #===============================================================================
364                         #                                New Style: 7270 (FW 54.04.58, 54.04.63-11941, 54.04.70, 54.04.74-14371)
365                         #                                                       7170 (FW 29.04.70) 22.03.2009
366                         #                                                       7141 (FW 40.04.68) 22.03.2009
367                         #       We expect one line with TrFonName followed by several lines with
368                         #       TrFonNr(Type,Number,Shortcut,Vanity), which all belong to the name in TrFonName.
369                         #===============================================================================
370                         found = re.match('.*<meta http-equiv=content-type content="text/html; charset=([^"]*)">', html, re.S)
371                         if found:
372                                 charset = found.group(1)
373                                 debug("[FritzCallFBF] _parseFritzBoxPhonebook: found charset: " + charset)
374                                 html = html2unicode(html.decode(charset)).encode('utf-8') # this looks silly, but has to be
375                         else: # this is kind of emergency conversion...
376                                 try:
377                                         html = html2unicode(html.decode('utf-8')).encode('utf-8') # this looks silly, but has to be
378                                 except UnicodeDecodeError:
379                                         html = html2unicode(html.decode('iso-8859-1')).encode('utf-8') # this looks silly, but has to be
380                         entrymask = re.compile('(TrFonName\("[^"]+", "[^"]+", "[^"]*"\);.*?)TrFon1\(\)', re.S)
381                         entries = entrymask.finditer(html)
382                         for entry in entries:
383                                 # debug(entry.group(1)
384                                 # TrFonName (id, name, category)
385                                 found = re.match('TrFonName\("[^"]*", "([^"]+)", "[^"]*"\);', entry.group(1))
386                                 if found:
387                                         name = found.group(1).replace(',','').strip()
388                                 else:
389                                         continue
390                                 # TrFonNr (type, rufnr, code, vanity)
391                                 detailmask = re.compile('TrFonNr\("([^"]*)", "([^"]*)", "([^"]*)", "([^"]*)"\);', re.S)
392                                 details = detailmask.finditer(entry.group(1))
393                                 for found in details:
394                                         thisnumber = found.group(2).strip()
395                                         if not thisnumber:
396                                                 debug("[FritzCallFBF] Ignoring entry with empty number for '''%s'''" % (name))
397                                                 continue
398                                         else:
399                                                 thisname = name
400                                                 type = found.group(1)
401                                                 if config.plugins.FritzCall.showType.value:
402                                                         if type == "mobile":
403                                                                 thisname = thisname + " (" + _("mobile") + ")"
404                                                         elif type == "home":
405                                                                 thisname = thisname + " (" + _("home") + ")"
406                                                         elif type == "work":
407                                                                 thisname = thisname + " (" + _("work") + ")"
408
409                                                 if config.plugins.FritzCall.showShortcut.value and found.group(3):
410                                                         thisname = thisname + ", " + _("Shortcut") + ": " + found.group(3)
411                                                 if config.plugins.FritzCall.showVanity.value and found.group(4):
412                                                         thisname = thisname + ", " + _("Vanity") + ": " + found.group(4)
413
414                                                 debug("[FritzCallFBF] Adding '''%s''' with '''%s''' from FRITZ!Box Phonebook!" % (thisname.strip(), thisnumber))
415                                                 # Beware: strings in phonebook.phonebook have to be in utf-8!
416                                                 phonebook.phonebook[thisnumber] = thisname
417
418                 elif re.search('TrFon', html):
419                         #===============================================================================
420                         #                               Old Style: 7050 (FW 14.04.33)
421                         #       We expect one line with TrFon(No,Name,Number,Shortcut,Vanity)
422                         #   Encoding should be plain Ascii...
423                         #===============================================================================                                
424                         entrymask = re.compile('TrFon\("[^"]*", "([^"]*)", "([^"]*)", "([^"]*)", "([^"]*)"\)', re.S)
425                         entries = entrymask.finditer(html)
426                         for found in entries:
427                                 name = found.group(1).strip().replace(',','')
428                                 # debug("[FritzCallFBF] pos: %s name: %s" %(found.group(0),name))
429                                 thisnumber = found.group(2).strip()
430                                 if config.plugins.FritzCall.showShortcut.value and found.group(3):
431                                         name = name + ", " + _("Shortcut") + ": " + found.group(3)
432                                 if config.plugins.FritzCall.showVanity.value and found.group(4):
433                                         name = name + ", " + _("Vanity") + ": " + found.group(4)
434                                 if thisnumber:
435                                         name = html2unicode(unicode(name)).encode('utf-8')
436                                         debug("[FritzCallFBF] Adding '''%s''' with '''%s''' from FRITZ!Box Phonebook!" % (name, thisnumber))
437                                         # Beware: strings in phonebook.phonebook have to be in utf-8!
438                                         phonebook.phonebook[thisnumber] = name
439                                 else:
440                                         debug("[FritzCallFBF] ignoring empty number for %s" % name)
441                                 continue
442                 else:
443                         self._notify(_("Could not parse FRITZ!Box Phonebook entry"))
444
445         def _errorLoad(self, error):
446                 debug("[FritzCallFBF] _errorLoad: %s" % (error))
447                 text = _("FRITZ!Box - Could not load phonebook: %s") % error
448                 self._notify(text)
449                 self._logout()
450
451         def getCalls(self, callScreen, callback, type):
452                 #
453                 # call sequence must be:
454                 # - login
455                 # - getPage -> _gotPageLogin
456                 # - loginCallback (_getCalls)
457                 # - getPage -> _getCalls1
458                 debug("[FritzCallFBF] getCalls")
459                 self._callScreen = callScreen
460                 self._callType = type
461                 self._callCallback = callback
462                 if (time.time() - self._timestampCalls) > 180: 
463                         debug("[FritzCallFBF] getCalls: outdated data, login and get new ones")
464                         self._timestampCalls = time.time()
465                         self._login(self._getCalls)
466                 elif not self._callList:
467                         debug("[FritzCallFBF] getCalls: time is ok, but no callList")
468                         self._getCalls1()
469                 else:
470                         debug("[FritzCallFBF] getCalls: time is ok, callList is ok")
471                         self._gotPageCalls()
472
473         def _getCalls(self, html=None):
474                 #
475                 # we need this to fill Anrufliste.csv
476                 # http://repeater1/cgi-bin/webcm?getpage=../html/de/menus/menu2.html&var:lang=de&var:menu=fon&var:pagename=foncalls
477                 #
478                 debug("[FritzCallFBF] _getCalls")
479                 if html:
480                         found = re.match('.*<p class="errorMessage">FEHLER:&nbsp;([^<]*)</p>', html, re.S)
481                         if found:
482                                 text = _("FRITZ!Box - Error logging in: %s") + found.group(1)
483                                 self._notify(text)
484                                 return
485
486                 if self._callScreen:
487                         self._callScreen.updateStatus(_("preparing"))
488                 parms = urlencode({'getpage':'../html/de/menus/menu2.html', 'var:lang':'de', 'var:pagename':'foncalls', 'var:menu':'fon', 'sid':self._md5Sid})
489                 url = "http://%s/cgi-bin/webcm?%s" % (config.plugins.FritzCall.hostname.value, parms)
490                 getPage(url).addCallback(self._getCalls1).addErrback(self._errorCalls)
491
492         def _getCalls1(self, html=""):
493                 #
494                 # finally we should have successfully lgged in and filled the csv
495                 #
496                 debug("[FritzCallFBF] _getCalls1")
497                 if self._callScreen:
498                         self._callScreen.updateStatus(_("finishing"))
499                 parms = urlencode({'getpage':'../html/de/FRITZ!Box_Anrufliste.csv', 'sid':self._md5Sid})
500                 url = "http://%s/cgi-bin/webcm?%s" % (config.plugins.FritzCall.hostname.value, parms)
501                 getPage(url).addCallback(self._gotPageCalls).addErrback(self._errorCalls)
502
503         def _gotPageCalls(self, csv=""):
504                 def _resolveNumber(number):
505                         if number.isdigit():
506                                 if config.plugins.FritzCall.internal.value and len(number) > 3 and number[0] == "0": number = number[1:]
507                                 # strip CbC prefix
508                                 if config.plugins.FritzCall.country.value == '0049':
509                                         if re.match('^0100\d\d', number):
510                                                 number = number[6:]
511                                         elif re.match('^010\d\d', number):
512                                                 number = number[5:]
513                                 if config.plugins.FritzCall.prefix.value and number and number[0] != '0':               # should only happen for outgoing
514                                         number = config.plugins.FritzCall.prefix.value + number
515                                 name = phonebook.search(number)
516                                 if name:
517                                         found = re.match('(.*?)\n.*', name)
518                                         if found:
519                                                 name = found.group(1)
520                                         number = name
521                         elif number == "":
522                                 number = _("UNKNOWN")
523                         # if len(number) > 20: number = number[:20]
524                         return number
525
526                 self._logout()
527                 if csv:
528                         debug("[FritzCallFBF] _gotPageCalls: got csv, setting callList")
529                         if self._callScreen:
530                                 self._callScreen.updateStatus(_("done"))
531                         # check for error: wrong password or password not set... TODO
532                         found = re.search('Melden Sie sich mit dem Kennwort der FRITZ!Box an', csv)
533                         if found:
534                                 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.")
535                                 # self.session.open(MessageBox, text, MessageBox.TYPE_ERROR, timeout=config.plugins.FritzCall.timeout.value)
536                                 self._notify(text)
537                                 return
538
539                         csv = csv.decode('iso-8859-1', 'replace').encode('utf-8', 'replace')
540                         lines = csv.splitlines()
541                         self._callList = lines
542                 elif self._callList:
543                         debug("[FritzCallFBF] _gotPageCalls: got no csv, but have callList")
544                         if self._callScreen:
545                                 self._callScreen.updateStatus(_("done, using last list"))
546                         lines = self._callList
547                 else:
548                         debug("[FritzCallFBF] _gotPageCalls: got no csv, no callList, leaving")
549                         return
550                         
551                 _callList = []
552                 for line in lines:
553                         # Typ;Datum;Name;Rufnummer;Nebenstelle;Eigene Rufnummer;Dauer
554                         found = re.match("^(" + self._callType + ");([^;]*);([^;]*);([^;]*);([^;]*);([^;]*);([^;]*)", line)
555                         if found:
556                                 direct = found.group(1)
557                                 date = found.group(2)
558                                 length = found.group(7)
559                                 remote = _resolveNumber(found.group(4))
560                                 if not remote and direct != FBF_OUT_CALLS and found.group(3):
561                                         remote = found.group(3)
562                                 found1 = re.match('Internet: (.*)', found.group(6))
563                                 if found1:
564                                         here = _resolveNumber(found1.group(1))
565                                 else:
566                                         here = _resolveNumber(found.group(6))
567                                 
568                                 # strip CbC prefix for Germany
569                                 number = found.group(4)
570                                 if config.plugins.FritzCall.country.value == '0049':
571                                         if re.match('^0100\d\d', number):
572                                                 number = number[6:]
573                                         elif re.match('^010\d\d', number):
574                                                 number = number[5:]
575                                 if config.plugins.FritzCall.prefix.value and number and number[0] != '0':               # should only happen for outgoing
576                                         number = config.plugins.FritzCall.prefix.value + number
577                                 _callList.append((number, date, here, direct, remote, length))
578
579                 # debug("[FritzCallFBF] _gotPageCalls result:\n" + text
580
581                 if self._callCallback is not None:
582                         # debug("[FritzCallFBF] _gotPageCalls call callback with\n" + text
583                         self._callCallback(_callList)
584                         self._callCallback = None
585                 self._callScreen = None
586
587         def _errorCalls(self, error):
588                 debug("[FritzCallFBF] _errorCalls: %s" % (error))
589                 text = _("FRITZ!Box - Could not load calls: %s") % error
590                 self._notify(text)
591                 self._logout()
592
593         def dial(self, number):
594                 ''' initiate a call to number '''
595                 self.number = number
596                 self._login(self._dial)
597                 
598         def _dial(self, html=None):
599                 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
600                 parms = urlencode({
601                         'getpage':'../html/de/menus/menu2.html',
602                         'var:pagename':'fonbuch',
603                         'var:menu':'home',
604                         'telcfg:settings/UseClickToDial':'1',
605                         'telcfg:settings/DialPort':config.plugins.FritzCall.extension.value,
606                         'telcfg:command/Dial':self.number,
607                         'sid':self._md5Sid
608                         })
609                 debug("[FritzCallFBF] dial url: '" + url + "' parms: '" + parms + "'")
610                 getPage(url,
611                         method="POST",
612                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
613                         headers={
614                                         'Content-Type': "application/x-www-form-urlencoded",
615                                         'Content-Length': str(len(parms))},
616                         postdata=parms).addCallback(self._okDial).addErrback(self._errorDial)
617
618         def _okDial(self, html):
619                 debug("[FritzCallFBF] okDial")
620                 self._logout()
621
622         def _errorDial(self, error):
623                 debug("[FritzCallFBF] errorDial: $s" % error)
624                 text = _("FRITZ!Box - Dialling failed: %s") % error
625                 self._notify(text)
626                 self._logout()
627
628         def changeWLAN(self, statusWLAN):
629                 ''' get status info from FBF '''
630                 debug("[FritzCallFBF] changeWLAN start")
631                 if not statusWLAN or (statusWLAN <> '1' and statusWLAN <> '0'):
632                         return
633                 self.statusWLAN = statusWLAN
634                 self._login(self._changeWLAN)
635                 
636         def _changeWLAN(self, html=None):
637                 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
638                 parms = urlencode({
639                         'getpage':'../html/de/menus/menu2.html',
640                         'var:lang':'de',
641                         'var:pagename':'wlan',
642                         'var:menu':'wlan',
643                         'wlan:settings/ap_enabled':str(self.statusWLAN),
644                         'sid':self._md5Sid
645                         })
646                 debug("[FritzCallFBF] changeWLAN url: '" + url + "' parms: '" + parms + "'")
647                 getPage(url,
648                         method="POST",
649                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
650                         headers={
651                                         'Content-Type': "application/x-www-form-urlencoded",
652                                         'Content-Length': str(len(parms))},
653                         postdata=parms).addCallback(self._okChangeWLAN).addErrback(self._errorChangeWLAN)
654
655         def _okChangeWLAN(self, html):
656                 debug("[FritzCallFBF] okDial")
657                 self._logout()
658
659         def _errorChangeWLAN(self, error):
660                 debug("[FritzCallFBF] _errorChangeWLAN: $s" % error)
661                 text = _("FRITZ!Box - Failed changing WLAN: %s") % error
662                 self._notify(text)
663                 self._logout()
664
665         def changeMailbox(self, whichMailbox):
666                 ''' switch mailbox on/off '''
667                 debug("[FritzCallFBF] changeMailbox start: " + str(whichMailbox))
668                 self.whichMailbox = whichMailbox
669                 self._login(self._changeMailbox)
670
671         def _changeMailbox(self, html=None):
672                 debug("[FritzCallFBF] _changeMailbox")
673                 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
674                 if self.whichMailbox == -1:
675                         for i in range(5):
676                                 if fritzbox.hasMailbox[i+1]: # we have to reference the global object here!
677                                         state = '0'
678                                 else:
679                                         state = '1'
680                                 parms = urlencode({
681                                         'tam:settings/TAM'+str(i)+'/Active':state,
682                                         'sid':self._md5Sid
683                                         })
684                                 debug("[FritzCallFBF] changeMailbox url: '" + url + "' parms: '" + parms + "'")
685                                 getPage(url,
686                                         method="POST",
687                                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
688                                         headers={
689                                                         'Content-Type': "application/x-www-form-urlencoded",
690                                                         'Content-Length': str(len(parms))},
691                                         postdata=parms).addCallback(self._okChangeMailbox).addErrback(self._errorChangeMailbox)
692                 elif self.whichMailbox > 4:
693                         debug("[FritzCallFBF] changeMailbox invalid mailbox number")
694                 else:
695                         if fritzbox.hasMailbox[self.whichMailbox+1]:
696                                 state = '0'
697                         else:
698                                 state = '1'
699                         parms = urlencode({
700                                 'tam:settings/TAM'+str(self.whichMailbox)+'/Active':state,
701                                 'sid':self._md5Sid
702                                 })
703                         debug("[FritzCallFBF] changeMailbox url: '" + url + "' parms: '" + parms + "'")
704                         getPage(url,
705                                 method="POST",
706                                 agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
707                                 headers={
708                                                 'Content-Type': "application/x-www-form-urlencoded",
709                                                 'Content-Length': str(len(parms))},
710                                 postdata=parms).addCallback(self._okChangeMailbox).addErrback(self._errorChangeMailbox)
711
712         def _okChangeMailbox(self, html):
713                 debug("[FritzCallFBF] _okChangeMailbox")
714                 self._logout()
715
716         def _errorChangeMailbox(self, error):
717                 debug("[FritzCallFBF] _errorChangeMailbox: $s" % error)
718                 text = _("FRITZ!Box - Failed changing Mailbox: %s") % error
719                 self._notify(text)
720                 self._logout()
721
722         def getInfo(self, callscreen, callback):
723                 ''' get status info from FBF '''
724                 debug("[FritzCallFBF] getInfo")
725                 self._callScreen = callscreen
726                 self.infoCallback = callback
727                 self._login(self._getInfo)
728                 
729         def _getInfo(self, html=None):
730                 # http://192.168.178.1/cgi-bin/webcm?getpage=../html/de/menus/menu2.html&var:lang=de&var:pagename=home&var:menu=home
731                 debug("[FritzCallFBF] _getInfo: verify login")
732                 if html:
733                         found = re.match('.*<p class="errorMessage">FEHLER:&nbsp;([^<]*)</p>', html, re.S)
734                         if found:
735                                 debug("[FritzCallFBF] _getInfo: Login failed: " + found.group(1))
736                                 text = _("FRITZ!Box - Error logging in: %s") + found.group(1)
737                                 self._notify(text)
738                                 return
739
740                 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
741                 parms = urlencode({
742                         'getpage':'../html/de/menus/menu2.html',
743                         'var:lang':'de',
744                         'var:pagename':'home',
745                         'var:menu':'home',
746                         'sid':self._md5Sid
747                         })
748                 debug("[FritzCallFBF] _getInfo url: '" + url + "' parms: '" + parms + "'")
749                 getPage(url,
750                         method="POST",
751                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
752                         headers={
753                                         'Content-Type': "application/x-www-form-urlencoded",
754                                         'Content-Length': str(len(parms))},
755                         postdata=parms).addCallback(self._okGetInfo).addErrback(self._errorGetInfo)
756
757         def _okGetInfo(self, html):
758                 def readInfo(html):
759                         boxInfo = None
760                         upTime = None
761                         ipAddress = None
762                         wlanState = None
763                         wlanEncrypt = None
764                         dslState = None
765                         dslSpeed = None
766                         tamActive = None
767                         dectActive = None
768
769                         debug("[FritzCallFBF] _okGetInfo/readinfo")
770                         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)
771                         if found:
772                                 boxInfo = found.group(1)+ ', ' + found.group(2)
773                                 boxInfo = boxInfo.replace('&nbsp;',' ')
774                                 # debug("[FritzCallFBF] _okGetInfo Boxinfo: " + boxInfo)
775                         else:
776                                 found = re.match('.*<p class="ac">([^<]*)</p>', html, re.S)
777                                 if found:
778                                         # debug("[FritzCallFBF] _okGetInfo Boxinfo: " + found.group(1))
779                                         boxInfo = found.group(1)
780                 
781                         found = re.match('.*if \(isNaN\(jetzt\)\)\s*return "";\s*var str = "([^"]*)";', html, re.S)
782                         if found:
783                                 # debug("[FritzCallFBF] _okGetInfo Uptime: " + found.group(1))
784                                 upTime = found.group(1)
785                         else:
786                                 found = re.match('.*str = g_pppSeit \+"([^<]*)<br>"\+mldIpAdr;', html, re.S)
787                                 if found:
788                                         # debug("[FritzCallFBF] _okGetInfo Uptime: " + found.group(1))
789                                         upTime = found.group(1)
790                 
791                         found = re.match(".*IpAdrDisplay\('([.\d]+)'\)", html, re.S)
792                         if found:
793                                 # debug("[FritzCallFBF] _okGetInfo IpAdrDisplay: " + found.group(1))
794                                 ipAddress = found.group(1)
795                 
796                         found = re.match('.*function WlanStateLed \(state\){.*?return StateLed\("(\d+)"\);\s*}', html, re.S)
797                         if found:
798                                 # debug("[FritzCallFBF] _okGetInfo WlanState: " + found.group(1))
799                                 wlanState = found.group(1)
800                 
801                         found = re.match('.*var (?:g_)?encryption = "(\d+)";', html, re.S)
802                         if found:
803                                 # debug("[FritzCallFBF] _okGetInfo WlanEncrypt: " + found.group(1))
804                                 wlanEncrypt = found.group(1)
805                 
806                         found = re.match('.*function DslStateDisplay \(state\){\s*var state = "(\d+)";', html, re.S)
807                         if found:
808                                 # debug("[FritzCallFBF] _okGetInfo DslState: " + found.group(1))
809                                 dslState = found.group(1)
810                 
811                         found = re.match('.*function DslStateDisplay \(state\){\s*var state = "\d+";.*?if \("3130" != "0"\) str = "([^"]*)";', html, re.S)
812                         if found:
813                                 # debug("[FritzCallFBF] _okGetInfo DslSpeed: " + found.group(1).strip())
814                                 dslSpeed = found.group(1).strip()
815                 
816                         if html.find('g_tamActive') != -1:
817                                 entries = re.compile('if \("(\d)" == "1"\) {\s*g_tamActive \+= 1;\s*}', re.S).finditer(html)
818                                 tamActive = [0, False, False, False, False, False]
819                                 i=1
820                                 for entry in entries:
821                                         state = entry.group(1)
822                                         if state == '1':
823                                                 tamActive[0] += 1
824                                                 tamActive[i] = True
825                                         i += 1
826
827                                 # debug("[FritzCallFBF] _okGetInfo tamActive: " + str(tamActive))
828                 
829                         if html.find('countDect2') != -1:
830                                 entries = re.compile('if \("1" == "1"\) countDect2\+\+;', re.S).findall(html)
831                                 dectActive = len(entries)
832                                 # debug("[FritzCallFBF] _okGetInfo dectActive: " + str(dectActive))
833                 
834                         return (boxInfo, upTime, ipAddress, wlanState, wlanEncrypt, dslState, dslSpeed, tamActive, dectActive)
835
836                 debug("[FritzCallFBF] _okGetInfo")
837                 info = readInfo(html)
838                 debug("[FritzCallFBF] _okGetInfo info: " + str(info))
839                 self._setProperties(info)
840                 self.infoCallback(info)
841                 self._logout()
842
843         def _errorGetInfo(self, error):
844                 debug("[FritzCallFBF] _errorGetInfo: %s" % (error))
845                 text = _("FRITZ!Box - Error getting status: %s") % error
846                 self._notify(text)
847                 self._logout()
848                 # linkP = open("/tmp/FritzCall_errorGetInfo.htm", "w")
849                 # linkP.write(error)
850                 # linkP.close()
851
852         def reset(self):
853                 self._login(self._reset)
854
855         def _reset(self, html=None):
856                 # 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
857                 self._callScreen.close()
858                 url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
859                 parms = urlencode({
860                         'getpage':'../html/reboot.html',
861                         'var:lang':'de',
862                         'var:pagename':'reset',
863                         'var:menu':'system',
864                         'logic:command/reboot':'../gateway/commands/saveconfig.html',
865                         'sid':self._md5Sid
866                         })
867                 debug("[FritzCallFBF] _reset url: '" + url + "' parms: '" + parms + "'")
868                 getPage(url,
869                         method="POST",
870                         agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5",
871                         headers={
872                                         'Content-Type': "application/x-www-form-urlencoded",
873                                         'Content-Length': str(len(parms))},
874                         postdata=parms)
875
876         def _okReset(self, html):
877                 debug("[FritzCallFBF] _okReset")
878                 self._logout()
879
880         def _errorReset(self, error):
881                 debug("[FritzCallFBF] _errorReset: %s" % (error))
882                 text = _("FRITZ!Box - Error resetting: %s") % error
883                 self._notify(text)
884                 self._logout()
885
886 #===============================================================================
887 #       def hangup(self):
888 #               ''' hangup call on port; not used for now '''
889 #               url = "http://%s/cgi-bin/webcm" % config.plugins.FritzCall.hostname.value
890 #               parms = urlencode({
891 #                       'id':'uiPostForm',
892 #                       'name':'uiPostForm',
893 #                       'login:command/password': config.plugins.FritzCall.password.value,
894 #                       'telcfg:settings/UseClickToDial':'1',
895 #                       'telcfg:settings/DialPort':config.plugins.FritzCall.extension.value,
896 #                       'telcfg:command/Hangup':'',
897 #                       'sid':self._md5Sid
898 #                       })
899 #               debug("[FritzCallFBF] hangup url: '" + url + "' parms: '" + parms + "'")
900 #               getPage(url,
901 #                       method="POST",
902 #                       headers={
903 #                                       'Content-Type': "application/x-www-form-urlencoded",
904 #                                       'Content-Length': str(len(parms))},
905 #                       postdata=parms)
906 #===============================================================================
907
908 # fritzbox = FritzCallFBF()
909
910 class FritzMenu(Screen,HelpableScreen):
911         def __init__(self, session):
912                 fontSize = scaleV(24,21) # indeed this is font size +2
913                 noButtons = 2 # reset, wlan
914                 if fritzbox.hasMailbox:
915                         noButtons += 1
916                 width = max(DESKTOP_WIDTH - scaleH(500,250), noButtons*140+(noButtons+1)*10)
917                 height = 5 + 2*fontSize + 10 + 2*fontSize + 10 + 2*fontSize + 10 + 40 + 5
918                 if fritzbox.hasMailbox:
919                         height += fontSize
920                 if fritzbox.hasDect:
921                         height += fontSize
922                 buttonsGap = (width-noButtons*140)/(noButtons+1)
923                 buttonsVPos = height-40-5
924                 if fritzbox.hasMailbox:
925                         mailboxLine = """
926                                 <widget name="FBFMailbox" position="%d,%d" size="%d,%d" font="Regular;%d" />
927                                 <widget name="mailbox_inactive" pixmap="skin_default/buttons/button_green_off.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
928                                 <widget name="mailbox_active" pixmap="skin_default/buttons/button_green.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
929                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
930                                 <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" />
931                                 """ % (
932                                                 40, 5+2*fontSize+10+4*fontSize+10, # position mailbox
933                                                 width-40-20, fontSize, # size mailbox
934                                                 fontSize-2,
935                                                 20, 5+2*fontSize+10+4*fontSize+10+(fontSize-16)/2, # position button mailbox
936                                                 20, 5+2*fontSize+10+4*fontSize+10+(fontSize-16)/2, # position button mailbox
937                                                 noButtons*buttonsGap+(noButtons-1)*140, buttonsVPos,
938                                                 noButtons*buttonsGap+(noButtons-1)*140, buttonsVPos,
939                                 )
940                 else:
941                         mailboxLine = ""
942                 if fritzbox.hasDect: # it is assumed here, that, when DECT, then also mailbox...
943                         dectLine = """
944                                 <widget name="FBFDect" position="%d,%d" size="%d,%d" font="Regular;%d" />
945                                 <widget name="dect_inactive" pixmap="skin_default/buttons/button_green_off.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
946                                 <widget name="dect_active" pixmap="skin_default/buttons/button_green.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
947                                 """ %(
948                                                 40, 5+2*fontSize+10+5*fontSize+10, # position dect
949                                                 width-40-20, fontSize, # size dect
950                                                 fontSize-2,
951                                                 20, 5+2*fontSize+10+5*fontSize+10+(fontSize-16)/2, # position button dect
952                                                 20, 5+2*fontSize+10+5*fontSize+10+(fontSize-16)/2, # position button dect
953                                 )
954                 else:
955                         dectLine = ""
956         
957                 self.skin = """
958                         <screen name="FritzMenu" position="%d,%d" size="%d,%d" title="%s" >
959                                 <widget name="FBFInfo" position="%d,%d" size="%d,%d" font="Regular;%d" />
960                                 <widget name="FBFInternet" position="%d,%d" size="%d,%d" font="Regular;%d" />
961                                 <widget name="internet_inactive" pixmap="skin_default/buttons/button_green_off.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
962                                 <widget name="internet_active" pixmap="skin_default/buttons/button_green.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
963                                 <widget name="FBFDsl" position="%d,%d" size="%d,%d" font="Regular;%d" />
964                                 <widget name="dsl_inactive" pixmap="skin_default/buttons/button_green_off.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
965                                 <widget name="dsl_active" pixmap="skin_default/buttons/button_green.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
966                                 <widget name="FBFWlan" position="%d,%d" size="%d,%d" font="Regular;%d" />
967                                 <widget name="wlan_inactive" pixmap="skin_default/buttons/button_green_off.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
968                                 <widget name="wlan_active" pixmap="skin_default/buttons/button_green.png" position="%d,%d" size="15,16" transparent="1" alphatest="on"/>
969                                 %s
970                                 %s
971                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
972                                 <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" />
973                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
974                                 <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" />
975                         </screen>""" % (
976                                                 (DESKTOP_WIDTH - width) / 2, (DESKTOP_HEIGHT - height) / 2, # position
977                                                 width, height, # size
978                                                 _("FRITZ!Box Fon Status"),
979                                                 40, 5, # position info
980                                                 width-2*40, 2*fontSize, # size info
981                                                 fontSize-2,
982                                                 40, 5+2*fontSize+10, # position internet
983                                                 width-40, 2*fontSize, # size internet
984                                                 fontSize-2,
985                                                 20, 5+2*fontSize+10+(fontSize-16)/2, # position button internet
986                                                 20, 5+2*fontSize+10+(fontSize-16)/2, # position button internet
987                                                 40, 5+2*fontSize+10+2*fontSize+10, # position dsl
988                                                 width-40-20, fontSize, # size dsl
989                                                 fontSize-2,
990                                                 20, 5+2*fontSize+10+2*fontSize+10+(fontSize-16)/2, # position button dsl
991                                                 20, 5+2*fontSize+10+2*fontSize+10+(fontSize-16)/2, # position button dsl
992                                                 40, 5+2*fontSize+10+3*fontSize+10, # position wlan
993                                                 width-40-20, fontSize, # size wlan
994                                                 fontSize-2,
995                                                 20, 5+2*fontSize+10+3*fontSize+10+(fontSize-16)/2, # position button wlan
996                                                 20, 5+2*fontSize+10+3*fontSize+10+(fontSize-16)/2, # position button wlan
997                                                 mailboxLine,
998                                                 dectLine,
999                                                 buttonsGap, buttonsVPos, buttonsGap, buttonsVPos,
1000                                                 buttonsGap+140+buttonsGap, buttonsVPos, buttonsGap+140+buttonsGap, buttonsVPos,
1001                                                 )
1002
1003                 Screen.__init__(self, session)
1004                 HelpableScreen.__init__(self)
1005                 # TRANSLATORS: keep it short, this is a button
1006                 self["key_red"] = Button(_("Reset"))
1007                 # TRANSLATORS: keep it short, this is a button
1008                 self["key_green"] = Button(_("Toggle WLAN"))
1009                 self.mailboxActive = False
1010                 if fritzbox.hasMailbox:
1011                         # TRANSLATORS: keep it short, this is a button
1012                         self["key_yellow"] = Button(_("Toggle Mailbox"))
1013                         self["menuActions"] = ActionMap(["OkCancelActions", "ColorActions", "NumberActions", "EPGSelectActions"],
1014                                                                                         {
1015                                                                                         "cancel": self.exit,
1016                                                                                         "ok": self.exit,
1017                                                                                         "red": self.reset,
1018                                                                                         "green": self.toggleWlan,
1019                                                                                         "yellow": (lambda: self.toggleMailbox(-1)),
1020                                                                                         "0": (lambda: self.toggleMailbox(0)),
1021                                                                                         "1": (lambda: self.toggleMailbox(1)),
1022                                                                                         "2": (lambda: self.toggleMailbox(2)),
1023                                                                                         "3": (lambda: self.toggleMailbox(3)),
1024                                                                                         "4": (lambda: self.toggleMailbox(4)),
1025                                                                                         "info": self._getInfo,
1026                                                                                         }, -2)
1027                         # TRANSLATORS: keep it short, this is a help text
1028                         self.helpList.append((self["menuActions"], "ColorActions", [("yellow", _("Toggle all mailboxes"))]))
1029                         # TRANSLATORS: keep it short, this is a help text
1030                         self.helpList.append((self["menuActions"], "NumberActions", [("0", _("Toggle 1. mailbox"))]))
1031                         # TRANSLATORS: keep it short, this is a help text
1032                         self.helpList.append((self["menuActions"], "NumberActions", [("1", _("Toggle 2. mailbox"))]))
1033                         # TRANSLATORS: keep it short, this is a help text
1034                         self.helpList.append((self["menuActions"], "NumberActions", [("2", _("Toggle 3. mailbox"))]))
1035                         # TRANSLATORS: keep it short, this is a help text
1036                         self.helpList.append((self["menuActions"], "NumberActions", [("3", _("Toggle 4. mailbox"))]))
1037                         # TRANSLATORS: keep it short, this is a help text
1038                         self.helpList.append((self["menuActions"], "NumberActions", [("4", _("Toggle 5. mailbox"))]))
1039                         self["FBFMailbox"] = Label(_('Mailbox'))
1040                         self["mailbox_inactive"] = Pixmap()
1041                         self["mailbox_active"] = Pixmap()
1042                         self["mailbox_active"].hide()
1043                 else:
1044                         self["menuActions"] = ActionMap(["OkCancelActions", "ColorActions", "EPGSelectActions"],
1045                                                                                         {
1046                                                                                         "cancel": self.exit,
1047                                                                                         "ok": self.exit,
1048                                                                                         "green": self.toggleWlan,
1049                                                                                         "red": self.reset,
1050                                                                                         "info": self._getInfo,
1051                                                                                         }, -2)
1052
1053                 # TRANSLATORS: keep it short, this is a help text
1054                 self.helpList.append((self["menuActions"], "OkCancelActions", [("cancel", _("Quit"))]))
1055                 # TRANSLATORS: keep it short, this is a help text
1056                 self.helpList.append((self["menuActions"], "OkCancelActions", [("ok", _("Quit"))]))
1057                 # TRANSLATORS: keep it short, this is a help text
1058                 self.helpList.append((self["menuActions"], "ColorActions", [("green", _("Toggle WLAN"))]))
1059                 # TRANSLATORS: keep it short, this is a help text
1060                 self.helpList.append((self["menuActions"], "ColorActions", [("red", _("Reset"))]))
1061                 # TRANSLATORS: keep it short, this is a help text
1062                 self.helpList.append((self["menuActions"], "EPGSelectActions", [("info", _("Refresh status"))]))
1063
1064                 self["FBFInfo"] = Label(_('Getting status from FRITZ!Box Fon...'))
1065
1066                 self["FBFInternet"] = Label('Internet')
1067                 self["internet_inactive"] = Pixmap()
1068                 self["internet_active"] = Pixmap()
1069                 self["internet_active"].hide()
1070
1071                 self["FBFDsl"] = Label('DSL')
1072                 self["dsl_inactive"] = Pixmap()
1073                 self["dsl_active"] = Pixmap()
1074                 self["dsl_active"].hide()
1075
1076                 self["FBFWlan"] = Label('WLAN ')
1077                 self["wlan_inactive"] = Pixmap()
1078                 self["wlan_active"] = Pixmap()
1079                 self["wlan_active"].hide()
1080                 self.wlanActive = False
1081
1082                 if fritzbox.hasDect: 
1083                         self["FBFDect"] = Label('DECT')
1084                         self["dect_inactive"] = Pixmap()
1085                         self["dect_active"] = Pixmap()
1086                         self["dect_active"].hide()
1087
1088                 self.timer = eTimer()
1089                 self.timer.callback.append(self._getInfo1)
1090                 self.onShown.append(lambda: self.timer.start(5000))
1091                 self.onHide.append(lambda: self.timer.stop())
1092                 self._getInfo()
1093
1094         def _getInfo(self):
1095                 self._getInfo1(self)
1096
1097         def _getInfo1(self, screen=None):
1098                 fritzbox.getInfo(screen, self._fillMenu)
1099
1100         def updateStatus(self, text):
1101                 self["FBFInfo"].setText(_("Getting status from FRITZ!Box Fon...") + text)
1102
1103         def _fillMenu(self, status):
1104                 (boxInfo, upTime, ipAddress, wlanState, wlanEncrypt, dslState, dslSpeed, tamActive, dectActive) = status
1105                 if not boxInfo:
1106                         return
1107                 self.wlanActive = (wlanState == '1')
1108                 self.mailboxActive = False
1109                 try:
1110                         self["FBFInfo"].setText(boxInfo.replace(', ', '\n'))
1111                         if ipAddress:
1112                                 if upTime:
1113                                         self["FBFInternet"].setText('Internet ' + _('IP Address:') + ' ' + ipAddress + '\n' + _('Connected since') + ' ' + upTime)
1114                                 else:
1115                                         self["FBFInternet"].setText('Internet ' + _('IP Address:') + ' ' + ipAddress)
1116                                 self["internet_inactive"].hide()
1117                                 self["internet_active"].show()
1118                         else:
1119                                 self["internet_active"].hide()
1120                                 self["internet_inactive"].show()
1121                         if dslState=='5':
1122                                 self["dsl_inactive"].hide()
1123                                 self["dsl_active"].show()
1124                         else:
1125                                 self["dsl_active"].hide()
1126                                 self["dsl_inactive"].show()
1127                         if dslSpeed:
1128                                 self["FBFDsl"].setText('DSL ' + dslSpeed)
1129                         if wlanState=='1':
1130                                 self["wlan_inactive"].hide()
1131                                 self["wlan_active"].show()
1132                                 if wlanEncrypt:
1133                                         if wlanEncrypt=='0':
1134                                                 self["FBFWlan"].setText('WLAN ' + _('not encrypted'))
1135                                         else:
1136                                                 self["FBFWlan"].setText('WLAN ' + _('encrypted'))
1137                         else:
1138                                 self["wlan_active"].hide()
1139                                 self["wlan_inactive"].show()
1140         
1141                         if fritzbox.hasMailbox:
1142                                 if  not tamActive or tamActive[0] == 0:
1143                                         self.mailboxActive = False
1144                                         self["mailbox_active"].hide()
1145                                         self["mailbox_inactive"].show()
1146                                         self["FBFMailbox"].setText(_('No mailbox active'))
1147                                 else:
1148                                         self.mailboxActive = True
1149                                         message = '('
1150                                         for i in range(5):
1151                                                 if tamActive[i+1]:
1152                                                         message = message + str(i) + ','
1153                                         message = message[:-1] + ')'
1154                                         self["mailbox_inactive"].hide()
1155                                         self["mailbox_active"].show()
1156                                         if tamActive[0] == 1:
1157                                                 self["FBFMailbox"].setText(_('One mailbox active') + ' ' + message)
1158                                         else:
1159                                                 self["FBFMailbox"].setText(str(tamActive[0]) + ' ' + _('mailboxes active') + ' ' + message)
1160         
1161                         if fritzbox.hasDect and dectActive:
1162                                 self["dect_inactive"].hide()
1163                                 self["dect_active"].show()
1164                                 if dectActive == 0:
1165                                         self["FBFDect"].setText(_('No DECT phone registered'))
1166                                 else:
1167                                         if dectActive == 1:
1168                                                 self["FBFDect"].setText(_('One DECT phone registered'))
1169                                         else:
1170                                                 self["FBFDect"].setText(str(dectActive) + ' ' + _('DECT phones registered'))
1171                 except KeyError:
1172                         debug("[FritzCallFBF] _fillMenu: " + traceback.format_exc())
1173
1174         def toggleWlan(self):
1175                 if self.wlanActive:
1176                         debug("[FritzMenu] toggleWlan off")
1177                         FritzCallFBF(False).changeWLAN('0') # we need a separate container so we do not collide with getInfo
1178                 else:
1179                         debug("[FritzMenu] toggleWlan off")
1180                         FritzCallFBF(False).changeWLAN('1') # we need a separate container so we do not collide with getInfo
1181
1182         def toggleMailbox(self, which):
1183                 debug("[FritzMenu] toggleMailbox")
1184                 if fritzbox.hasMailbox:
1185                         debug("[FritzMenu] toggleMailbox off")
1186                         FritzCallFBF(False).changeMailbox(which) # we need a separate container so we do not collide with getInfo
1187
1188         def reset(self):
1189                 fritzbox.reset()
1190                 self.exit()
1191
1192         def exit(self):
1193                 self.timer.stop()
1194                 self.close()
1195
1196
1197 class FritzDisplayCalls(Screen, HelpableScreen):
1198
1199         def __init__(self, session, text=""):
1200                 if config.plugins.FritzCall.fullscreen.value:
1201                         self.width = DESKTOP_WIDTH
1202                         self.height = DESKTOP_HEIGHT
1203                         backMainPng = ""
1204                         if os.path.exists(resolveFilename(SCOPE_SKIN_IMAGE, DESKTOP_SKIN + "/menu/back-main.png")):
1205                                 backMainPng = DESKTOP_SKIN + "/menu/back-main.png"
1206                         elif os.path.exists(resolveFilename(SCOPE_SKIN_IMAGE, "Kerni-HD1/menu/back-main.png")):
1207                                 backMainPng = "Kerni-HD1/menu/back-main.png"
1208                         elif os.path.exists(resolveFilename(SCOPE_SKIN_IMAGE, "Kerni-HD1-picon/menu/back-main.png")):
1209                                 backMainPng = "Kerni-HD1-picon/menu/back-main.png"
1210                         if backMainPng:
1211                                         backMainLine = """<ePixmap position="0,0" zPosition="-10" size="%d,%d" pixmap="%s" transparent="1" />""" % (self.width, self.height, backMainPng)
1212                         else:
1213                                 backMainLine = ""
1214                         debug("[FritzDisplayCalls] backMainLine: " + backMainLine)
1215                                 
1216                         # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
1217                         self.skin = """
1218                                 <screen name="FritzDisplayCalls" position="0,0" size="%d,%d" title="%s" flags="wfNoBorder">
1219                                         %s
1220                                         <widget source="global.CurrentTime" render="Label" position="%d,%d" size="%d,%d" font="Regular;%d" halign="right" backgroundColor="#0b67a2" transparent="1">
1221                                                 <convert type="ClockToText">Default</convert>
1222                                         </widget>
1223                                         <widget source="global.CurrentTime" render="Label" position="%d,%d" size="%d,%d" font="Regular;%d" halign="right" backgroundColor="#0b67a2" transparent="1">
1224                                                 <convert type="ClockToText">Date</convert>
1225                                         </widget>
1226                                         <eLabel text="%s" position="%d,%d" size="%d,%d" font="Regular;%d" halign="center" backgroundColor="#0b67a2" transparent="1"/>
1227                         
1228                                         <widget name="statusbar" position="%d,%d"  size="%d,%d" font="Regular;%d" backgroundColor="#353e575e" transparent="1" />
1229                                         <widget name="entries" position="%d,%d" size="%d,%d" scrollbarMode="showOnDemand" transparent="1" />
1230                         
1231                                         <ePixmap pixmap="skin_default/buttons/red.png"          position="%d,%d"        size="%d,%d" alphatest="on" />
1232                                         <ePixmap pixmap="skin_default/buttons/green.png"        position="%d,%d"        size="%d,%d" alphatest="on" />
1233                                         <ePixmap pixmap="skin_default/buttons/yellow.png"       position="%d,%d"        size="%d,%d" alphatest="on" />
1234                                         <ePixmap pixmap="skin_default/buttons/blue.png"         position="%d,%d"        size="%d,%d" alphatest="on" />
1235                                         <widget name="key_red"  position="%d,%d"        size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
1236                                         <widget name="key_green"        position="%d,%d"        size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
1237                                         <widget name="key_yellow"       position="%d,%d"        size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
1238                                         <widget name="key_blue"         position="%d,%d"        size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
1239                                         <ePixmap position="%d,%d" size="%d,%d" zPosition="2" pixmap="%s" transparent="1" alphatest="blend" />           
1240                                 </screen>""" % (
1241                                                         self.width, self.height, _("Phone calls"),
1242                                                         backMainLine,
1243                                                         scaleH(1130, XXX), scaleV(40, XXX), scaleH(80, XXX), scaleV(26, XXX), scaleV(26, XXX), # time
1244                                                         scaleH(900, XXX), scaleV(70, XXX), scaleH(310, XXX), scaleV(22, XXX), scaleV(20, XXX), # date
1245                                                         "FritzCall " + _("Phone calls"), scaleH(500, XXX), scaleV(63, XXX), scaleH(330, XXX), scaleV(30, XXX), scaleV(27, XXX), # eLabel
1246                                                         scaleH(80, XXX), scaleV(150, XXX), scaleH(280, XXX), scaleV(200, XXX), scaleV(22, XXX), # statusbar
1247                                                         scaleH(420, XXX), scaleV(120, XXX), scaleH(790, XXX), scaleV(438, XXX), # entries
1248                                                         scaleH(450, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # red
1249                                                         scaleH(640, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # green
1250                                                         scaleH(830, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # yellow
1251                                                         scaleH(1020, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # blue
1252                                                         scaleH(480, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # red
1253                                                         scaleH(670, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # green
1254                                                         scaleH(860, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # yellow
1255                                                         scaleH(1050, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # blue
1256                                                         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
1257                                                                                                                 )
1258                 else:
1259                         self.width = scaleH(1100, 570)
1260                         debug("[FritzDisplayCalls] width: " + str(self.width))
1261                         # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
1262                         self.skin = """
1263                                 <screen name="FritzDisplayCalls" position="%d,%d" size="%d,%d" title="%s" >
1264                                         <eLabel position="0,0" size="%d,2" backgroundColor="#aaaaaa" />
1265                                         <widget name="statusbar" position="%d,%d" size="%d,%d" font="Regular;%d" backgroundColor="#aaaaaa" transparent="1" />
1266                                         <eLabel position="0,%d" size="%d,2" backgroundColor="#aaaaaa" />
1267                                         <widget name="entries" position="%d,%d" size="%d,%d" scrollbarMode="showOnDemand" />
1268                                         <eLabel position="0,%d" size="%d,2" backgroundColor="#aaaaaa" />
1269                                         <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
1270                                         <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
1271                                         <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
1272                                         <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
1273                                         <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" />
1274                                         <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" />
1275                                         <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" />
1276                                         <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" />
1277                                 </screen>""" % (
1278                                                         scaleH(90, 75), scaleV(100, 78), # position 
1279                                                         scaleH(1100, 570), scaleV(560, 430), # size
1280                                                         _("Phone calls"),
1281                                                         scaleH(1100, 570), # eLabel width
1282                                                         scaleH(40, 5), scaleV(10, 5), # statusbar position
1283                                                         scaleH(1050, 560), scaleV(25, 22), # statusbar size
1284                                                         scaleV(22, 21), # statusbar font size
1285                                                         scaleV(40, 28), # eLabel position vertical
1286                                                         scaleH(1100, 570), # eLabel width
1287                                                         scaleH(40, 5), scaleV(55, 40), # entries position
1288                                                         scaleH(1040, 560), scaleV(458, 340), # entries size
1289                                                         scaleV(518, 390), # eLabel position vertical
1290                                                         scaleH(1100, 570), # eLabel width
1291                                                         scaleH(20, 5), scaleV(525, 395), # widget red
1292                                                         scaleH(290, 145), scaleV(525, 395), # widget green
1293                                                         scaleH(560, 285), scaleV(525, 395), # widget yellow
1294                                                         scaleH(830, 425), scaleV(525, 395), # widget blue
1295                                                         scaleH(20, 5), scaleV(525, 395), scaleV(22, 21), # widget red
1296                                                         scaleH(290, 145), scaleV(525, 395), scaleV(22, 21), # widget green
1297                                                         scaleH(560, 285), scaleV(525, 395), scaleV(22, 21), # widget yellow
1298                                                         scaleH(830, 425), scaleV(525, 395), scaleV(22, 21), # widget blue
1299                                                                                                                 )
1300
1301                 Screen.__init__(self, session)
1302                 HelpableScreen.__init__(self)
1303
1304                 # TRANSLATORS: keep it short, this is a button
1305                 self["key_yellow"] = Button(_("All"))
1306                 # TRANSLATORS: keep it short, this is a button
1307                 self["key_red"] = Button(_("Missed"))
1308                 # TRANSLATORS: keep it short, this is a button
1309                 self["key_blue"] = Button(_("Incoming"))
1310                 # TRANSLATORS: keep it short, this is a button
1311                 self["key_green"] = Button(_("Outgoing"))
1312
1313                 self["setupActions"] = ActionMap(["OkCancelActions", "ColorActions"],
1314                 {
1315                         "yellow": (lambda: self.display(FBF_ALL_CALLS)),
1316                         "red": (lambda: self.display(FBF_MISSED_CALLS)),
1317                         "blue": (lambda: self.display(FBF_IN_CALLS)),
1318                         "green": (lambda: self.display(FBF_OUT_CALLS)),
1319                         "cancel": self.ok,
1320                         "ok": self.showEntry, }, - 2)
1321
1322                 # TRANSLATORS: keep it short, this is a help text
1323                 self.helpList.append((self["setupActions"], "OkCancelActions", [("ok", _("Show details of entry"))]))
1324                 # TRANSLATORS: keep it short, this is a help text
1325                 self.helpList.append((self["setupActions"], "OkCancelActions", [("cancel", _("Quit"))]))
1326                 # TRANSLATORS: keep it short, this is a help text
1327                 self.helpList.append((self["setupActions"], "ColorActions", [("yellow", _("Display all calls"))]))
1328                 # TRANSLATORS: keep it short, this is a help text
1329                 self.helpList.append((self["setupActions"], "ColorActions", [("red", _("Display missed calls"))]))
1330                 # TRANSLATORS: keep it short, this is a help text
1331                 self.helpList.append((self["setupActions"], "ColorActions", [("blue", _("Display incoming calls"))]))
1332                 # TRANSLATORS: keep it short, this is a help text
1333                 self.helpList.append((self["setupActions"], "ColorActions", [("green", _("Display outgoing calls"))]))
1334
1335                 self["statusbar"] = Label(_("Getting calls from FRITZ!Box..."))
1336                 self["entries"] = MenuList([], True, content=eListboxPythonMultiContent)
1337                 fontSize = scaleV(22, 18)
1338                 fontHeight = scaleV(24, 20)
1339                 self["entries"].l.setFont(0, gFont("Regular", fontSize))
1340                 self["entries"].l.setItemHeight(fontHeight)
1341
1342                 debug("[FritzDisplayCalls] init: '''%s'''" % config.plugins.FritzCall.fbfCalls.value)
1343                 self.display()
1344
1345         def ok(self):
1346                 self.close()
1347
1348         def display(self, which=config.plugins.FritzCall.fbfCalls.value):
1349                 debug("[FritzDisplayCalls] display")
1350                 config.plugins.FritzCall.fbfCalls.value = which
1351                 config.plugins.FritzCall.fbfCalls.save()
1352                 self.header = fbfCallsChoices[which]
1353                 fritzbox.getCalls(self, self.gotCalls, which)
1354
1355         def gotCalls(self, callList):
1356                 debug("[FritzDisplayCalls] gotCalls")
1357                 self.updateStatus(self.header + " (" + str(len(callList)) + ")")
1358                 sortlist = []
1359                 for (number, date, remote, direct, here, length) in callList:
1360                         found = re.match("(\d\d.\d\d.)\d\d( \d\d:\d\d)", date)
1361                         if found: date = found.group(1) + found.group(2)
1362                         if direct == FBF_OUT_CALLS:
1363                                 dir = LoadPixmap(resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/images/callout.png"))
1364                         elif direct == FBF_IN_CALLS:
1365                                 dir = LoadPixmap(resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/images/callin.png"))
1366                         else:
1367                                 dir = LoadPixmap(resolveFilename(SCOPE_PLUGINS, "Extensions/FritzCall/images/callinfailed.png"))
1368                         dateFieldWidth = scaleH(150,100)
1369                         dirFieldWidth = 16
1370                         lengthFieldWidth = scaleH(75,50)
1371                         remoteFieldWidth = scaleH(250,100)
1372                         scrollbarWidth = scaleH(90,45)
1373                         fieldWidth = self.width -dateFieldWidth -5 -dirFieldWidth -5 -lengthFieldWidth -5 -remoteFieldWidth -scrollbarWidth -5
1374                         # debug("[FritzDisplayCalls] gotCalls: d: %d; f: %d; d: %d; r: %d" %(dateFieldWidth, fieldWidth, dirFieldWidth, remoteFieldWidth))
1375                         sortlist.append([number,
1376                                                          (eListboxPythonMultiContent.TYPE_TEXT, 0, 0, dateFieldWidth, scaleV(24,20), 0, RT_HALIGN_LEFT, date),
1377                                                          (eListboxPythonMultiContent.TYPE_PIXMAP_ALPHATEST, dateFieldWidth+5, 0, dirFieldWidth, 16, dir),
1378                                                          (eListboxPythonMultiContent.TYPE_TEXT, dateFieldWidth+5+dirFieldWidth+5, 0, fieldWidth, scaleV(24,20), 0, RT_HALIGN_LEFT, here),
1379                                                          (eListboxPythonMultiContent.TYPE_TEXT, dateFieldWidth+5+dirFieldWidth+5+fieldWidth+5, 0, lengthFieldWidth, scaleV(24,20), 0, RT_HALIGN_LEFT, length),
1380                                                          (eListboxPythonMultiContent.TYPE_TEXT, dateFieldWidth+5+dirFieldWidth+5+fieldWidth+5+lengthFieldWidth+5, 0, remoteFieldWidth, scaleV(24,20), 0, RT_HALIGN_RIGHT, remote)
1381                                                          ])
1382
1383                 self["entries"].setList(sortlist)
1384
1385         def updateStatus(self, text):
1386                 self["statusbar"].setText(_("Getting calls from FRITZ!Box...") + text)
1387
1388         def showEntry(self):
1389                 debug("[FritzDisplayCalls] showEntry")
1390                 cur = self["entries"].getCurrent()
1391                 if cur:
1392                         if cur[0]:
1393                                 debug("[FritzDisplayCalls] showEntry %s" % (cur[0]))
1394                                 number = cur[0]
1395                                 fullname = phonebook.search(cur[0])
1396                                 if fullname:
1397                                         # we have a name for this number
1398                                         name = fullname
1399                                         self.session.open(FritzOfferAction, self, number, name)
1400                                 else:
1401                                         # we don't
1402                                         self.session.open(FritzOfferAction, self, number)
1403                         else:
1404                                 # we do not even have a number...
1405                                 self.session.open(MessageBox,
1406                                                   _("UNKNOWN"),
1407                                                   type=MessageBox.TYPE_INFO)
1408
1409
1410 class FritzOfferAction(Screen):
1411
1412         def __init__(self, session, parent, number, name=""):
1413                 # TODO: scaling for HD
1414                 noButtons = 3
1415                 width = max(scaleH(440,440), noButtons*140)
1416                 height = scaleV(176,176) # = 5 + 126 + 40 + 5; 6 lines of text possible
1417                 buttonsGap = (width-noButtons*140)/(noButtons+1)
1418                 buttonsVPos = height-40-5
1419                 # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
1420                 self.skin = """
1421                         <screen name="FritzOfferAction" position="%d,%d" size="%d,%d" title="%s" >
1422                                 <widget name="text" position="5,5" size="%d,%d" font="Regular;%d" />
1423                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
1424                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
1425                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
1426                                 <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" />
1427                                 <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" />
1428                                 <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" />
1429                         </screen>""" % (
1430                                                         (DESKTOP_WIDTH - width) / 2,
1431                                                         (DESKTOP_HEIGHT - height) / 2,
1432                                                         width,
1433                                                         height,
1434                                                         _("Do what?"),
1435                                                         width - 10,
1436                                                         height - 10 - 40,
1437                                                         scaleH(21,21),
1438                                                         buttonsGap, buttonsVPos,
1439                                                         buttonsGap+140+buttonsGap, buttonsVPos,
1440                                                         buttonsGap+2*(140+buttonsGap), buttonsVPos,
1441                                                         buttonsGap, buttonsVPos,
1442                                                         buttonsGap+140+buttonsGap, buttonsVPos,
1443                                                         buttonsGap+2*(140+buttonsGap), buttonsVPos,
1444                                                         ) 
1445                 debug("[FritzOfferAction] init: %s, %s" %(number, name))
1446                 Screen.__init__(self, session)
1447         
1448                 # TRANSLATORS: keep it short, this is a button
1449                 self["key_red"] = Button(_("Lookup"))
1450                 # TRANSLATORS: keep it short, this is a button
1451                 self["key_green"] = Button(_("Call"))
1452                 # TRANSLATORS: keep it short, this is a button
1453                 self["key_yellow"] = Button(_("Save"))
1454                 # TRANSLATORS: keep it short, this is a button
1455                 # self["key_blue"] = Button(_("Search"))
1456
1457                 self["FritzOfferActions"] = ActionMap(["OkCancelActions", "ColorActions"],
1458                 {
1459                         "red": self.lookup,
1460                         "green": self.call,
1461                         "yellow": self.add,
1462                         "cancel": self.exit,
1463                         "ok": self.exit, }, - 2)
1464
1465                 self["text"] = Label(number + "\n\n" + name.replace(", ", "\n"))
1466                 self.actualNumber = number
1467                 self.actualName = name.replace("\n", ", ")
1468                 self.parent = parent
1469                 self.lookupState = 0
1470
1471         def lookup(self):
1472                 phonebookLocation = config.plugins.FritzCall.phonebookLocation.value
1473                 if self.lookupState == 0:
1474                         self.lookupState = 1
1475                         self["text"].setText(self.actualNumber + "\n\n" + _("Reverse searching..."))
1476                         ReverseLookupAndNotifier(self.actualNumber, self.lookedUp, "UTF-8", config.plugins.FritzCall.country.value)
1477                         return
1478                 if self.lookupState == 1 and os.path.exists(phonebookLocation + "/PhoneBook.csv"):
1479                         self["text"].setText(self.actualNumber + "\n\n" + _("Searching in Outlook export..."))
1480                         self.lookupState = 2
1481                         self.lookedUp(self.actualNumber, FritzOutlookCSV.findNumber(self.actualNumber, phonebookLocation + "/PhoneBook.csv")) #@UndefinedVariable
1482                         return
1483                 else:
1484                         self.lookupState = 2
1485                 if self.lookupState == 2 and os.path.exists(phonebookLocation + "/PhoneBook.ldif"):
1486                         self["text"].setText(self.actualNumber + "\n\n" + _("Searching in LDIF..."))
1487                         self.lookupState = 0
1488                         FritzLDIF.findNumber(self.actualNumber, open(phonebookLocation + "/PhoneBook.ldif"), self.lookedUp)
1489                         return
1490                 else:
1491                         self.lookupState = 0
1492                         self.lookup()
1493
1494         def lookedUp(self, number, name):
1495                 if not name:
1496                         if self.lookupState == 1:
1497                                 name = _("No result from reverse lookup")
1498                         elif self.lookupState == 2:
1499                                 name = _("No result from Outlook export")
1500                         else:
1501                                 name = _("No result from LDIF")
1502                 self.actualNumber = number
1503                 self.actualName = name
1504                 message = number + "\n\n" + name.replace(", ", "\n")
1505                 self["text"].setText(str(message))
1506
1507         def call(self):
1508                 debug("[FritzOfferAction] add: %s" %self.actualNumber)
1509                 fritzbox.dial(self.actualNumber)
1510                 self.exit()
1511
1512         def add(self):
1513                 debug("[FritzOfferAction] add: %s, %s" %(self.actualNumber, self.actualName))
1514                 phonebook.FritzDisplayPhonebook(self.session).add(self.parent, self.actualNumber, self.actualName)
1515                 self.exit()
1516
1517         def exit(self):
1518                 self.close()
1519
1520
1521 class FritzCallPhonebook:
1522         def __init__(self):
1523                 debug("[FritzCallPhonebook] init")
1524                 # Beware: strings in phonebook.phonebook have to be in utf-8!
1525                 self.phonebook = {}
1526                 self.reload()
1527
1528         def reload(self):
1529                 debug("[FritzCallPhonebook] reload")
1530                 # Beware: strings in phonebook.phonebook have to be in utf-8!
1531                 self.phonebook = {}
1532
1533                 if not config.plugins.FritzCall.enable.value:
1534                         return
1535
1536                 phonebookFilename = config.plugins.FritzCall.phonebookLocation.value + "/PhoneBook.txt"
1537                 if config.plugins.FritzCall.phonebook.value and os.path.exists(phonebookFilename):
1538                         debug("[FritzCallPhonebook] reload: read " + phonebookFilename)
1539                         phonebookTxtCorrupt = False
1540                         for line in open(phonebookFilename):
1541                                 try:
1542                                         # Beware: strings in phonebook.phonebook have to be in utf-8!
1543                                         line = line.decode("utf-8")
1544                                 except UnicodeDecodeError: # this is just for the case, somebody wrote latin1 chars into PhoneBook.txt
1545                                         try:
1546                                                 line = line.decode("iso-8859-1")
1547                                                 debug("[FritzCallPhonebook] Fallback to ISO-8859-1 in %s" % line)
1548                                                 phonebookTxtCorrupt = True
1549                                         except:
1550                                                 debug("[FritzCallPhonebook] Could not parse internal Phonebook Entry %s" % line)
1551                                                 phonebookTxtCorrupt = True
1552                                 line = line.encode("utf-8")
1553                                 if re.match("^\d+#.*$", line):
1554                                         try:
1555                                                 number, name = line.split("#")
1556                                                 if not self.phonebook.has_key(number):
1557                                                         # Beware: strings in phonebook.phonebook have to be in utf-8!
1558                                                         self.phonebook[number] = name
1559                                         except ValueError: # how could this possibly happen?!?!
1560                                                 debug("[FritzCallPhonebook] Could not parse internal Phonebook Entry %s" % line)
1561                                                 phonebookTxtCorrupt = True
1562                                 else:
1563                                         debug("[FritzCallPhonebook] Could not parse internal Phonebook Entry %s" % line)
1564                                         phonebookTxtCorrupt = True
1565
1566                         if phonebookTxtCorrupt:
1567                                 # dump phonebook to PhoneBook.txt
1568                                 debug("[FritzCallPhonebook] dump Phonebook.txt")
1569                                 os.rename(phonebookFilename, phonebookFilename + ".bck")
1570                                 fNew = open(phonebookFilename, 'w')
1571                                 # Beware: strings in phonebook.phonebook are utf-8!
1572                                 for (number, name) in self.phonebook.iteritems():
1573                                         # Beware: strings in PhoneBook.txt have to be in utf-8!
1574                                         fNew.write(number + "#" + name.encode("utf-8"))
1575                                 fNew.close()
1576
1577 #===============================================================================
1578 #               #
1579 #               # read entries from Outlook export
1580 #               #
1581 #               # not reliable with coding yet
1582 #               # 
1583 #               # import csv exported from Outlook 2007 with csv(Windows)
1584 #               csvFilename = "/tmp/PhoneBook.csv"
1585 #               if config.plugins.FritzCall.phonebook.value and os.path.exists(csvFilename):
1586 #                       try:
1587 #                               readOutlookCSV(csvFilename, self.add)
1588 #                               os.rename(csvFilename, csvFilename + ".done")
1589 #                       except ImportError:
1590 #                               debug("[FritzCallPhonebook] CSV import failed" %line)
1591 #===============================================================================
1592
1593                 
1594 #===============================================================================
1595 #               #
1596 #               # read entries from LDIF
1597 #               #
1598 #               # import ldif exported from Thunderbird 2.0.0.19
1599 #               ldifFilename = "/tmp/PhoneBook.ldif"
1600 #               if config.plugins.FritzCall.phonebook.value and os.path.exists(ldifFilename):
1601 #                       try:
1602 #                               parser = MyLDIF(open(ldifFilename), self.add)
1603 #                               parser.parse()
1604 #                               os.rename(ldifFilename, ldifFilename + ".done")
1605 #                       except ImportError:
1606 #                               debug("[FritzCallPhonebook] LDIF import failed" %line)
1607 #===============================================================================
1608                 
1609                 if config.plugins.FritzCall.fritzphonebook.value:
1610                         FritzCallFBF(False).loadFritzBoxPhonebook()
1611
1612                 if DESKTOP_WIDTH <> 1280 or DESKTOP_HEIGHT <> 720:
1613                         config.plugins.FritzCall.fullscreen.value = False
1614
1615         def search(self, number):
1616                 # debug("[FritzCallPhonebook] Searching for %s" %number
1617                 name = None
1618                 if config.plugins.FritzCall.phonebook.value or config.plugins.FritzCall.fritzphonebook.value:
1619                         if self.phonebook.has_key(number):
1620                                 name = self.phonebook[number].replace(", ", "\n").strip()
1621                 return name
1622
1623         def add(self, number, name):
1624                 '''
1625                 
1626                 @param number: number of entry
1627                 @param name: name of entry, has to be in utf-8
1628                 '''
1629                 debug("[FritzCallPhonebook] add")
1630                 name = name.replace("\n", ", ").replace('#','') # this is just for safety reasons. add should only be called with newlines converted into commas
1631                 self.remove(number)
1632                 self.phonebook[number] = name;
1633                 if number and number <> 0:
1634                         if config.plugins.FritzCall.phonebook.value:
1635                                 try:
1636                                         name = name.strip() + "\n"
1637                                         string = "%s#%s" % (number, name)
1638                                         # Beware: strings in PhoneBook.txt have to be in utf-8!
1639                                         f = open(config.plugins.FritzCall.phonebookLocation.value + "/PhoneBook.txt", 'a')
1640                                         f.write(string)
1641                                         f.close()
1642                                         debug("[FritzCallPhonebook] added %s with %s to Phonebook.txt" % (number, name.strip()))
1643                                         return True
1644         
1645                                 except IOError:
1646                                         return False
1647
1648         def remove(self, number):
1649                 if number in self.phonebook:
1650                         debug("[FritzCallPhonebook] remove entry in phonebook")
1651                         del self.phonebook[number]
1652                         if config.plugins.FritzCall.phonebook.value:
1653                                 try:
1654                                         phonebookFilename = config.plugins.FritzCall.phonebookLocation.value + "/PhoneBook.txt"
1655                                         debug("[FritzCallPhonebook] remove entry in Phonebook.txt")
1656                                         fOld = open(phonebookFilename, 'r')
1657                                         fNew = open(phonebookFilename + str(os.getpid()), 'w')
1658                                         line = fOld.readline()
1659                                         while (line):
1660                                                 if not re.match("^" + number + "#.*$", line):
1661                                                         fNew.write(line)
1662                                                 line = fOld.readline()
1663                                         fOld.close()
1664                                         fNew.close()
1665                                         os.remove(phonebookFilename)
1666                                         os.rename(phonebookFilename + str(os.getpid()), phonebookFilename)
1667                                         debug("[FritzCallPhonebook] removed %s from Phonebook.txt" % number)
1668                                         return True
1669         
1670                                 except IOError:
1671                                         pass
1672                 return False
1673
1674         class FritzDisplayPhonebook(Screen, HelpableScreen, NumericalTextInput):
1675         
1676                 def __init__(self, session):
1677                         if config.plugins.FritzCall.fullscreen.value:
1678                                 self.width = DESKTOP_WIDTH
1679                                 self.height = DESKTOP_HEIGHT
1680                                 backMainPng = ""
1681                                 if os.path.exists(resolveFilename(SCOPE_SKIN_IMAGE, DESKTOP_SKIN + "/menu/back-main.png")):
1682                                         backMainPng = DESKTOP_SKIN + "/menu/back-main.png"
1683                                 elif os.path.exists(resolveFilename(SCOPE_SKIN_IMAGE, "Kerni-HD1/menu/back-main.png")):
1684                                         backMainPng = "Kerni-HD1/menu/back-main.png"
1685                                 elif os.path.exists(resolveFilename(SCOPE_SKIN_IMAGE, "Kerni-HD1-picon/menu/back-main.png")):
1686                                         backMainPng = "Kerni-HD1-picon/menu/back-main.png"
1687                                 if backMainPng:
1688                                         backMainLine = """<ePixmap position="0,0" zPosition="-10" size="%d,%d" pixmap="%s" transparent="1" />""" % (self.width, self.height, backMainPng)
1689                                 else:
1690                                         backMainLine = ""
1691                                 debug("[FritzDisplayPhonebook] backMainLine: " + backMainLine)
1692                                         
1693                                 # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
1694                                 self.skin = """
1695                                         <screen name="FritzdisplayPhonebook" position="0,0" size="%d,%d" title="%s" flags="wfNoBorder">
1696                                                 %s
1697                                                 <widget source="global.CurrentTime" render="Label" position="%d,%d" size="%d,%d" font="Regular;%d" halign="right" backgroundColor="#0b67a2" transparent="1">
1698                                                         <convert type="ClockToText">Default</convert>
1699                                                 </widget>
1700                                                 <widget source="global.CurrentTime" render="Label" position="%d,%d" size="%d,%d" font="Regular;%d" halign="right" backgroundColor="#0b67a2" transparent="1">
1701                                                         <convert type="ClockToText">Date</convert>
1702                                                 </widget>
1703                                                 <eLabel text="%s" position="%d,%d" size="%d,%d" font="Regular;%d" halign="center" backgroundColor="#0b67a2" transparent="1"/>
1704                                 
1705                                                 <widget name="entries" position="%d,%d" size="%d,%d" scrollbarMode="showOnDemand" transparent="1" />
1706                                 
1707                                                 <ePixmap pixmap="skin_default/buttons/red.png"          position="%d,%d"        size="%d,%d" alphatest="on" />
1708                                                 <ePixmap pixmap="skin_default/buttons/green.png"        position="%d,%d"        size="%d,%d" alphatest="on" />
1709                                                 <ePixmap pixmap="skin_default/buttons/yellow.png"       position="%d,%d"        size="%d,%d" alphatest="on" />
1710                                                 <ePixmap pixmap="skin_default/buttons/blue.png"         position="%d,%d"        size="%d,%d" alphatest="on" />
1711                                                 <widget name="key_red"  position="%d,%d"        size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
1712                                                 <widget name="key_green"        position="%d,%d"        size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
1713                                                 <widget name="key_yellow"       position="%d,%d"        size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
1714                                                 <widget name="key_blue"         position="%d,%d"        size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
1715                                                 <ePixmap position="%d,%d" size="%d,%d" zPosition="2" pixmap="%s" transparent="1" alphatest="blend" />   
1716                                         </screen>""" % (
1717                                                                         self.width, self.height, _("Phonebook"),
1718                                                                         backMainLine,
1719                                                                         scaleH(1130, XXX), scaleV(40, XXX), scaleH(80, XXX), scaleV(26, XXX), scaleV(26, XXX), # time
1720                                                                         scaleH(900, XXX), scaleV(70, XXX), scaleH(310, XXX), scaleV(22, XXX), scaleV(20, XXX), # date
1721                                                                         "FritzCall " + _("Phonebook"), scaleH(80, XXX), scaleV(63, XXX), scaleH(300, XXX), scaleV(30, XXX), scaleV(27, XXX), # eLabel
1722                                                                         scaleH(420, XXX), scaleV(120, XXX), scaleH(790, XXX), scaleV(438, XXX), # entries
1723                                                                         scaleH(450, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # red
1724                                                                         scaleH(640, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # green
1725                                                                         scaleH(830, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # yellow
1726                                                                         scaleH(1020, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # blue
1727                                                                         scaleH(480, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # red
1728                                                                         scaleH(670, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # green
1729                                                                         scaleH(860, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # yellow
1730                                                                         scaleH(1050, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # blue
1731                                                                         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
1732                                                                                                                                 )
1733                         else:
1734                                 self.width = scaleH(1100, 570)
1735                                 debug("[FritzDisplayPhonebook] width: " + str(self.width))
1736                                 # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
1737                                 self.skin = """
1738                                         <screen name="FritzDisplayPhonebook" position="%d,%d" size="%d,%d" title="%s" >
1739                                                 <eLabel position="0,0" size="%d,2" backgroundColor="#aaaaaa" />
1740                                                 <widget name="entries" position="%d,%d" size="%d,%d" scrollbarMode="showOnDemand" />
1741                                                 <eLabel position="0,%d" size="%d,2" backgroundColor="#aaaaaa" />
1742                                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
1743                                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
1744                                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
1745                                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
1746                                                 <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" />
1747                                                 <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" />
1748                                                 <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" />
1749                                                 <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" />
1750                                         </screen>""" % (
1751                                                         scaleH(90, 75), scaleV(100, 73), # position 
1752                                                         scaleH(1100, 570), scaleV(560, 430), # size
1753                                                         _("Phonebook"),
1754                                                         scaleH(1100, 570), # eLabel width
1755                                                         scaleH(40, 5), scaleV(20, 5), # entries position
1756                                                         scaleH(1040, 560), scaleV(488, 380), # entries size
1757                                                         scaleV(518, 390), # eLabel position vertical
1758                                                         scaleH(1100, 570), # eLabel width
1759                                                         scaleH(20, 5), scaleV(525, 395), # ePixmap red
1760                                                         scaleH(290, 145), scaleV(525, 395), # ePixmap green
1761                                                         scaleH(560, 285), scaleV(525, 395), # ePixmap yellow
1762                                                         scaleH(830, 425), scaleV(525, 395), # ePixmap blue
1763                                                         scaleH(20, 5), scaleV(525, 395), scaleV(22, 21), # widget red
1764                                                         scaleH(290, 145), scaleV(525, 395), scaleV(22, 21), # widget green
1765                                                         scaleH(560, 285), scaleV(525, 395), scaleV(22, 21), # widget yellow
1766                                                         scaleH(830, 425), scaleV(525, 395), scaleV(22, 21), # widget blue
1767                                                         )
1768         
1769                         Screen.__init__(self, session)
1770                         NumericalTextInput.__init__(self)
1771                         HelpableScreen.__init__(self)
1772                 
1773                         # TRANSLATORS: keep it short, this is a button
1774                         self["key_red"] = Button(_("Delete"))
1775                         # TRANSLATORS: keep it short, this is a button
1776                         self["key_green"] = Button(_("New"))
1777                         # TRANSLATORS: keep it short, this is a button
1778                         self["key_yellow"] = Button(_("Edit"))
1779                         # TRANSLATORS: keep it short, this is a button
1780                         self["key_blue"] = Button(_("Search"))
1781         
1782                         self["setupActions"] = ActionMap(["OkCancelActions", "ColorActions"],
1783                         {
1784                                 "red": self.delete,
1785                                 "green": self.add,
1786                                 "yellow": self.edit,
1787                                 "blue": self.search,
1788                                 "cancel": self.exit,
1789                                 "ok": self.showEntry, }, - 2)
1790         
1791                         # TRANSLATORS: keep it short, this is a help text
1792                         self.helpList.append((self["setupActions"], "OkCancelActions", [("ok", _("Show details of entry"))]))
1793                         # TRANSLATORS: keep it short, this is a help text
1794                         self.helpList.append((self["setupActions"], "OkCancelActions", [("cancel", _("Quit"))]))
1795                         # TRANSLATORS: keep it short, this is a help text
1796                         self.helpList.append((self["setupActions"], "ColorActions", [("red", _("Delete entry"))]))
1797                         # TRANSLATORS: keep it short, this is a help text
1798                         self.helpList.append((self["setupActions"], "ColorActions", [("green", _("Add entry to phonebook"))]))
1799                         # TRANSLATORS: keep it short, this is a help text
1800                         self.helpList.append((self["setupActions"], "ColorActions", [("yellow", _("Edit selected entry"))]))
1801                         # TRANSLATORS: keep it short, this is a help text
1802                         self.helpList.append((self["setupActions"], "ColorActions", [("blue", _("Search (case insensitive)"))]))
1803         
1804                         self["entries"] = MenuList([], True, content=eListboxPythonMultiContent)
1805                         fontSize = scaleV(22, 18)
1806                         fontHeight = scaleV(24, 20)
1807                         self["entries"].l.setFont(0, gFont("Regular", fontSize))
1808                         self["entries"].l.setItemHeight(fontHeight)
1809                         debug("[FritzCallPhonebook] displayPhonebook init")
1810                         self.display()
1811         
1812                 def display(self, filter=""):
1813                         debug("[FritzCallPhonebook] displayPhonebook/display")
1814                         self.sortlist = []
1815                         # Beware: strings in phonebook.phonebook are utf-8!
1816                         sortlistHelp = sorted((name.lower(), name, number) for (number, name) in phonebook.phonebook.iteritems())
1817                         for (low, name, number) in sortlistHelp:
1818                                 if number == "01234567890":
1819                                         continue
1820                                 try:
1821                                         low = low.decode("utf-8")
1822                                 except (UnicodeDecodeError, UnicodeEncodeError):  # this should definitely not happen
1823                                         try:
1824                                                 low = low.decode("iso-8859-1")
1825                                         except:
1826                                                 debug("[FritzCallPhonebook] displayPhonebook/display: corrupt phonebook entry for %s" % number)
1827                                                 # self.session.open(MessageBox, _("Corrupt phonebook entry\nfor number %s\nDeleting.") %number, type = MessageBox.TYPE_ERROR)
1828                                                 phonebook.remove(number)
1829                                                 continue
1830                                 else:
1831                                         if filter:
1832                                                 filter = filter.lower()
1833                                                 if low.find(filter) == - 1:
1834                                                         continue
1835                                         name = name.strip().decode("utf-8")
1836                                         number = number.strip().decode("utf-8")
1837                                         found = re.match("([^,]*),.*", name)   # strip address information from the name part
1838                                         if found:
1839                                                 shortname = found.group(1)
1840                                         else:
1841                                                 shortname = name
1842                                         numberFieldWidth = scaleV(200,150)
1843                                         fieldWidth = self.width -5 -numberFieldWidth -10 -scaleH(90,45)
1844                                         number = number.encode("utf-8", "replace")
1845                                         name = name.encode("utf-8", "replace")
1846                                         shortname = shortname.encode('utf-8', 'replace')
1847                                         self.sortlist.append([(number,
1848                                                                    name),
1849                                                                    (eListboxPythonMultiContent.TYPE_TEXT, 0, 0, fieldWidth, scaleH(24,20), 0, RT_HALIGN_LEFT, shortname),
1850                                                                    (eListboxPythonMultiContent.TYPE_TEXT, fieldWidth +5, 0, numberFieldWidth, scaleH(24,20), 0, RT_HALIGN_LEFT, number)
1851                                                                    ])
1852                                 
1853                         self["entries"].setList(self.sortlist)
1854         
1855                 def showEntry(self):
1856                         cur = self["entries"].getCurrent()
1857                         if cur and cur[0]:
1858                                 debug("[FritzCallPhonebook] displayPhonebook/showEntry (%s,%s)" % (cur[0][0], cur[0][1]))
1859                                 number = cur[0][0]
1860                                 name = cur[0][1]
1861                                 self.session.open(FritzOfferAction, self, number, name)
1862         
1863                 def delete(self):
1864                         cur = self["entries"].getCurrent()
1865                         if cur and cur[0]:
1866                                 debug("[FritzCallPhonebook] displayPhonebook/delete " + cur[0][0])
1867                                 self.session.openWithCallback(
1868                                         self.deleteConfirmed,
1869                                         MessageBox,
1870                                         _("Do you really want to delete entry for\n\n%(number)s\n\n%(name)s?") 
1871                                         % { 'number':str(cur[0][0]), 'name':str(cur[0][1]).replace(", ", "\n") }
1872                                                                 )
1873                         else:
1874                                 self.session.open(MessageBox, _("No entry selected"), MessageBox.TYPE_INFO)
1875         
1876                 def deleteConfirmed(self, ret):
1877                         debug("[FritzCallPhonebook] displayPhonebook/deleteConfirmed")
1878                         #
1879                         # if ret: delete number from sortlist, delete number from phonebook.phonebook and write it to disk
1880                         #
1881                         cur = self["entries"].getCurrent()
1882                         if cur:
1883                                 if ret:
1884                                         # delete number from sortlist, delete number from phonebook.phonebook and write it to disk
1885                                         debug("[FritzCallPhonebook] displayPhonebook/deleteConfirmed: remove " + cur[0][0])
1886                                         phonebook.remove(cur[0][0])
1887                                         self.display()
1888                                 # else:
1889                                         # self.session.open(MessageBox, _("Not deleted."), MessageBox.TYPE_INFO)
1890                         else:
1891                                 self.session.open(MessageBox, _("No entry selected"), MessageBox.TYPE_INFO)
1892         
1893                 def add(self, parent=None, number="", name=""):
1894                         class addScreen(Screen, ConfigListScreen):
1895                                 '''ConfiglistScreen with two ConfigTexts for Name and Number'''
1896         
1897                                 def __init__(self, session, parent, number="", name=""):
1898                                         #
1899                                         # setup screen with two ConfigText and OK and ABORT button
1900                                         # 
1901                                         # TODO: scaling for HD
1902                                         noButtons = 2
1903                                         width = max(scaleH(570,570), noButtons*140)
1904                                         height = scaleV(100,100) # = 5 + 126 + 40 + 5; 6 lines of text possible
1905                                         buttonsGap = (width-noButtons*140)/(noButtons+1)
1906                                         buttonsVPos = height-40-5
1907                                         # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
1908                                         self.skin = """
1909                                                 <screen position="%d,%d" size="%d,%d" title="%s" >
1910                                                 <widget name="config" position="5,5" size="%d,%d" scrollbarMode="showOnDemand" />
1911                                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
1912                                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
1913                                                 <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" />
1914                                                 <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" />
1915                                                 </screen>""" % (
1916                                                                                 (DESKTOP_WIDTH - width) / 2,
1917                                                                                 (DESKTOP_HEIGHT - height) / 2,
1918                                                                                 width,
1919                                                                                 height,
1920                                                                                 _("Add entry to phonebook"),
1921                                                                                 width - 5 - 5,
1922                                                                                 height - 5 - 40 - 5,
1923                                                                                 buttonsGap, buttonsVPos,
1924                                                                                 buttonsGap+140+buttonsGap, buttonsVPos,
1925                                                                                 buttonsGap, buttonsVPos,
1926                                                                                 buttonsGap+140+buttonsGap, buttonsVPos,
1927                                                                                 )
1928                                         Screen.__init__(self, session)
1929                                         self.session = session
1930                                         self.parent = parent
1931                                         # TRANSLATORS: keep it short, this is a button
1932                                         self["key_red"] = Button(_("Cancel"))
1933                                         # TRANSLATORS: keep it short, this is a button
1934                                         self["key_green"] = Button(_("OK"))
1935                                         self["setupActions"] = ActionMap(["SetupActions", "ColorActions"],
1936                                         {
1937                                                 "cancel": self.cancel,
1938                                                 "red": self.cancel,
1939                                                 "green": self.add,
1940                                                 "ok": self.add,
1941                                         }, - 2)
1942         
1943                                         self.list = [ ]
1944                                         ConfigListScreen.__init__(self, self.list, session=session)
1945                                         config.plugins.FritzCall.name.value = name
1946                                         config.plugins.FritzCall.number.value = number
1947                                         self.list.append(getConfigListEntry(_("Name"), config.plugins.FritzCall.name))
1948                                         self.list.append(getConfigListEntry(_("Number"), config.plugins.FritzCall.number))
1949                                         self["config"].list = self.list
1950                                         self["config"].l.setList(self.list)
1951         
1952                                 def add(self):
1953                                         # get texts from Screen
1954                                         # add (number,name) to sortlist and phonebook.phonebook and disk
1955                                         self.number = config.plugins.FritzCall.number.value
1956                                         self.name = config.plugins.FritzCall.name.value
1957                                         if not self.number or not self.name:
1958                                                 self.session.open(MessageBox, _("Entry incomplete."), type=MessageBox.TYPE_ERROR)
1959                                                 return
1960                                         # add (number,name) to sortlist and phonebook.phonebook and disk
1961         #                                       oldname = phonebook.search(self.number)
1962         #                                       if oldname:
1963         #                                               self.session.openWithCallback(
1964         #                                                       self.overwriteConfirmed,
1965         #                                                       MessageBox,
1966         #                                                       _("Do you really want to overwrite entry for %(number)s\n\n%(name)s\n\nwith\n\n%(newname)s?")
1967         #                                                       % {
1968         #                                                       'number':self.number,
1969         #                                                       'name': oldname,
1970         #                                                       'newname':self.name.replace(", ","\n")
1971         #                                                       }
1972         #                                                       )
1973         #                                               self.close()
1974         #                                               return
1975                                         phonebook.add(self.number, self.name)
1976                                         self.close()
1977                                         self.parent.display()
1978         
1979                                 def overwriteConfirmed(self, ret):
1980                                         if ret:
1981                                                 phonebook.remove(self.number)
1982                                                 phonebook.add(self.number, self.name)
1983                                                 self.parent.display()
1984         
1985                                 def cancel(self):
1986                                         self.close()
1987         
1988                         debug("[FritzCallPhonebook] displayPhonebook/add")
1989                         if not parent:
1990                                 parent = self
1991                         self.session.open(addScreen, parent, number, name)
1992         
1993                 def edit(self):
1994                         debug("[FritzCallPhonebook] displayPhonebook/edit")
1995                         cur = self["entries"].getCurrent()
1996                         if cur is None:
1997                                 self.session.open(MessageBox, _("No entry selected"), MessageBox.TYPE_INFO)
1998                         else:
1999                                 (number, name) = cur[0]
2000                                 self.add(self, number, name)
2001         
2002                 def search(self):
2003                         debug("[FritzCallPhonebook] displayPhonebook/search")
2004                         self.help_window = self.session.instantiateDialog(NumericalTextInputHelpDialog, self)
2005                         self.help_window.show()
2006                         self.session.openWithCallback(self.doSearch, InputBox, _("Enter Search Terms"), _("Search phonebook"))
2007         
2008                 def doSearch(self, searchTerms):
2009                         if not searchTerms: searchTerms = ""
2010                         debug("[FritzCallPhonebook] displayPhonebook/doSearch: " + searchTerms)
2011                         if self.help_window:
2012                                 self.session.deleteDialog(self.help_window)
2013                                 self.help_window = None
2014                         self.display(searchTerms)
2015         
2016                 def exit(self):
2017                         self.close()
2018
2019
2020 class FritzCallSetup(Screen, ConfigListScreen, HelpableScreen):
2021
2022         def __init__(self, session, args=None):
2023                 if config.plugins.FritzCall.fullscreen.value:
2024                         self.width = DESKTOP_WIDTH
2025                         self.height = DESKTOP_HEIGHT
2026                         backMainPng = ""
2027                         backMainLine = ""
2028                         if os.path.exists(resolveFilename(SCOPE_SKIN_IMAGE, DESKTOP_SKIN + "/menu/back-main.png")):
2029                                 backMainPng = DESKTOP_SKIN + "/menu/back-main.png"
2030                         elif os.path.exists(resolveFilename(SCOPE_SKIN_IMAGE, "Kerni-HD1/menu/back-main.png")):
2031                                 backMainPng = "Kerni-HD1/menu/back-main.png"
2032                         elif os.path.exists(resolveFilename(SCOPE_SKIN_IMAGE, "Kerni-HD1-picon/menu/back-main.png")):
2033                                 backMainPng = "Kerni-HD1-picon/menu/back-main.png"
2034                         if backMainPng:
2035                                 backMainLine = """<ePixmap position="0,0" zPosition="-10" size="%d,%d" pixmap="%s" transparent="1" />""" % (self.width, self.height, backMainPng)
2036                         else:
2037                                 backMainLine = ""
2038                         debug("[FritzCallSetup] backMainLine: " + backMainLine)
2039                         # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
2040                         self.skin = """
2041                                 <screen name="FritzCallSetup" position="0,0" size="%d,%d" title="%s" flags="wfNoBorder">
2042                                         %s
2043                                         <widget source="global.CurrentTime" render="Label" position="%d,%d" size="%d,%d" font="Regular;%d" halign="right" backgroundColor="#0b67a2" transparent="1">
2044                                                 <convert type="ClockToText">Default</convert>
2045                                         </widget>
2046                                         <widget source="global.CurrentTime" render="Label" position="%d,%d" size="%d,%d" font="Regular;%d" halign="right" backgroundColor="#0b67a2" transparent="1">
2047                                                 <convert type="ClockToText">Date</convert>
2048                                         </widget>
2049                                         <eLabel text="%s" position="%d,%d" size="%d,%d" font="Regular;%d" halign="center" backgroundColor="#0b67a2" transparent="1"/>
2050                         
2051                                         <widget name="consideration" position="%d,%d"  size="%d,%d" font="Regular;%d" halign="center" backgroundColor="#353e575e" transparent="1" />
2052                                         <widget name="config" position="%d,%d" size="%d,%d" scrollbarMode="showOnDemand" transparent="1" />
2053                         
2054                                         <ePixmap pixmap="skin_default/buttons/red.png"          position="%d,%d"        size="%d,%d" alphatest="on" />
2055                                         <ePixmap pixmap="skin_default/buttons/green.png"        position="%d,%d"        size="%d,%d" alphatest="on" />
2056                                         <ePixmap pixmap="skin_default/buttons/yellow.png"       position="%d,%d"        size="%d,%d" alphatest="on" />
2057                                         <ePixmap pixmap="skin_default/buttons/blue.png"         position="%d,%d"        size="%d,%d" alphatest="on" />
2058                                         <ePixmap pixmap="skin_default/buttons/key_info.png"     position="%d,%d"        size="%d,%d" alphatest="on" />
2059                                         <ePixmap pixmap="skin_default/buttons/key_menu.png"     position="%d,%d"        size="%d,%d" alphatest="on" />
2060                                         <widget name="key_red" position="%d,%d"                 size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
2061                                         <widget name="key_green"  position="%d,%d"      size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
2062                                         <widget name="key_yellow" position="%d,%d"      size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
2063                                         <widget name="key_blue" position="%d,%d"        size="%d,%d" zPosition="1" font="Regular;%d" halign="left" backgroundColor="black" transparent="1" />
2064                                         <ePixmap position="%d,%d" size="%d,%d" zPosition="2" pixmap="%s" transparent="1" alphatest="blend" />           
2065                                 </screen>""" % (
2066                                                                 self.width, self.height, _("FritzCall Setup"),
2067                                                                 backMainLine,
2068                                                                 scaleH(1130, XXX), scaleV(40, XXX), scaleH(80, XXX), scaleV(26, XXX), scaleV(26, XXX), # time
2069                                                                 scaleH(900, XXX), scaleV(70, XXX), scaleH(310, XXX), scaleV(22, XXX), scaleV(20, XXX), # date
2070                                                                 _("FritzCall Setup"), scaleH(500, XXX), scaleV(63, XXX), scaleH(330, XXX), scaleV(30, XXX), scaleV(27, XXX), # eLabel
2071                                                                 scaleH(80, XXX), scaleV(150, XXX), scaleH(250, XXX), scaleV(200, XXX), scaleV(22, XXX), # consideration
2072                                                                 scaleH(420, XXX), scaleV(125, XXX), scaleH(790, XXX), scaleV(428, XXX), # config
2073                                                                 scaleH(150, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # red
2074                                                                 scaleH(350, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # green
2075                                                                 scaleH(550, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # yellow
2076                                                                 scaleH(750, XXX), scaleV(588, XXX), scaleH(21, XXX), scaleV(21, XXX), # blue
2077                                                                 scaleH(1050, XXX), scaleV(586, XXX), scaleH(35, XXX), scaleV(24, XXX), # info
2078                                                                 scaleH(1150, XXX), scaleV(586, XXX), scaleH(35, XXX), scaleV(24, XXX), # menu
2079                                                                 scaleH(175, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # red
2080                                                                 scaleH(375, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # green
2081                                                                 scaleH(575, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # yellow
2082                                                                 scaleH(775, XXX), scaleV(587, XXX), scaleH(160, XXX), scaleV(22, XXX), scaleV(20, XXX), # blue
2083                                                                 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
2084                                                                                                                                 ) 
2085                 else:
2086                         self.width = scaleH(20+4*(140+90)+2*(35+40)+20, 4*140+2*35)
2087                         width = self.width
2088                         debug("[FritzCallSetup] width: " + str(self.width))
2089                         # TRANSLATORS: this is a window title. Avoid the use of non ascii chars
2090                         self.skin = """
2091                                 <screen name="FritzCallSetup" position="%d,%d" size="%d,%d" title="%s" >
2092                                 <eLabel position="0,0" size="%d,2" backgroundColor="#aaaaaa" />
2093                                 <widget name="consideration" position="%d,%d" halign="center" size="%d,%d" font="Regular;%d" backgroundColor="#20040404" transparent="1" />
2094                                 <eLabel position="0,%d" size="%d,2" backgroundColor="#aaaaaa" />
2095                                 <widget name="config" position="%d,%d" size="%d,%d" scrollbarMode="showOnDemand" backgroundColor="#20040404" transparent="1" />
2096                                 <eLabel position="0,%d" size="%d,2" backgroundColor="#aaaaaa" />
2097                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
2098                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
2099                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
2100                                 <ePixmap position="%d,%d" zPosition="4" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
2101                                 <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" />
2102                                 <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" />
2103                                 <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" />
2104                                 <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" />
2105                                 <ePixmap position="%d,%d" zPosition="4" size="35,25" pixmap="skin_default/buttons/key_info.png" transparent="1" alphatest="on" />
2106                                 <ePixmap position="%d,%d" zPosition="4" size="35,25" pixmap="skin_default/buttons/key_menu.png" transparent="1" alphatest="on" />
2107                                 </screen>""" % (
2108                                                         (DESKTOP_WIDTH-width)/2, scaleV(100, 73), # position 
2109                                                         width, scaleV(560, 430), # size
2110                                                         _("FritzCall Setup") + 
2111                                                         " (" + "$Revision$"[1: - 1] + 
2112                                                         "$Date$"[7:23] + ")",
2113                                                         width, # eLabel width
2114                                                         scaleH(40, 20), scaleV(10, 5), # consideration position
2115                                                         scaleH(width-80, width-40), scaleV(25, 45), # consideration size
2116                                                         scaleV(22, 20), # consideration font size
2117                                                         scaleV(40, 50), # eLabel position vertical
2118                                                         width, # eLabel width
2119                                                         scaleH(40, 5), scaleV(60, 57), # config position
2120                                                         scaleH(width-80, width-10), scaleV(453, 328), # config size
2121                                                         scaleV(518, 390), # eLabel position vertical
2122                                                         width, # eLabel width
2123                                                         scaleH(20, 0), scaleV(525, 395), # pixmap red
2124                                                         scaleH(20+140+90, 140), scaleV(525, 395), # pixmap green
2125                                                         scaleH(20+2*(140+90), 2*140), scaleV(525, 395), # pixmap yellow
2126                                                         scaleH(20+3*(140+90), 3*140), scaleV(525, 395), # pixmap blue
2127                                                         scaleH(20, 0), scaleV(525, 395), scaleV(21, 21), # pixmap red
2128                                                         scaleH(20+(140+90), 140), scaleV(525, 395), scaleV(21, 21), # widget green
2129                                                         scaleH(20+2*(140+90), 2*140), scaleV(525, 395), scaleV(21, 21), # widget yellow
2130                                                         scaleH(20+3*(140+90), 3*140), scaleV(525, 395), scaleV(21, 21), # widget blue
2131                                                         scaleH(20+4*(140+90), 4*140), scaleV(532, 402), # button info
2132                                                         scaleH(20+4*(140+90)+(35+40), 4*140+35), scaleV(532, 402) # button menu
2133                                                                                                                 )
2134
2135                 Screen.__init__(self, session)
2136                 HelpableScreen.__init__(self)
2137                 self.session = session
2138
2139                 self["consideration"] = Label(_("You need to enable the monitoring on your FRITZ!Box by dialing #96*5*!"))
2140                 self.list = []
2141
2142                 # Initialize Buttons
2143                 # TRANSLATORS: keep it short, this is a button
2144                 self["key_red"] = Button(_("Cancel"))
2145                 # TRANSLATORS: keep it short, this is a button
2146                 self["key_green"] = Button(_("OK"))
2147                 # TRANSLATORS: keep it short, this is a button
2148                 self["key_yellow"] = Button(_("Phone calls"))
2149                 # TRANSLATORS: keep it short, this is a button
2150                 self["key_blue"] = Button(_("Phonebook"))
2151                 # TRANSLATORS: keep it short, this is a button
2152                 self["key_info"] = Button(_("About FritzCall"))
2153                 # TRANSLATORS: keep it short, this is a button
2154                 self["key_menu"] = Button(_("FRITZ!Box Fon Status"))
2155
2156                 self["setupActions"] = ActionMap(["ColorActions", "OkCancelActions", "MenuActions", "EPGSelectActions"],
2157                 {
2158                         "red": self.cancel,
2159                         "green": self.save,
2160                         "yellow": self.displayCalls,
2161                         "blue": self.displayPhonebook,
2162                         "cancel": self.cancel,
2163                         "ok": self.save,
2164                         "menu": self.menu,
2165                         "info": self.about,
2166                 }, - 2)
2167
2168                 # TRANSLATORS: keep it short, this is a help text
2169                 self.helpList.append((self["setupActions"], "ColorActions", [("red", _("quit"))]))
2170                 # TRANSLATORS: keep it short, this is a help text
2171                 self.helpList.append((self["setupActions"], "ColorActions", [("green", _("save and quit"))]))
2172                 # TRANSLATORS: keep it short, this is a help text
2173                 self.helpList.append((self["setupActions"], "ColorActions", [("yellow", _("display calls"))]))
2174                 # TRANSLATORS: keep it short, this is a help text
2175                 self.helpList.append((self["setupActions"], "ColorActions", [("blue", _("display phonebook"))]))
2176                 # TRANSLATORS: keep it short, this is a help text
2177                 self.helpList.append((self["setupActions"], "OkCancelActions", [("ok", _("save and quit"))]))
2178                 # TRANSLATORS: keep it short, this is a help text
2179                 self.helpList.append((self["setupActions"], "OkCancelActions", [("cancel", _("quit"))]))
2180                 # TRANSLATORS: keep it short, this is a help text
2181                 self.helpList.append((self["setupActions"], "MenuActions", [("menu", _("FRITZ!Box Fon Status"))]))
2182                 # TRANSLATORS: keep it short, this is a help text
2183                 self.helpList.append((self["setupActions"], "EPGSelectActions", [("info", _("About FritzCall"))]))
2184
2185                 ConfigListScreen.__init__(self, self.list, session=session)
2186                 self.createSetup()
2187
2188
2189         def keyLeft(self):
2190                 ConfigListScreen.keyLeft(self)
2191                 self.createSetup()
2192
2193         def keyRight(self):
2194                 ConfigListScreen.keyRight(self)
2195                 self.createSetup()
2196
2197         def createSetup(self):
2198                 self.list = [ ]
2199                 self.list.append(getConfigListEntry(_("Call monitoring"), config.plugins.FritzCall.enable))
2200                 if config.plugins.FritzCall.enable.value:
2201                         self.list.append(getConfigListEntry(_("Mute on call"), config.plugins.FritzCall.muteOnCall))
2202                         self.list.append(getConfigListEntry(_("FRITZ!Box FON address (Name or IP)"), config.plugins.FritzCall.hostname))
2203
2204                         self.list.append(getConfigListEntry(_("Show after Standby"), config.plugins.FritzCall.afterStandby))
2205
2206                         self.list.append(getConfigListEntry(_("Show Calls for specific MSN"), config.plugins.FritzCall.filter))
2207                         if config.plugins.FritzCall.filter.value:
2208                                 self.list.append(getConfigListEntry(_("MSN to show (separated by ,)"), config.plugins.FritzCall.filtermsn))
2209
2210                         self.list.append(getConfigListEntry(_("Show Outgoing Calls"), config.plugins.FritzCall.showOutgoing))
2211                         if config.plugins.FritzCall.showOutgoing.value:
2212                                 self.list.append(getConfigListEntry(_("Areacode to add to Outgoing Calls (if necessary)"), config.plugins.FritzCall.prefix))
2213                         self.list.append(getConfigListEntry(_("Timeout for Call Notifications (seconds)"), config.plugins.FritzCall.timeout))
2214                         self.list.append(getConfigListEntry(_("Reverse Lookup Caller ID (select country below)"), config.plugins.FritzCall.lookup))
2215                         if config.plugins.FritzCall.lookup.value:
2216                                 self.list.append(getConfigListEntry(_("Country"), config.plugins.FritzCall.country))
2217
2218                         self.list.append(getConfigListEntry(_("Password Accessing FRITZ!Box"), config.plugins.FritzCall.password))
2219                         self.list.append(getConfigListEntry(_("Extension number to initiate call on"), config.plugins.FritzCall.extension))
2220                         self.list.append(getConfigListEntry(_("Read PhoneBook from FRITZ!Box"), config.plugins.FritzCall.fritzphonebook))
2221                         if config.plugins.FritzCall.fritzphonebook.value:
2222                                 self.list.append(getConfigListEntry(_("Append type of number"), config.plugins.FritzCall.showType))
2223                                 self.list.append(getConfigListEntry(_("Append shortcut number"), config.plugins.FritzCall.showShortcut))
2224                                 self.list.append(getConfigListEntry(_("Append vanity name"), config.plugins.FritzCall.showVanity))
2225
2226                         self.list.append(getConfigListEntry(_("Use internal PhoneBook"), config.plugins.FritzCall.phonebook))
2227                         if config.plugins.FritzCall.phonebook.value:
2228                                 self.list.append(getConfigListEntry(_("PhoneBook Location"), config.plugins.FritzCall.phonebookLocation))
2229                                 if config.plugins.FritzCall.lookup.value:
2230                                         self.list.append(getConfigListEntry(_("Automatically add new Caller to PhoneBook"), config.plugins.FritzCall.addcallers))
2231
2232                         self.list.append(getConfigListEntry(_("Strip Leading 0"), config.plugins.FritzCall.internal))
2233                         # self.list.append(getConfigListEntry(_("Default display mode for FRITZ!Box calls"), config.plugins.FritzCall.fbfCalls))
2234                         if DESKTOP_WIDTH == 1280 and DESKTOP_HEIGHT == 720:
2235                                 self.list.append(getConfigListEntry(_("Full screen display"), config.plugins.FritzCall.fullscreen))
2236                         self.list.append(getConfigListEntry(_("Debug"), config.plugins.FritzCall.debug))
2237
2238                 self["config"].list = self.list
2239                 self["config"].l.setList(self.list)
2240
2241         def save(self):
2242                 global fritzbox
2243 #               debug("[FritzCallSetup] save"
2244                 for x in self["config"].list:
2245                         x[1].save()
2246                 if fritz_call:
2247                         fritz_call.connect()
2248                 self.close()
2249
2250         def cancel(self):
2251 #               debug("[FritzCallSetup] cancel"
2252                 for x in self["config"].list:
2253                         x[1].cancel()
2254                 self.close()
2255
2256         def displayCalls(self):
2257                 self.session.open(FritzDisplayCalls)
2258
2259         def displayPhonebook(self):
2260                 self.session.open(phonebook.FritzDisplayPhonebook)
2261
2262         def about(self):
2263                 self.session.open(FritzAbout)
2264
2265         def menu(self):
2266                 self.session.open(FritzMenu)
2267
2268 standbyMode = False
2269
2270 class FritzCallList:
2271         def __init__(self):
2272                 self.callList = [ ]
2273         
2274         def add(self, event, date, number, caller, phone):
2275                 debug("[FritzCallList] add")
2276                 if len(self.callList) > 10:
2277                         if self.callList[0] != "Start":
2278                                 self.callList[0] = "Start"
2279                         del self.callList[1]
2280
2281                 self.callList.append((event, number, date, caller, phone))
2282         
2283         def display(self):
2284                 debug("[FritzCallList] display")
2285                 global standbyMode
2286                 global my_global_session
2287                 standbyMode = False
2288                 # Standby.inStandby.onClose.remove(self.display) object does not exist anymore...
2289                 # build screen from call list
2290                 text = "\n"
2291                 if self.callList[0] == "Start":
2292                         text = text + _("Last 10 calls:\n")
2293                         del self.callList[0]
2294
2295                 for call in self.callList:
2296                         (event, number, date, caller, phone) = call
2297                         if event == "RING":
2298                                 direction = "->"
2299                         else:
2300                                 direction = "<-"
2301
2302                         # shorten the date info
2303                         found = re.match(".*(\d\d.\d\d.)\d\d( \d\d:\d\d)", date)
2304                         if found: date = found.group(1) + found.group(2)
2305
2306                         # our phone could be of the form "0123456789 (home)", then we only take "home"
2307                         found = re.match(".*\((.*)\)", phone)
2308                         if found: phone = found.group(1)
2309
2310                         #  if we have an unknown number, show the number
2311                         if caller == _("UNKNOWN") and number != "":
2312                                 caller = number
2313                         else:
2314                                 # strip off the address part of the remote number, if there is any
2315                                 found = re.match("(.*)\n.*", caller)
2316                                 if found: caller = found.group(1)
2317
2318                         while (len(caller) + len(phone)) > 40:
2319                                 if len(caller) > len(phone):
2320                                         caller = caller[: - 1]
2321                                 else:
2322                                         phone = phone[: - 1]
2323
2324                         text = text + "%s %s %s %s\n" % (date, caller, direction, phone)
2325
2326                 debug("[FritzCallList] display: '%s %s %s %s'" % (date, caller, direction, phone))
2327                 # display screen
2328                 Notifications.AddNotification(MessageBox, text, type=MessageBox.TYPE_INFO)
2329                 # my_global_session.open(FritzDisplayCalls, text) # TODO please HELP: from where can I get a session?
2330                 self.callList = [ ]
2331                 self.text = ""
2332
2333 callList = FritzCallList()
2334
2335 from GlobalActions import globalActionMap #@UnresolvedImport
2336 def notifyCall(event, date, number, caller, phone):
2337         if Standby.inStandby is None or config.plugins.FritzCall.afterStandby.value == "each":
2338                 if config.plugins.FritzCall.muteOnCall.value: