Merge branch 'master' into master_internal
[enigma2-plugins.git] / eibox / src / plugin.py
1 # -*- coding: UTF-8 -*-
2
3 from Components.ActionMap import ActionMap
4 from Components.Sensors import sensors
5 from Components.Sources.Sensor import SensorSource
6 from Components.Sources.StaticText import StaticText
7 from Components.Sources.Progress import Progress
8 from Components.ConfigList import ConfigListScreen
9 from Components.Pixmap import Pixmap,MultiPixmap
10 from Components.Label import MultiColorLabel
11 from Components.config import config, getConfigListEntry, ConfigSubsection, ConfigBoolean, ConfigOnOff, ConfigInteger, ConfigSlider, ConfigText, ConfigSelection, ConfigSelectionNumber, ConfigNothing, ConfigFloat, ConfigText
12 from Screens.Screen import Screen
13 from Screens.MessageBox import MessageBox
14 from Plugins.Plugin import PluginDescriptor
15 from Tools.Directories import fileExists, resolveFilename, SCOPE_PLUGINS
16 from Tools.LoadPixmap import LoadPixmap
17 from enigma import eTimer, eListbox, gFont, eListboxPythonMultiContent, \
18         RT_HALIGN_LEFT, RT_HALIGN_CENTER, RT_VALIGN_CENTER, RT_WRAP, eRect, eTimer
19
20 from socket import *
21 import xml.dom.minidom
22
23 from Components.Language import language
24 from os import environ as os_environ
25 import gettext
26
27 def localeInit():
28         lang = language.getLanguage()[:2] # getLanguage returns e.g. "fi_FI" for "language_country"
29         os_environ["LANGUAGE"] = lang # Enigma doesn't set this (or LC_ALL, LC_MESSAGES, LANG). gettext needs it!
30         gettext.bindtextdomain("EIBox", resolveFilename(SCOPE_PLUGINS, "Extensions/EIBox/locale"))
31
32 def _(txt):
33         t = gettext.dgettext("EIBox", txt)
34         if t == txt:
35                 t = gettext.gettext(txt)
36         return t
37
38 localeInit()
39 language.addCallback(localeInit)
40
41 config.eib = ConfigSubsection()
42 config.eib.xmlfile = ConfigText(default="design.xml")
43 config.eib.host = ConfigText(default="")
44 config.eib.port = ConfigInteger(default="1028")
45 config.eib.debug = ConfigBoolean(default=True)
46 config.eib.refresh = ConfigInteger(default="1000")
47
48 EIB_SWITCH, EIB_MULTISWITCH, EIB_DIMMER, EIB_GOTO, EIB_THERMO, EIB_TEXT = ("switch", "multi", "dimmer", "goto", "thermostat", "text")
49
50 file_prefix = resolveFilename(SCOPE_PLUGINS, 'Extensions/EIBox/')
51 img_prefix = file_prefix + 'images/'
52
53 up_down_descriptions = {False: _("up"), True: _("down")}
54 class ConfigUpDown(ConfigBoolean):
55         def __init__(self, default = False):
56                 ConfigBoolean.__init__(self, default = default, descriptions = up_down_descriptions)
57
58 goto_descriptions = {False: "", True: ""}
59 class ConfigGoto(ConfigBoolean):
60         def __init__(self, default = False):
61                 ConfigBoolean.__init__(self, default = default, descriptions = goto_descriptions)
62
63 class ConfigEIBText(ConfigText):
64         def __init__(self, default = "", fixed_size = True, visible_width = False):
65                 ConfigText.__init__(self, default, fixed_size, visible_width)
66         
67         def onSelect(self, session):
68                 self.allmarked = (self.value != "")
69
70 class EIBObject(object):
71         def __init__(self, order, object_id, object_type, label, position, img=None, custom_img=[], custom_values=[], textformat=None, readonly=False):
72                 self.order = order
73                 self.object_id = object_id
74                 self.object_type = object_type
75                 self.label = label
76                 self.position = position
77                 self.img = img
78                 self.custom_img = custom_img
79                 self.custom_values = custom_values
80                 self.textformat = textformat
81                 self.readonly = readonly
82                 self.config_element = None
83                 self.createConfigElement()
84                 self.multiswitch_dict = {}
85
86         def createConfigElement(self):
87                 if self.object_type == EIB_SWITCH and self.img in ("light", "outlet", "fan", "pump"):
88                         self.config_element = ConfigOnOff()
89                 elif self.object_type == EIB_SWITCH and self.img == "blinds":
90                         self.config_element = ConfigUpDown()
91                 elif self.object_type == EIB_SWITCH:
92                         self.config_element = ConfigBoolean()
93                 elif self.object_type == EIB_MULTISWITCH:
94                         choiceslist = []
95                         for choice in self.custom_values:
96                                 choiceslist.append(choice)
97                         self.config_element = ConfigSelection(choices=choiceslist)
98                 elif self.object_type == EIB_DIMMER:
99                         self.config_element = ConfigSelectionNumber(0,255,1)
100                         #self.config_element = ConfigSlider(increment = 10, limits=(0,255))
101                 elif self.object_type == EIB_THERMO:
102                         self.config_element = ConfigFloat(default=[0,0],limits=[(-31,+31),(0,99)])
103                 elif self.object_type == EIB_GOTO:
104                         self.config_element = ConfigGoto()
105                 elif self.object_type == EIB_TEXT:
106                         self.config_element = ConfigEIBText()
107                 else:
108                         print "[createConfigElement] couldn't create config_element for", self.getInfo()
109
110         def getValue(self):
111                 if self.config_element:
112                         if self.object_type == EIB_THERMO:
113                                 val = self.config_element.getValue()
114                                 return "%d.%d" % (val[0], val[1])
115                         else:
116                                 return self.config_element.getValue()
117
118         def getText(self):
119                 if self.config_element:
120                         if self.object_type == EIB_THERMO:
121                                 return str(self.value+"C")
122                         elif self.object_type == EIB_TEXT:
123                                 return str(self.textformat.replace("$1",str(self.value)))
124                         else:
125                                 return str(self.value)
126
127         def getPos(self, offset=[0,0]):
128                 x = self.position[0] - offset[0]
129                 y = self.position[1] - offset[1]
130                 return "%d,%d" % (x,y)
131
132         def setValue(self, val):
133                 if self.config_element:
134                         if self.object_type == EIB_SWITCH:
135                                 if val == "off" or val == "down" or val == 0 or val == False:
136                                         self.config_element.setValue(False)
137                                 if val == "on" or val == "up" or val == 1 or val == True:
138                                         self.config_element.setValue(True)
139                         else:
140                                 try:
141                                         if self.object_type == EIB_THERMO:
142                                                 val = val.split('.')
143                                                 if len(val) == 1:
144                                                         val.append(0)
145                                                 self.config_element.setValue([int(val[0]),int(val[1])])
146                                         elif self.object_type in (EIB_TEXT, EIB_MULTISWITCH) :
147                                                 self.config_element.setValue(str(val))
148                                         else:
149                                                 self.config_element.setValue(int(val))
150                                 except ValueError:
151                                         print "[setValue] Error setting", val, self.getInfo()
152                                         return
153                         if config.eib.debug.value:
154                                 print "[setValue]", self.object_id, ":=", val, "before:", self.config_element.getValue()
155                 else:
156                         print "[setValue] error: no config_element", self.getInfo()
157
158         def getInfo(self):
159                 return "[EIBOject] order=%d, id=%s, type=%s, label=%s, position=(%d,%d), img=%s, custom_img=%s, custom_values=%s, textformat=%s, readonly=%s, config_element=%s, value=%s" % (self.order, self.object_id, str(self.object_type), self.label, self.position[0], self.position[1], str(self.img), str(self.custom_img), str(self.custom_values), str(self.textformat), str(self.readonly), str(self.config_element), self.value)
160
161         def getKNXvalue(self):
162                 value = self.value
163                 if type(value) == bool and value == True:
164                         return "on"
165                 elif type(value) == bool and value == False:
166                         return "off"
167                 else:
168                         return str(value)
169
170         value = property(getValue, setValue)
171                 
172 class EIBObjects(object):
173         def __init__(self, zone_id, zone_name, zone_img):
174                 self.ids = {}
175                 self.cfg = {}
176                 self.zone_id = zone_id
177                 self.zone_name = zone_name
178                 self.zone_img = zone_img
179
180         def by_id (self, x): return self.ids[x]
181         def by_cfg(self, x): return self.cfg[x]
182
183         def append(self, x):
184                 self.ids[x.object_id] = x
185                 self.cfg[x.config_element] = x
186
187         def EIBwriteSingle(self, EIBObject):
188                 ret = True
189                 if not EIBObject.readonly:
190                         query = '<write><object id="%s" value="%s"/></write>\n\x04' % (EIBObject.object_id, EIBObject.getKNXvalue())
191                         ret = self.sendKNX(query)
192                 return ret
193
194         def EIBreadSingle(self, EIBObject):
195                 query = '<read><object id="%s" /></read>\n\x04' % EIBObject.object_id
196                 return self.sendKNX(query, self.parseSingleRead, EIBObject)
197
198         def EIBreadAll(self):
199                 persist_request_cmd = '<read><objects>'
200                 for EIBObject in self.ids.itervalues():
201                         if EIBObject.object_type != EIB_GOTO:
202                                 persist_request_cmd += '<object id="%s"/>' % EIBObject.object_id
203                 persist_request_cmd += '</objects></read>\n\x04'
204                 return self.sendKNX(persist_request_cmd, self.parseMultiRead)
205
206         def sendKNX(self, query, callback=None, user_args=None):
207                 try:
208                         knx = socket(AF_INET, SOCK_STREAM)
209                         host = config.eib.host.getValue()
210                         port = int(config.eib.port.getValue())
211                         knx.connect((host, port))
212                         knx.settimeout(2)
213                         ret = knx.send(query)
214                         if config.eib.debug.value:
215                                 print "[sendKNX]", query, ret
216                         
217                         knxdata = knx.recv(1024)
218                         while not knxdata.endswith('\n\x04'):
219                                 knxdata += knx.recv(1024)
220                         knx.close()
221                         if callback:
222                                 callback(knxdata[:-1], user_args)
223                         return True
224                 except timeout:
225                         print ("[sendKNX] socket timeout with linknx server %s:%d") % (host, port)
226                 except error:
227                         print ("[sendKNX] can't connect to linknx server %s:%d") % (host, port)
228                 return False
229
230         def parseSingleRead(self, knxdata, EIBObject):
231                 if config.eib.debug.value:
232                         print "[parseSingleRead]", knxdata
233                 try:
234                         dom = xml.dom.minidom.parseString(knxdata)
235                         if dom.childNodes[0].getAttribute("status") == "success":
236                                 subnode = dom.childNodes[0].childNodes[0]
237                                 if subnode.nodeType == xml.dom.minidom.Text.nodeType:
238                                         value = subnode.nodeValue
239                                         if config.eib.debug.value:
240                                                 print "[parseSingleRead] value=", value
241                                         try:
242                                                 EIBObject.value = str(value)
243                                         except KeyError:
244                                                 print "[parseSingleRead] KeyError exception"
245                                         return
246                         print ("[parseSingleRead] XML parser error parseSingleRead failed")
247                 except xml.parsers.expat.ExpatError, ValueError:
248                         print ("[parseSingleRead] XML parser error parseSingleRead DOM error")
249
250         def parseMultiRead(self, knxdata, user_args):
251                 if config.eib.debug.value:
252                         print "[parseMultiRead]", knxdata
253                 try:
254                         dom = xml.dom.minidom.parseString(knxdata)
255                         for node in dom.childNodes[0].childNodes:
256                                 if node.nodeType == xml.dom.minidom.Element.nodeType:
257                                     if node.tagName == 'objects':
258                                         for subnode in node.childNodes:
259                                             if subnode.nodeType == xml.dom.minidom.Element.nodeType:
260                                                 if subnode.tagName == 'object':
261                                                     i = 0
262                                                     object_id = None
263                                                     value = None
264                                                     while i < subnode.attributes.length:
265                                                       item = subnode.attributes.item(i)
266                                                       key = item.name.encode("utf-8")
267                                                       if key == "id":
268                                                         object_id = item.nodeValue
269                                                       elif key == "value":
270                                                         value = item.nodeValue
271                                                       i += 1
272                                                     if object_id and value != None and self.ids.has_key(object_id):
273                                                           EIBObject = self.ids[object_id]
274                                                           EIBObject.value = value
275                                                           if config.eib.debug.value:
276                                                                 print "[parseMultiRead]", EIBObject.object_id, " := ", EIBObject.value
277                                                     elif config.eib.debug.value:
278                                                           print "[parseMultiRead] couldn't parse persistence object", object_id, value
279                 except xml.parsers.expat.ExpatError:
280                         print ("[parseMultiRead] XML parser error") 
281
282         def __iter__ (self):
283                 list = self.ids.itervalues()
284                 return iter(sorted(list, key=lambda EIBObject: EIBObject.order))
285                 
286 class EIBoxZoneScreen(Screen, ConfigListScreen):
287
288         def __init__(self, session, EIB_objects):
289                 skin = """
290                 <screen position="center,center" size="550,450" title="E.I.B.ox" >
291                         <widget name="config" position="10,420" size="530,26" zPosition="1" transparent="1" scrollbarMode="showNever" />
292                         <ePixmap pixmap="%s" position="0,0" size="550,400" zPosition="-1" alphatest="on" />\n""" % (img_prefix+EIB_objects.zone_img)
293
294                 offset = [12, 10] # fix up browser css spacing
295                 iconsize = [32, 32]
296                 
297                 self.setup_title = "E.I.B.ox"
298
299                 self.EIB_objects = EIB_objects
300                 for EIB_object in self.EIB_objects:
301                         if EIB_object.object_type == EIB_GOTO:
302                                 pixmap_src = (img_prefix + 'goto' + EIB_object.img.capitalize() + '.png')
303                                 skin += '\t\t\t<widget name="%s" pixmap="%s" position="%s" size="32,32" transparent="1" alphatest="on" borderColor="#004679" zPosition="1" />\n' % (EIB_object.object_id, pixmap_src, EIB_object.getPos(offset))
304                                 self[EIB_object.object_id] = Pixmap()
305
306                         elif EIB_object.object_type in (EIB_SWITCH, EIB_MULTISWITCH, EIB_DIMMER):
307                                 if EIB_object.object_type == EIB_DIMMER or EIB_object.img == "light":
308                                         pixmaps_sources = ['light_off.png','light_on.png']
309                                 elif EIB_object.img == "blinds":
310                                         pixmaps_sources = ['blinds_up.png','blinds_down.png']
311                                 elif EIB_object.img == "outlet":
312                                         pixmaps_sources = ['outlet_off.png','outlet_on.png']
313                                 elif EIB_object.img == "fan":
314                                         pixmaps_sources = ['fan_off.png','fan_on.png']
315                                 elif EIB_object.img == "pump":
316                                         pixmaps_sources = ['pump_off.png','pump_on.png']
317                                 else:
318                                         pixmaps_sources = list(EIB_object.custom_img)
319
320                                 for idx, filename in enumerate(pixmaps_sources):
321                                           pixmaps_sources[idx] = img_prefix+filename
322                                 pixmaps_string = ','.join(pixmaps_sources)
323                                 skin += '\t\t\t<widget name="%s" pixmaps="%s" position="%s" size="32,32" transparent="1" alphatest="on" borderColor="#004679" zPosition="1" />\n' % (EIB_object.object_id, pixmaps_string, EIB_object.getPos(offset))
324                                 self[EIB_object.object_id] = MultiPixmap()
325
326                                 if EIB_object.object_type == EIB_DIMMER:
327                                         skin += '\t\t\t<widget source="%s_progress" render="Progress" pixmap="skin_default/progress_small.png" position="%s" size="32,5" backgroundColor="#4f74BB" zPosition="1" />\n' % (EIB_object.object_id, EIB_object.getPos([offset[0],offset[1]-iconsize[1]]))
328                                         self[EIB_object.object_id+"_progress"] = Progress()
329                                         self[EIB_object.object_id+"_progress"].range = 255
330                         
331                         elif EIB_object.object_type in (EIB_THERMO, EIB_TEXT):
332                                 skin += '\t\t\t<widget name="%s" position="%s" size="120,20" font="Regular;14" halign="left" valign="center" foregroundColors="#000000,#0000FF" transparent="1" zPosition="1" />\n' % (EIB_object.object_id, EIB_object.getPos(offset))
333                                 self[EIB_object.object_id] = MultiColorLabel()
334                 skin += """
335                 </screen>"""
336                 if config.eib.debug.value:
337                         print skin
338
339                 self.skin = skin
340                 Screen.__init__(self, session)
341                 self.initConfigList()
342                 ConfigListScreen.__init__(self, self.list, session = self.session, on_change = self.changedEntry)
343                 self.onChangedEntry = [ ]
344
345                 self["actions"] = ActionMap(["SetupActions", "OkCancelActions", "ColorActions", "DirectionActions"], 
346                 {
347                         "up": self.keyUp,
348                         "upUp": self.keyPass,
349                         "upRepeated": self.keyUp,
350                         "down": self.keyDown,
351                         "downUp": self.keyPass,
352                         "downRepeated": self.keyDown,
353                         "leftRepeated": self.keyLeftRepeated,
354                         "rightRepeated": self.keyRightRepeated,
355                         "cancel": self.keyCancel,
356                         "red": self.keyCancel,
357                         "green": self.keyOk,
358                         "ok": self.keyOk
359                 }, -2)
360
361                 self.onLayoutFinish.append(self.layoutFinished)
362
363         def handleInputHelpers(self):
364                 pass
365         
366         def keyPass(self):
367                 pass
368
369         def keyUp(self):
370                 self.moveBorder(self["config"].instance.moveUp)
371
372         def keyDown(self):
373                 self.moveBorder(self["config"].instance.moveDown)
374
375         def keyOk(self):
376                 EIB_object = self.getCurrentObj()
377                 if EIB_object and EIB_object.object_type == EIB_DIMMER:
378                         if EIB_object.value < 128:
379                                 EIB_object.value = 255
380                         else:
381                                 EIB_object.value = 0
382                         self.changedEntry()
383                         self["config"].invalidateCurrent()
384                 else:
385                         self.keyRight()
386
387         def keyRight(self):
388                 ConfigListScreen.keyRight(self)
389
390         def keyRightRepeated(self):
391                 EIB_object = self.getCurrentObj()
392                 if EIB_object and EIB_object.object_type == EIB_DIMMER:
393                         value = EIB_object.getValue()
394                         if value < 255-15:
395                                 EIB_object.value += 15
396                         else:
397                                 EIB_object.value = 255
398                         self.changedEntry()
399                         self["config"].invalidateCurrent()
400
401         def keyLeftRepeated(self):
402                 EIB_object = self.getCurrentObj()
403                 if EIB_object and EIB_object.object_type == EIB_DIMMER:
404                         value = EIB_object.getValue()
405                         if value > 15:
406                                 EIB_object.value -= 15
407                         else:
408                                 EIB_object.value = 0
409                         self.changedEntry()
410                         self["config"].invalidateCurrent()
411
412         def keyCancel(self):
413                 self.exit()
414
415         def layoutFinished(self):
416                 self.moveBorder()
417                 self.refreshObjects()
418                 self.refresh_timer = eTimer()
419                 self.refresh_timer_conn = self.refresh_timer.timeout.connect(self.refreshObjects)
420                 interval = config.eib.refresh.value
421                 if interval >= 500:
422                         self.refresh_timer.start(interval)
423
424         def getCurrentObj(self):
425                 current = self["config"].getCurrent()
426                 if current:
427                         return self.EIB_objects.by_cfg(current[1])
428                 else:
429                         return None
430
431         def moveBorder(self, direction=None):
432                 if direction != None:
433                         for EIB_object in self.EIB_objects:
434                                 if EIB_object.object_type in (EIB_SWITCH, EIB_DIMMER, EIB_GOTO, EIB_MULTISWITCH):
435                                         self[EIB_object.object_id].instance.setBorderWidth(0)
436                                 elif EIB_object.object_type in (EIB_THERMO, EIB_TEXT):
437                                         self[EIB_object.object_id].setForegroundColorNum(0)
438                         self["config"].instance.moveSelection(direction)
439                 current = self["config"].getCurrent()
440                 if current:
441                         EIB_object = self.EIB_objects.by_cfg(current[1])
442                         if EIB_object.object_type in (EIB_SWITCH, EIB_DIMMER, EIB_GOTO, EIB_MULTISWITCH):
443                                 self[EIB_object.object_id].instance.setBorderWidth(5)
444                         elif EIB_object.object_type in (EIB_THERMO, EIB_TEXT):
445                                         self[EIB_object.object_id].setForegroundColorNum(1)
446
447         def refreshObjects(self):
448                 status = self.EIB_objects.EIBreadAll()
449                 for EIB_object in self.EIB_objects:
450                         self.updateObject(EIB_object)
451                 self.setWindowTitle(status)
452
453         def setWindowTitle(self, status):
454                 self.setup_title = "E.I.B.ox %s " % self.EIB_objects.zone_name
455                 if status == True:
456                         self.setup_title += _("(online)")
457                 else:
458                         self.setup_title += _("(offline!)")
459                 self.setTitle(self.setup_title)
460
461         def updateObject(self, EIB_object):
462                 if EIB_object.object_type in (EIB_THERMO, EIB_TEXT):
463                         self.updateLabel(EIB_object)
464                 else:
465                         self.updateIcon(EIB_object)
466
467         def updateLabel(self, EIB_object):
468                 if config.eib.debug.value:
469                         print "[refreshObjects]", EIB_object.getInfo(), EIB_object.getText()
470                 self[EIB_object.object_id].setText(EIB_object.getText())
471
472         def updateIcon(self, EIB_object):
473                 if config.eib.debug.value:
474                         print "[updateIcon]", EIB_object.getInfo()
475                 if EIB_object.object_type == EIB_MULTISWITCH:
476                         if EIB_object.value in EIB_object.custom_values:
477                                 idx = int(EIB_object.custom_values.index(EIB_object.value))
478                                 if len(EIB_object.custom_img) > idx:
479                                         self[EIB_object.object_id].setPixmapNum(idx)
480                         return
481                 if EIB_object.object_type not in (EIB_SWITCH, EIB_DIMMER):
482                         return
483                 if type(EIB_object.value) == bool or EIB_object.value == 0:
484                         self[EIB_object.object_id].setPixmapNum(int(EIB_object.value))
485                 elif type(EIB_object.value) == int and EIB_object.value > 0:
486                         self[EIB_object.object_id].setPixmapNum(1)
487                 if EIB_object.object_type == EIB_DIMMER:
488                         self[EIB_object.object_id+"_progress"].value = EIB_object.value
489
490         def initConfigList(self):
491                 self.list = []
492                 for EIB_object in self.EIB_objects:
493                         self.list.append(getConfigListEntry(EIB_object.label, EIB_object.config_element))
494         
495         def changedEntry(self):
496                 current = self["config"].getCurrent()
497                 if current:
498                         EIB_object = self.EIB_objects.by_cfg(current[1])
499                         if EIB_object.object_type == EIB_GOTO:
500                                 self.exit(EIB_object.object_id)
501                         else:
502                                 if not EIB_object.readonly:
503                                         self.EIB_objects.EIBwriteSingle(EIB_object)
504                                 status = self.EIB_objects.EIBreadSingle(EIB_object)
505                                 self.updateObject(EIB_object)
506                                 self.setWindowTitle(status)
507                 for summaryWatcher in self.onChangedEntry:
508                         summaryWatcher()
509
510         def getCurrentEntry(self):
511                 return str(self["config"].getCurrent()[0])
512
513         def getCurrentValue(self):
514                 return str(self["config"].getCurrent()[1].getText())
515
516         def createSummary(self):
517                 from Screens.Setup import SetupSummary
518                 return SetupSummary
519
520         def exit(self, gotoZone=None):
521                 self.refresh_timer_conn = None
522                 self.close(gotoZone)
523
524 class EIBox(Screen, ConfigListScreen):
525         skin = """
526                 <screen position="center,center" size="570,420" title="E.I.B.ox" >
527                 </screen>"""
528
529         def __init__(self, session, args = None):               
530                 Screen.__init__(self, session)
531
532                 self["actions"] = ActionMap(["OkCancelActions", "ColorActions", "DirectionActions"], 
533                 {
534                         "cancel": self.close,
535                         "red": self.close
536                 }, -1)
537                 
538                 self.gotoZone = None
539                 self.EIB_zones = {}
540                 self.onShown.append(self.onFirstShown)
541
542         def ZoneScreenCB(self, gotoZone=None):
543                 if not gotoZone:
544                         self.close()
545                 else:
546                         self.gotoZone = gotoZone
547                         self.displayZone()
548
549         def onFirstShown(self):
550                 self.onShown.remove(self.onFirstShown)
551                 self.loadXML(resolveFilename(SCOPE_PLUGINS, file_prefix+config.eib.xmlfile.value))              
552                 self.displayZone()
553
554         def displayZone(self):
555                 if self.gotoZone in self.EIB_zones:
556                         self.session.openWithCallback(self.ZoneScreenCB, EIBoxZoneScreen, self.EIB_zones[self.gotoZone])
557         
558         def errorOut(self, message):
559                 self.session.openWithCallback(self.close, MessageBox, message, type = MessageBox.TYPE_ERROR)
560
561         def loadXML(self, filename):
562                 try:
563                         if not fileExists(filename):
564                                 self.errorOut("[loadXML] " + str(filename) + ' ' + _("not found"))
565                                 return
566                                 #raise AttributeError
567                         file = open(filename, "r")
568                         data = file.read().decode("utf-8").replace('&',"&amp;").encode("ascii",'xmlcharrefreplace')
569                         file.close()
570                         projectfiledom = xml.dom.minidom.parseString(data)
571                         for node in projectfiledom.childNodes[0].childNodes:
572                           if node.nodeType == xml.dom.minidom.Element.nodeType:
573                             if node.tagName == 'zones':
574                               for subnode in node.childNodes:
575                                 if subnode.nodeType == xml.dom.minidom.Element.nodeType:
576                                   if subnode.tagName == 'zone':
577                                     zone_id = str(subnode.getAttribute("id"))
578                                     zone_img = str(subnode.getAttribute("img"))
579                                     zone_name = str(subnode.getAttribute("name"))
580                                     filename = img_prefix + zone_img
581                                     if not zone_img or not fileExists(filename):
582                                         print "[loadXML] ", filename, " not found! using default image"
583                                         zone_img = "default_bg.png"
584                                     self.EIB_zones[zone_id] = EIBObjects(zone_id, zone_name, zone_img)
585                                     if config.eib.debug.value:
586                                         print "[loadXML] new EIB_zone", zone_id, zone_name, zone_img, self.EIB_zones[zone_id]
587                                     self.xmlGetZoneNode(subnode, zone_id)
588                                     if self.gotoZone == None:
589                                         self.gotoZone = zone_id
590                                     #self.EIB_zones[zone_id].EIBreadAll()
591                             if node.tagName == 'settings':
592                                 config.eib.host.value = node.getAttribute("host")
593                                 config.eib.port.value = int(node.getAttribute("port"))
594                                 config.eib.refresh.value = int(node.getAttribute("refresh"))
595                                 debug = False
596                                 if node.getAttribute("debug") == "true":
597                                         debug = True
598                                 config.eib.debug.setValue(debug)
599                                 if config.eib.debug.value:
600                                         print "[loadXML] parsed settings! host:", config.eib.host.value, "port:", config.eib.port.value, "refresh:", config.eib.refresh.value, "debug:", config.eib.debug.value
601                 except:
602                         self.errorOut("[loadXML] " + str(filename) + ' ' + _("parser error"))
603
604         def xmlGetMultiNodes(self, node):
605                 values = []
606                 images = []
607                 for subnode in node.childNodes:
608                         print "[xmlGetMultiNodes] subnode", subnode
609                         if subnode.nodeType == xml.dom.minidom.Element.nodeType:
610                                 i = 0
611                                 value = 0
612                                 img = None
613                                 while i < subnode.attributes.length:
614                                         item = subnode.attributes.item(i)
615                                         key = item.name.encode("utf-8")
616                                         if key == "value":
617                                                 values.append(str(item.nodeValue))
618                                         elif key == "img":
619                                                 images.append(str(item.nodeValue))
620                                         i = i+1
621                 return values, images
622         def xmlGetZoneNode(self, node, zone):
623                 order = 0
624                 for subnode in node.childNodes:
625                         if subnode.nodeType == xml.dom.minidom.Element.nodeType:
626                                 i = 0
627                                 object_id = None
628                                 object_type = None
629                                 label = None
630                                 img = None
631                                 readonly = False
632                                 custom_img = [None, None]
633                                 custom_values = []
634                                 temp_id = None
635                                 setpoint_id = None
636                                 textformat = None
637                                 while i < subnode.attributes.length:
638                                         item = subnode.attributes.item(i)
639                                         key = item.name.encode("utf-8")
640                                         if key == "object":
641                                                 object_id = item.nodeValue
642                                         #if key == "switch": # dimmer on/off (1 bit)
643                                                 #object_id = item.nodeValue
644                                         if key == "value": # dimmer brightness value (8 bit)
645                                                 object_id = item.nodeValue
646                                         if key == "target": # goto target
647                                                 object_id = item.nodeValue
648                                         if key == "temp": # thermostat actual value
649                                                 temp_id = item.nodeValue
650                                         if key == "setpoint": # thermostat set point
651                                                 setpoint_id = item.nodeValue
652                                         if key == "type":
653                                                 if item.nodeValue in ("dimmer", "switch", "goto", "thermostat", "text"):
654                                                         object_type = item.nodeValue
655                                                 elif item.nodeValue == "thermostat":
656                                                         object_type = item.nodeValue
657                                                 elif item.nodeValue == "multi":
658                                                         object_type = item.nodeValue
659                                                         custom_values, custom_img = self.xmlGetMultiNodes(subnode)
660                                         if key == "label":
661                                                 label = item.nodeValue
662                                         if key == "img":
663                                                 img = str(item.nodeValue)
664                                         if key == "x":
665                                                 x = int(item.nodeValue)
666                                         if key == "y":
667                                                 y = int(item.nodeValue)
668                                         if key == "off":
669                                                 custom_img[0] = str(item.nodeValue)
670                                         if key == "on":
671                                                 custom_img[1] = str(item.nodeValue)
672                                         if key == "format":
673                                                 textformat = item.nodeValue
674                                                 readonly = True
675                                         if key == "readonly" and item.nodeValue == "true":
676                                                 readonly = True
677                                         i += 1
678                                 if object_id and object_type and label and x and y:
679                                         obj = EIBObject(order, object_id, object_type, label, (x,y), img, custom_img, custom_values, textformat, readonly)
680                                         self.EIB_zones[zone].append(obj)
681                                         if config.eib.debug.value:
682                                                 print "[xmlGetZoneNode] new", obj.getInfo()
683                                         order += 1
684                                 elif temp_id and setpoint_id and label and x and y:
685                                         obj = EIBObject(order, temp_id, EIB_THERMO, label+' '+_("(actual)"), (x,y), readonly=True)
686                                         self.EIB_zones[zone].append(obj)
687                                         if config.eib.debug.value:
688                                                 print "[xmlGetZoneNode] new", obj.getInfo()
689                                         order += 1
690                                         obj = EIBObject(order, setpoint_id, EIB_THERMO, label+' '+_("(set point)"), (x,y+16), readonly=readonly)
691                                         self.EIB_zones[zone].append(obj)
692                                         if config.eib.debug.value:
693                                                 print "[xmlGetZoneNode] new", obj.getInfo()
694                                         order += 1
695                                 else:
696                                         print "[xmlGetZoneNode] couldn't parse object", object_id, object_type, label, (x,y), img, custom_img, custom_values, textformat, readonly, temp_id, setpoint_id
697
698 def main(session, **kwargs):
699         session.open(EIBox)
700
701 def Plugins(**kwargs):
702         return PluginDescriptor(name = "E.I.B.ox", description = _("Visualization for European Installation Bus"), where = PluginDescriptor.WHERE_EXTENSIONSMENU, fnc = main)
703