AutoTimerConfiguration.py: speedup generating xml a bit
[enigma2-plugins.git] / autotimer / src / AutoTimerConfiguration.py
1 # -*- coding: UTF-8 -*-
2 # for localized messages
3 from . import _
4
5 from AutoTimerComponent import preferredAutoTimerComponent
6 from RecordTimer import AFTEREVENT
7 from Tools.XMLTools import stringToXML
8 from ServiceReference import ServiceReference
9
10 from enigma import eServiceReference
11
12 CURRENT_CONFIG_VERSION = "5"
13
14 def getValue(definitions, default):
15         # Initialize Output
16         ret = ""
17
18         # How many definitions are present
19         if isinstance(definitions, list):
20                 Len = len(definitions)
21                 if Len > 0:
22                         childNodes = definitions[Len-1].text
23                 else:
24                         childNodes = ""
25         else:
26                 ret = definitions.text
27
28         # Return stripped output or (if empty) default
29         return ret.strip() or default
30
31 def parseConfig(configuration, list, version = None, uniqueTimerId = 0, defaultTimer = None):
32         if version != CURRENT_CONFIG_VERSION:
33                 parseConfigOld(configuration, list, uniqueTimerId)
34                 return
35
36         if defaultTimer is not None:
37                 # Read in defaults for a new timer
38                 for defaults in configuration.findall("defaults"):
39                         parseEntry(defaults, defaultTimer, True)
40
41         for timer in configuration.findall("timer"):
42                 uniqueTimerId += 1
43                 baseTimer = preferredAutoTimerComponent(
44                         uniqueTimerId,
45                         '',
46                         '',
47                         True
48                 )
49
50                 if parseEntry(timer, baseTimer):
51                         list.append(baseTimer)
52
53 def parseEntry(element, baseTimer, defaults = False):
54         if not defaults:
55                 # Read out match
56                 baseTimer.match = element.get("match", "").encode("UTF-8")
57                 if not baseTimer.match:
58                         print '[AutoTimer] Erroneous config is missing attribute "match", skipping entry'
59                         return False
60
61                 # Read out name
62                 baseTimer.name = element.get("name", "").encode("UTF-8")
63                 if not baseTimer.name:
64                         print '[AutoTimer] Timer is missing attribute "name", defaulting to match'
65                         baseTimer.name = baseTimer.match
66
67                 # Read out enabled
68                 enabled = element.get("enabled", "yes")
69                 if enabled == "no":
70                         baseTimer.enabled = False
71                 elif enabled == "yes":
72                         baseTimer.enabled = True
73                 else:
74                         print '[AutoTimer] Erroneous config contains invalid value for "enabled":', enabled,', disabling'
75                         baseTimer.enabled = False
76
77                 # Read timeframe
78                 before = element.get("before")
79                 after = element.get("after")
80                 if before and after:
81                         baseTimer.timeframe = (int(after), int(before))
82
83         # Read out encoding (won't change if no value is set)
84         baseTimer.encoding = element.get("encoding")
85
86         # Read out search type/case
87         baseTimer.searchType = element.get("searchType", baseTimer.searchType)
88         baseTimer.searchCase = element.get("searchCase", baseTimer.searchCase)
89
90         # Read out if we should change to alternative services
91         baseTimer.overrideAlternatives = int(element.get("overrideAlternatives", baseTimer.overrideAlternatives))
92
93         # Read out timespan
94         start = element.get("from")
95         end = element.get("to")
96         if start and end:
97                 start = [int(x) for x in start.split(':')]
98                 end = [int(x) for x in end.split(':')]
99                 baseTimer.timespan = (start, end)
100
101         # Read out max length
102         maxduration = element.get("maxduration")
103         if maxduration:
104                 baseTimer.maxduration = int(maxduration)*60
105
106         # Read out recording path
107         default = baseTimer.destination or ""
108         baseTimer.destination = element.get("location", default).encode("UTF-8") or None
109
110         # Read out offset
111         offset = element.get("offset")
112         if offset:
113                 offset = offset.split(",")
114                 if len(offset) == 1:
115                         before = after = int(offset[0] or 0) * 60
116                 else:
117                         before = int(offset[0] or 0) * 60
118                         after = int(offset[1] or 0) * 60
119                 baseTimer.offset = (before, after)
120
121         # Read out counter
122         baseTimer.matchCount = int(element.get("counter", 0))
123         baseTimer.matchFormatString = element.get("counterFormat", "")
124         if not defaults:
125                 baseTimer.matchLeft = int(element.get("left", baseTimer.matchCount))
126                 baseTimer.matchLimit = element.get("lastActivation", "")
127                 baseTimer.lastBegin = int(element.get("lastBegin", 0))
128
129         # Read out justplay
130         baseTimer.justplay = int(element.get("justplay", 0))
131
132         # Read out avoidDuplicateDescription
133         baseTimer.avoidDuplicateDescription = int(element.get("avoidDuplicateDescription", 0))
134
135         # Read out allowed services
136         l = element.findall("serviceref")
137         if l:
138                 servicelist = []
139
140                 for service in l:
141                         value = service.text
142                         if value:
143                                 myref = eServiceReference(str(value))
144                                 if not (myref.flags & eServiceReference.isGroup):
145                                         # strip all after last :
146                                         pos = value.rfind(':')
147                                         if pos != -1:
148                                                 if value[pos-1] == ':':
149                                                         pos -= 1
150                                                 value = value[:pos+1]
151
152                                 servicelist.append(value)
153                 baseTimer.services = servicelist
154
155         # Read out allowed bouquets
156         l = element.findall("bouquet")
157         if l:
158                 bouquets = []
159                 for bouquet in l:
160                         value = bouquet.text
161                         if value:
162                                 bouquets.append(value)
163                 baseTimer.bouquets = bouquets
164
165         # Read out afterevent
166         l = element.findall("afterevent")
167         if l:
168                 idx = {
169                         "none": AFTEREVENT.NONE,
170                         "deepstandby": AFTEREVENT.DEEPSTANDBY,
171                         "shutdown": AFTEREVENT.DEEPSTANDBY,
172                         "standby": AFTEREVENT.STANDBY,
173                         "auto": AFTEREVENT.AUTO
174                 }
175                 afterevents = []
176                 for afterevent in l:
177                         value = afterevent.text
178
179                         if idx.has_key(value):
180                                 value = idx[value]
181                         else:
182                                 print '[AutoTimer] Erroneous config contains invalid value for "afterevent":', afterevent,', ignoring definition'
183                                 continue
184
185                         start = afterevent.get("from")
186                         end = afterevent.get("to")
187                         if start and end:
188                                 start = [int(x) for x in start.split(':')]
189                                 end = [int(x) for x in end.split(':')]
190                                 afterevents.append((value, (start, end)))
191                         else:
192                                 afterevents.append((value, None))
193                 baseTimer.afterevent = afterevents
194
195         # Read out exclude
196         l = element.findall("exclude")
197         idx = {"title": 0, "shortdescription": 1, "description": 2, "dayofweek": 3}
198         if l:
199                 excludes = ([], [], [], [])
200                 for exclude in l:
201                         where = exclude.get("where")
202                         value = exclude.text
203                         if not (value and where):
204                                 continue
205
206                         if idx.has_key(where):
207                                 excludes[idx[where]].append(value.encode("UTF-8"))
208                 baseTimer.exclude = excludes
209
210         # Read out includes (use same idx)
211         l = element.findall("include")
212         if l:
213                 includes = ([], [], [], [])
214                 for include in l:
215                         where = include.get("where")
216                         value = include.text
217                         if not (value and where):
218                                 continue
219
220                         if idx.has_key(where):
221                                 includes[idx[where]].append(value.encode("UTF-8"))
222                 baseTimer.include = includes
223
224         # Read out recording tags
225         l =  element.findall("tag")
226         if l:
227                 tags = []
228                 for tag in l:
229                         value = tag.text
230                         if not value:
231                                 continue
232
233                         tags.append(value.encode("UTF-8"))
234                 baseTimer.tags = tags
235
236         return True
237
238 def parseConfigOld(configuration, list, uniqueTimerId = 0):
239         print "[AutoTimer] Trying to parse old config"
240
241         # Iterate Timers
242         for timer in configuration.findall("timer"):
243                 # Increment uniqueTimerId
244                 uniqueTimerId += 1
245
246                 # Get name (V2+)
247                 name = timer.get("name")
248                 if name:
249                         name = name.encode("UTF-8")
250                 # Get name (= match) (V1)
251                 else:
252                         # Read out name
253                         name = getValue(timer.findall("name"), "").encode("UTF-8")
254
255                 if not name:
256                         print '[AutoTimer] Erroneous config is missing attribute "name", skipping entry'
257                         continue
258
259                 # Read out match (V3+)
260                 match = timer.get("match")
261                 if match:
262                         # Read out match
263                         match = match.encode("UTF-8")
264                         if not match:
265                                 print '[AutoTimer] Erroneous config contains empty attribute "match", skipping entry'
266                                 continue
267                 # V2-
268                 else:
269                         # Setting match to name
270                         match = name
271
272
273                 # See if Timer is ensabled (V2+)
274                 enabled = timer.get("enabled")
275                 if enabled:
276                         if enabled == "no":
277                                 enabled = False
278                         elif enabled == "yes":
279                                 enabled = True
280                         else:
281                                 print '[AutoTimer] Erroneous config contains invalid value for "enabled":', enabled,', skipping entry'
282                                 enabled = False
283                 # V1
284                 else:
285                         elements = timer.findall("enabled")
286                         if elements:
287                                 if getValue(elements, "yes") == "no":
288                                         enabled = False
289                                 else:
290                                         enabled = True
291                         else:
292                                 enabled = True
293
294                 # Read out timespan (V4+; Falling back on missing definition should be OK)
295                 start = timer.get("from")
296                 end = timer.get("to")
297                 if start and end:
298                         start = [int(x) for x in start.split(':')]
299                         end = [int(x) for x in end.split(':')]
300                         timetuple = (start, end)
301                 # V3-
302                 else:
303                         elements = timer.findall("timespan")
304                         Len = len(elements)
305                         if Len:
306                                 # Read out last definition
307                                 start = elements[Len-1].get("from")
308                                 end = elements[Len-1].get("to")
309                                 if start and end:
310                                         start = [int(x) for x in start.split(':')]
311                                         end = [int(x) for x in end.split(':')]
312                                         timetuple = (start, end)
313                                 else:
314                                         print '[AutoTimer] Erroneous config contains invalid definition of "timespan", ignoring definition'
315                                         timetuple = None
316                         else:
317                                 timetuple = None
318
319                 # Read out allowed services (V*)
320                 elements = timer.findall("serviceref")
321                 if elements:
322                         servicelist = []
323                         for service in elements:
324                                 value = service.text
325                                 if value:
326                                         myref = eServiceReference(str(value))
327                                         if not (myref.flags & eServiceReference.isGroup):
328                                                 # strip all after last :
329                                                 pos = value.rfind(':')
330                                                 if pos != -1:
331                                                         if value[pos-1] == ':':
332                                                                 pos -= 1
333                                                         value = value[:pos+1]
334
335                                         servicelist.append(value)
336                 else:
337                         servicelist = None
338
339                 # Read out allowed bouquets (V* though officially supported since V4)
340                 bouquets = []
341                 for bouquet in timer.findall("bouquet"):
342                         value = bouquet.text
343                         if value:
344                                 bouquets.append(value)
345
346                 # Read out offset (V4+)
347                 offset = timer.get("offset")
348                 if offset:
349                         offset = offset.split(",")
350                         if len(offset) == 1:
351                                 before = after = int(offset[0] or 0) * 60
352                         else:
353                                 before = int(offset[0] or 0) * 60
354                                 after = int(offset[1] or 0) * 60
355                         offset = (before, after)
356                 # V3-
357                 else:
358                         elements = timer.findall("offset")
359                         Len = len(elements)
360                         if Len:
361                                 value = elements[Len-1].get("both")
362                                 if value == '':
363                                         before = int(elements[Len-1].get("before", 0)) * 60
364                                         after = int(elements[Len-1].get("after", 0)) * 60
365                                 else:
366                                         before = after = int(value) * 60
367                                 offset = (before, after)
368                         else:
369                                 offset = None
370
371                 # Read out counter
372                 counter = int(timer.get("counter", '0'))
373                 counterLeft = int(timer.get("left", counter))
374                 counterLimit = timer.get("lastActivation")
375                 counterFormat = timer.get("counterFormat", "")
376                 lastBegin = int(timer.get("lastBegin", 0))
377
378                 # Read out justplay
379                 justplay = int(timer.get("justplay", '0'))
380
381                 # Read out avoidDuplicateDescription
382                 avoidDuplicateDescription = int(timer.get("avoidDuplicateDescription", 0))
383
384                 # Read out afterevent (compatible to V* though behaviour for V3- is different as V4+ allows multiple afterevents while the last definication was chosen before)
385                 idx = {
386                         "none": AFTEREVENT.NONE,
387                         "deepstandby": AFTEREVENT.DEEPSTANDBY,
388                         "shutdown": AFTEREVENT.DEEPSTANDBY,
389                         "standby": AFTEREVENT.STANDBY,
390                         "auto": AFTEREVENT.AUTO
391                 }
392                 afterevent = []
393                 for element in timer.findall("afterevent"):
394                         value = element.text
395
396                         if idx.has_key(value):
397                                 value = idx[value]
398                         else:
399                                 print '[AutoTimer] Erroneous config contains invalid value for "afterevent":', afterevent,', ignoring definition'
400                                 continue
401
402                         start = element.get("from")
403                         end = element.get("to")
404                         if start and end:
405                                 start = [int(x) for x in start.split(':')]
406                                 end = [int(x) for x in end.split(':')]
407                                 afterevent.append((value, (start, end)))
408                         else:
409                                 afterevent.append((value, None))
410
411                 # Read out exclude (V*)
412                 idx = {"title": 0, "shortdescription": 1, "description": 2, "dayofweek": 3}
413                 excludes = ([], [], [], [])
414                 for exclude in timer.findall("exclude"):
415                         where = exclude.get("where")
416                         value = exclude.text
417                         if not (value and where):
418                                 continue
419
420                         if idx.has_key(where):
421                                 excludes[idx[where]].append(value.encode("UTF-8"))
422
423                 # Read out includes (use same idx) (V4+ feature, should not harm V3-)
424                 includes = ([], [], [], [])
425                 for include in timer.findall("include"):
426                         where = include.get("where")
427                         value = include.text
428                         if not (value and where):
429                                 continue
430
431                         if idx.has_key(where):
432                                 includes[idx[where]].append(value.encode("UTF-8"))
433
434                 # Read out max length (V4+)
435                 maxlen = timer.get("maxduration")
436                 if maxlen:
437                         maxlen = int(maxlen)*60
438                 # V3-
439                 else:
440                         elements = timer.findall("maxduration")
441                         if elements:
442                                 maxlen = getValue(elements, None)
443                                 if maxlen is not None:
444                                         maxlen = int(maxlen)*60
445                         else:
446                                 maxlen = None
447
448                 # Read out recording path
449                 destination = timer.get("destination", "").encode("UTF-8") or None
450
451                 # Read out recording tags
452                 tags = []
453                 for tag in timer.findall("tag"):
454                         value = tag.text
455                         if not value:
456                                 continue
457
458                         tags.append(value.encode("UTF-8"))
459
460                 # Finally append timer
461                 list.append(preferredAutoTimerComponent(
462                                 uniqueTimerId,
463                                 name,
464                                 match,
465                                 enabled,
466                                 timespan = timetuple,
467                                 services = servicelist,
468                                 offset = offset,
469                                 afterevent = afterevent,
470                                 exclude = excludes,
471                                 include = includes,
472                                 maxduration = maxlen,
473                                 destination = destination,
474                                 matchCount = counter,
475                                 matchLeft = counterLeft,
476                                 matchLimit = counterLimit,
477                                 matchFormatString = counterFormat,
478                                 lastBegin = lastBegin,
479                                 justplay = justplay,
480                                 avoidDuplicateDescription = avoidDuplicateDescription,
481                                 bouquets = bouquets,
482                                 tags = tags
483                 ))
484
485 def buildConfig(defaultTimer, timers, webif = False):
486         # Generate List in RAM
487         list = ['<?xml version="1.0" ?>\n<autotimer version="', CURRENT_CONFIG_VERSION, '">\n\n']
488         append = list.append
489         extend = list.extend
490
491         # This gets deleted afterwards if we do not have set any defaults
492         append(' <defaults')
493         if webif:
494                 extend((' id="', str(defaultTimer.getId()),'"'))
495
496         # Timespan
497         if defaultTimer.hasTimespan():
498                 extend((' from="', defaultTimer.getTimespanBegin(), '" to="', defaultTimer.getTimespanEnd(), '"'))
499
500         # Duration
501         if defaultTimer.hasDuration():
502                 extend((' maxduration="', str(defaultTimer.getDuration()), '"'))
503
504         # Destination
505         if defaultTimer.hasDestination():
506                 extend((' location="', stringToXML(defaultTimer.destination), '"'))
507
508         # Offset
509         if defaultTimer.hasOffset():
510                 if defaultTimer.isOffsetEqual():
511                         extend((' offset="', str(defaultTimer.getOffsetBegin()), '"'))
512                 else:
513                         extend((' offset="', str(defaultTimer.getOffsetBegin()), ',', str(defaultTimer.getOffsetEnd()), '"'))
514
515         # Counter
516         if defaultTimer.hasCounter():
517                 extend((' counter="', str(defaultTimer.getCounter()), '"'))
518                 if defaultTimer.hasCounterFormatString():
519                         extend((' counterFormat="', str(defaultTimer.getCounterFormatString()), '"'))
520
521         # Duplicate Description
522         if defaultTimer.getAvoidDuplicateDescription():
523                 append(' avoidDuplicateDescription="1" ')
524
525         # Only display justplay if true
526         if defaultTimer.justplay:
527                 extend((' justplay="', str(defaultTimer.getJustplay()), '"'))
528
529         # Only display encoding if != utf-8
530         if defaultTimer.encoding != 'UTF-8' or webif:
531                 extend((' encoding="', str(defaultTimer.encoding), '"'))
532
533         # Only display searchType if exact
534         if defaultTimer.searchType == "exact":
535                 extend((' searchType="', str(defaultTimer.searchType), '"'))
536
537         # Only display searchCase if sensitive
538         if defaultTimer.searchCase == "sensitive":
539                 extend((' searchCase="', str(defaultTimer.searchCase), '"'))
540
541         # Close still opened defaults tag
542         append('>\n')
543
544         if webif:
545                 # Services + Bouquets
546                 for serviceref in defaultTimer.services + defaultTimer.bouquets:
547                         ref = ServiceReference(str(serviceref))
548                         extend((
549                                 '  <e2service>\n',
550                                 '   <e2servicereference>', str(serviceref), '</e2servicereference>\n',
551                                 '   <e2servicename>', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), '</e2servicename>\n',
552                                 '  </e2service>\n',
553                         ))
554         else:
555                 # Services
556                 for serviceref in defaultTimer.services:
557                         ref = ServiceReference(str(serviceref))
558                         extend(('  <serviceref>', serviceref, '</serviceref>',
559                                                 ' <!-- ', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), ' -->\n',
560                         ))
561
562                 # Bouquets
563                 for bouquet in defaultTimer.bouquets:
564                         ref = ServiceReference(str(bouquet))
565                         extend(('  <bouquet>', str(bouquet), '</bouquet>',
566                                                 ' <!-- ', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), ' -->\n',
567                         ))
568
569         # AfterEvent
570         if defaultTimer.hasAfterEvent():
571                 idx = {
572                         AFTEREVENT.NONE: "none",
573                         AFTEREVENT.STANDBY: "standby",
574                         AFTEREVENT.DEEPSTANDBY: "shutdown",
575                         AFTEREVENT.AUTO: "auto"
576                 }
577                 for afterevent in defaultTimer.afterevent:
578                         action, timespan = afterevent
579                         append('  <afterevent')
580                         if timespan[0] is not None:
581                                 append(' from="%02d:%02d" to="%02d:%02d"' % (timespan[0][0], timespan[0][1], timespan[1][0], timespan[1][1]))
582                         extend(('>', idx[action], '</afterevent>\n'))
583
584         # Excludes
585         for title in defaultTimer.getExcludedTitle():
586                 extend(('  <exclude where="title">', stringToXML(title), '</exclude>\n'))
587         for short in defaultTimer.getExcludedShort():
588                 extend(('  <exclude where="shortdescription">', stringToXML(short), '</exclude>\n'))
589         for desc in defaultTimer.getExcludedDescription():
590                 extend(('  <exclude where="description">', stringToXML(desc), '</exclude>\n'))
591         for day in defaultTimer.getExcludedDays():
592                 extend(('  <exclude where="dayofweek">', stringToXML(day), '</exclude>\n'))
593
594         # Includes
595         for title in defaultTimer.getIncludedTitle():
596                 extend(('  <include where="title">', stringToXML(title), '</include>\n'))
597         for short in defaultTimer.getIncludedShort():
598                 extend(('  <include where="shortdescription">', stringToXML(short), '</include>\n'))
599         for desc in defaultTimer.getIncludedDescription():
600                 extend(('  <include where="description">', stringToXML(desc), '</include>\n'))
601         for day in defaultTimer.getIncludedDays():
602                 extend(('  <include where="dayofweek">', stringToXML(day), '</include>\n'))
603
604         # Tags
605         if webif and defaultTimer.tags:
606                 extend(('  <e2tags>', stringToXML(' '.join(defaultTimer.tags)), '</e2tags>\n'))
607         else:
608                 for tag in defaultTimer.tags:
609                         extend(('  <tag>', stringToXML(tag), '</tag>\n'))
610
611         # Keep the list clean
612         if len(list) == 5:
613                 list.pop() # >
614                 list.pop() # <defaults
615         else:
616                 append(' </defaults>\n\n')
617
618         # Iterate timers
619         for timer in timers:
620                 # Common attributes (match, enabled)
621                 extend((' <timer name="', stringToXML(timer.name), '" match="', stringToXML(timer.match), '" enabled="', timer.getEnabled(), '"'))
622                 if webif:
623                         extend((' id="', str(timer.getId()),'"'))
624
625                 # Timespan
626                 if timer.hasTimespan():
627                         extend((' from="', timer.getTimespanBegin(), '" to="', timer.getTimespanEnd(), '"'))
628
629                 # Timeframe
630                 if timer.hasTimeframe():
631                         extend((' after="', str(timer.getTimeframeBegin()), '" before="', str(timer.getTimeframeEnd()), '"'))
632
633                 # Duration
634                 if timer.hasDuration():
635                         extend((' maxduration="', str(timer.getDuration()), '"'))
636
637                 # Destination
638                 if timer.hasDestination():
639                         extend((' location="', stringToXML(timer.destination), '"'))
640
641                 # Offset
642                 if timer.hasOffset():
643                         if timer.isOffsetEqual():
644                                 extend((' offset="', str(timer.getOffsetBegin()), '"'))
645                         else:
646                                 extend((' offset="', str(timer.getOffsetBegin()), ',', str(timer.getOffsetEnd()), '"'))
647
648                 # Counter
649                 if timer.hasCounter():
650                         extend((' lastBegin="', str(timer.getLastBegin()), '" counter="', str(timer.getCounter()), '" left="', str(timer.getCounterLeft()) ,'"'))
651                         if timer.hasCounterFormatString():
652                                 extend((' lastActivation="', str(timer.getCounterLimit()), '"'))
653                                 extend((' counterFormat="', str(timer.getCounterFormatString()), '"'))
654
655                 # Duplicate Description
656                 if timer.getAvoidDuplicateDescription():
657                         extend((' avoidDuplicateDescription="', str(timer.getAvoidDuplicateDescription()), '"'))
658
659                 # Only display justplay if true
660                 if timer.justplay:
661                         extend((' justplay="', str(timer.getJustplay()), '"'))
662
663                 # Only display encoding if != utf-8
664                 if timer.encoding != 'UTF-8' or webif:
665                         extend((' encoding="', str(timer.encoding), '"'))
666
667                 # Only display searchType if exact
668                 if timer.searchType == "exact":
669                         extend((' searchType="', str(timer.searchType), '"'))
670
671                 # Only display searchCase if sensitive
672                 if timer.searchCase == "sensitive":
673                         extend((' searchCase="', str(timer.searchCase), '"'))
674
675                 # Only display overrideAlternatives if true
676                 if timer.overrideAlternatives:
677                         extend((' overrideAlternatives="', str(timer.getOverrideAlternatives()), '"'))
678
679                 # Close still opened timer tag
680                 append('>\n')
681
682                 if webif:
683                         # Services + Bouquets
684                         for serviceref in timer.services + timer.bouquets:
685                                 ref = ServiceReference(str(serviceref))
686                                 extend((
687                                         '  <e2service>\n',
688                                         '   <e2servicereference>', str(serviceref), '</e2servicereference>\n',
689                                         '   <e2servicename>', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), '</e2servicename>\n',
690                                         '  </e2service>\n',
691                                 ))
692                 else:
693                         # Services
694                         for serviceref in timer.services:
695                                 ref = ServiceReference(str(serviceref))
696                                 extend(('  <serviceref>', serviceref, '</serviceref>',
697                                                         ' <!-- ', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), ' -->\n',
698                                 ))
699
700                         # Bouquets
701                         for bouquet in timer.bouquets:
702                                 ref = ServiceReference(str(bouquet))
703                                 extend(('  <bouquet>', str(bouquet), '</bouquet>',
704                                                         ' <!-- ', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), ' -->\n',
705                                 ))
706
707                 # AfterEvent
708                 if timer.hasAfterEvent():
709                         idx = {
710                                 AFTEREVENT.NONE: "none",
711                                 AFTEREVENT.STANDBY: "standby",
712                                 AFTEREVENT.DEEPSTANDBY: "shutdown",
713                                 AFTEREVENT.AUTO: "auto"
714                         }
715                         for afterevent in timer.afterevent:
716                                 action, timespan = afterevent
717                                 append('  <afterevent')
718                                 if timespan[0] is not None:
719                                         append(' from="%02d:%02d" to="%02d:%02d"' % (timespan[0][0], timespan[0][1], timespan[1][0], timespan[1][1]))
720                                 extend(('>', idx[action], '</afterevent>\n'))
721
722                 # Excludes
723                 for title in timer.getExcludedTitle():
724                         extend(('  <exclude where="title">', stringToXML(title), '</exclude>\n'))
725                 for short in timer.getExcludedShort():
726                         extend(('  <exclude where="shortdescription">', stringToXML(short), '</exclude>\n'))
727                 for desc in timer.getExcludedDescription():
728                         extend(('  <exclude where="description">', stringToXML(desc), '</exclude>\n'))
729                 for day in timer.getExcludedDays():
730                         extend(('  <exclude where="dayofweek">', stringToXML(day), '</exclude>\n'))
731
732                 # Includes
733                 for title in timer.getIncludedTitle():
734                         extend(('  <include where="title">', stringToXML(title), '</include>\n'))
735                 for short in timer.getIncludedShort():
736                         extend(('  <include where="shortdescription">', stringToXML(short), '</include>\n'))
737                 for desc in timer.getIncludedDescription():
738                         extend(('  <include where="description">', stringToXML(desc), '</include>\n'))
739                 for day in timer.getIncludedDays():
740                         extend(('  <include where="dayofweek">', stringToXML(day), '</include>\n'))
741
742                 # Tags
743                 if webif and timer.tags:
744                         extend(('  <e2tags>', stringToXML(' '.join(timer.tags)), '</e2tags>\n'))
745                 else:
746                         for tag in timer.tags:
747                                 extend(('  <tag>', stringToXML(tag), '</tag>\n'))
748
749                 # End of Timer
750                 append(' </timer>\n\n')
751
752         # End of Configuration
753         append('</autotimer>\n')
754
755         return list
756