Index: /ipk/source.sh4/swapepg_autotimer_1_0/CONTROL/control
===================================================================
--- /ipk/source.sh4/swapepg_autotimer_1_0/CONTROL/control	(revision 8760)
+++ /ipk/source.sh4/swapepg_autotimer_1_0/CONTROL/control	(revision 8760)
@@ -0,0 +1,11 @@
+Package: enigma2-plugin-swapepg-autotimer
+Version: 1.0
+Description: Auto Timer 1.0 for Swapstick
+Section: infos
+Priority: optional
+Maintainer: AAF Forum
+Architecture: sh4
+OE: Auto Timer 1.0 for Swapstick
+Homepage: http://www.aaf-digital.info
+Depends:
+Source: http://www.aaf-digital.info
Index: /ipk/source.sh4/swapepg_autotimer_1_0/CONTROL/postinst
===================================================================
--- /ipk/source.sh4/swapepg_autotimer_1_0/CONTROL/postinst	(revision 8760)
+++ /ipk/source.sh4/swapepg_autotimer_1_0/CONTROL/postinst	(revision 8760)
@@ -0,0 +1,7 @@
+#!/bin/sh
+TMP=/tmp/.epg
+echo "successfully installed"
+echo "syncing disk"
+echo "please reboot your box so that the extension will be mounted..."
+sync
+exit 0
Index: /ipk/source.sh4/swapepg_autotimer_1_0/CONTROL/postrm
===================================================================
--- /ipk/source.sh4/swapepg_autotimer_1_0/CONTROL/postrm	(revision 8760)
+++ /ipk/source.sh4/swapepg_autotimer_1_0/CONTROL/postrm	(revision 8760)
@@ -0,0 +1,9 @@
+#!/bin/sh
+TMP=/tmp/.epg
+
+rm -rf /var/swap/extensions/AutoTimer
+
+echo "successfully removed"
+echo "syncing disk"
+sync
+exit 0
Index: /ipk/source.sh4/swapepg_autotimer_1_0/CONTROL/preinst
===================================================================
--- /ipk/source.sh4/swapepg_autotimer_1_0/CONTROL/preinst	(revision 8760)
+++ /ipk/source.sh4/swapepg_autotimer_1_0/CONTROL/preinst	(revision 8760)
@@ -0,0 +1,38 @@
+#!/bin/sh
+#
+TMP=/tmp/.epg
+echo "syncing disk"
+sync
+
+model=`cat /etc/model`
+echo""
+echo "Checking your Boxtype...."
+echo "Some Plugins will not work correctly on your $model!"
+echo ""
+if [ "$model" = "" ]; then
+	echo "Sorry! This Plugin is not available for your $model because it will not work correctly!!!"
+	echo "Aborting installation..."
+	exit 1
+else
+	echo "Boxtype: $model OK"
+fi
+
+echo "checking swapstick"
+if [ ! -d /var/swap/extensions/ ]; then
+  echo "--------------------------"
+	echo "no swapstick found...."
+	echo "--------------------------"
+	exit 1
+fi
+echo "swapstick found...."
+echo "installing Auto Timer Plugin to swapstick..."
+echo "checking OS"
+if  [ `cat /etc/motd | grep AAF | grep M | grep rev | wc -l` -eq 0 ]; then                      
+	echo ---------------------------
+	echo DONT USE this IPK Package!!
+	echo ---
+	echo Only for AAF Image!!
+	echo ---------------------------
+	exit 1
+fi
+exit 0
Index: /ipk/source.sh4/swapepg_autotimer_1_0/CONTROL/prerm
===================================================================
--- /ipk/source.sh4/swapepg_autotimer_1_0/CONTROL/prerm	(revision 8760)
+++ /ipk/source.sh4/swapepg_autotimer_1_0/CONTROL/prerm	(revision 8760)
@@ -0,0 +1,6 @@
+#!/bin/sh
+TMP=/tmp/.epg
+echo "syncing disk"
+sync
+echo "removing Auto Timer from swapstick"
+exit 0
Index: /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/AutoPoller.py
===================================================================
--- /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/AutoPoller.py	(revision 8760)
+++ /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/AutoPoller.py	(revision 8760)
@@ -0,0 +1,41 @@
+# Timer
+from enigma import eTimer
+
+# Config
+from Components.config import config
+
+class AutoPoller:
+	"""Automatically Poll AutoTimer"""
+
+	def __init__(self):
+		# Init Timer
+		self.timer = eTimer()
+
+	def start(self, initial = True):
+		if initial:
+			delay = 2
+		else:
+			delay = config.plugins.autotimer.interval.value*3600
+
+		if self.query not in self.timer.callback:
+			self.timer.callback.append(self.query)
+		self.timer.startLongTimer(delay)
+
+	def stop(self):
+		if self.query in self.timer.callback:
+			self.timer.callback.remove(self.query)
+		self.timer.stop()
+
+	def query(self):
+		from plugin import autotimer
+
+		# Ignore any program errors
+		try:
+			autotimer.parseEPG()
+		except Exception:
+			# Dump error to stdout
+			import traceback, sys
+			traceback.print_exc(file=sys.stdout)
+
+		self.timer.startLongTimer(config.plugins.autotimer.interval.value*3600)
+
Index: /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/AutoTimer.py
===================================================================
--- /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/AutoTimer.py	(revision 8760)
+++ /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/AutoTimer.py	(revision 8760)
@@ -0,0 +1,334 @@
+# Plugins Config
+from xml.etree.cElementTree import parse as cet_parse
+from os import path as os_path
+from AutoTimerConfiguration import parseConfig, writeConfig
+
+# Navigation (RecordTimer)
+import NavigationInstance
+
+# Timer
+from ServiceReference import ServiceReference
+from RecordTimer import RecordTimerEntry
+from Components.TimerSanityCheck import TimerSanityCheck
+
+# Timespan
+from time import localtime, time
+
+# EPGCache & Event
+from enigma import eEPGCache, eServiceReference
+
+# Enigma2 Config
+from Components.config import config
+
+# AutoTimer Component
+from AutoTimerComponent import AutoTimerComponent
+
+XML_CONFIG = "/etc/enigma2/autotimer.xml"
+
+def getTimeDiff(timer, begin, end):
+	if begin <= timer.begin <= end:
+		return end - timer.begin
+	elif timer.begin <= begin <= timer.end:
+		return timer.end - begin
+	return 0
+
+typeMap = {
+	"exact": eEPGCache.EXAKT_TITLE_SEARCH,
+	"partial": eEPGCache.PARTIAL_TITLE_SEARCH
+}
+
+caseMap = {
+	"sensitive": eEPGCache.CASE_CHECK,
+	"insensitive": eEPGCache.NO_CASE_CHECK
+}
+
+class AutoTimerIgnoreTimerException(Exception):
+	def __init__(self, cause):
+		self.cause = cause
+
+	def __str__(self):
+		return "[AutoTimer] " + str(self.cause)
+
+	def __repr__(self):
+		return str(type(self))
+
+class AutoTimer:
+	"""Read and save xml configuration, query EPGCache"""
+
+	def __init__(self):
+		# Initialize
+		self.timers = []
+		self.configMtime = -1
+		self.uniqueTimerId = 0
+		self.defaultTimer = AutoTimerComponent(
+			0,		# Id
+			"",		# Name
+			"",		# Match
+			True 	# Enabled
+		)
+
+# Configuration
+
+	def readXml(self):
+		# Abort if no config found
+		if not os_path.exists(XML_CONFIG):
+			print "[AutoTimer] No configuration file present"
+			return
+
+		# Parse if mtime differs from whats saved
+		mtime = os_path.getmtime(XML_CONFIG)
+		if mtime == self.configMtime:
+			print "[AutoTimer] No changes in configuration, won't parse"
+			return
+
+		# Save current mtime
+		self.configMtime = mtime
+
+		# Parse Config
+		configuration = cet_parse(XML_CONFIG).getroot()
+
+		# Empty out timers and reset Ids
+		del self.timers[:]
+		self.defaultTimer.clear(-1, True)
+
+		parseConfig(
+			configuration,
+			self.timers,
+			configuration.get("version"),
+			0,
+			self.defaultTimer
+		)
+		self.uniqueTimerId = len(self.timers)
+
+	def writeXml(self):
+		writeConfig(XML_CONFIG, self.defaultTimer, self.timers)
+
+# Manage List
+
+	def add(self, timer):
+		self.timers.append(timer)
+
+	def getEnabledTimerList(self):
+		return [x for x in self.timers if x.enabled]
+
+	def getTimerList(self):
+		return self.timers
+
+	def getTupleTimerList(self):
+		list = self.timers
+		return [(x,) for x in list]
+
+	def getSortedTupleTimerList(self):
+		list = self.timers[:]
+		list.sort()
+		return [(x,) for x in list]
+
+	def getUniqueId(self):
+		self.uniqueTimerId += 1
+		return self.uniqueTimerId
+
+	def remove(self, uniqueId):
+		idx = 0
+		for timer in self.timers:
+			if timer.id == uniqueId:
+				self.timers.pop(idx)
+				return
+			idx += 1
+
+	def set(self, timer):
+		idx = 0
+		for stimer in self.timers:
+			if stimer == timer:
+				self.timers[idx] = timer
+				return
+			idx += 1
+		self.timers.append(timer)
+
+# Main function
+
+	def parseEPG(self, simulateOnly = False):
+		if NavigationInstance.instance is None:
+			print "[AutoTimer] Navigation is not available, can't parse EPG"
+			return (0, 0, 0, [])
+
+		total = 0
+		new = 0
+		modified = 0
+		timers = []
+
+		self.readXml()
+
+		# Save Recordings in a dict to speed things up a little
+		# We include processed timers as we might search for duplicate descriptions
+		recorddict = {}
+		for timer in NavigationInstance.instance.RecordTimer.timer_list + NavigationInstance.instance.RecordTimer.processed_timers:
+			recorddict.setdefault(str(timer.service_ref), []).append(timer)
+
+		# Iterate Timer
+		for timer in self.getEnabledTimerList():
+			# Workaround to allow search for umlauts if we know the encoding
+			match = timer.match
+			if timer.encoding != 'UTF-8':
+				try:
+					match = match.decode('UTF-8').encode(timer.encoding)
+				except UnicodeDecodeError:
+					pass
+
+			# Search EPG, default to empty list
+			epgcache = eEPGCache.getInstance()
+			ret = epgcache.search(('RI', 100, typeMap[timer.searchType], match, caseMap[timer.searchCase])) or ()
+
+			for serviceref, eit in ret:
+				eserviceref = eServiceReference(serviceref)
+
+				evt = epgcache.lookupEventId(eserviceref, eit)
+				if not evt:
+					print "[AutoTimer] Could not create Event!"
+					continue
+
+				# Try to determine real service (we always choose the last one)
+				n = evt.getNumOfLinkageServices()
+				if n > 0:
+					i = evt.getLinkageService(eserviceref, n-1)
+					serviceref = i.toString()
+
+				# Gather Information
+				name = evt.getEventName()
+				description = evt.getShortDescription()
+				begin = evt.getBeginTime()
+				duration = evt.getDuration()
+				end = begin + duration
+
+				# If event starts in less than 60 seconds skip it
+				if begin < time() + 60:
+					continue
+
+				# Convert begin time
+				timestamp = localtime(begin)
+
+				# Update timer
+				timer.update(begin, timestamp)
+
+				# Check Duration, Timespan and Excludes
+				if timer.checkServices(serviceref) \
+					or timer.checkDuration(duration) \
+					or timer.checkTimespan(timestamp) \
+					or timer.checkFilter(name, description,
+						evt.getExtendedDescription(), str(timestamp.tm_wday)):
+					continue
+
+				if timer.hasOffset():
+					# Apply custom Offset
+					begin, end = timer.applyOffset(begin, end)
+				else:
+					# Apply E2 Offset
+					begin -= config.recording.margin_before.value * 60
+					end += config.recording.margin_after.value * 60
+
+				# Eventually change service to alternative
+				if timer.overrideAlternatives:
+					serviceref = timer.getAlternative(serviceref)
+
+				total += 1
+
+				# Append to timerlist and abort if simulating
+				timers.append((name, begin, end, serviceref, timer.name))
+				if simulateOnly:
+					continue
+
+				# Initialize
+				newEntry = None
+				oldExists = False
+
+				# Check for double Timers
+				# We first check eit and if user wants us to guess event based on time
+				# we try this as backup. The allowed diff should be configurable though.
+				for rtimer in recorddict.get(serviceref, ()):
+					if rtimer.eit == eit or config.plugins.autotimer.try_guessing.value and getTimeDiff(rtimer, begin, end) > ((duration/10)*8):
+						oldExists = True
+
+						# Abort if we don't want to modify timers or timer is repeated
+						if config.plugins.autotimer.refresh.value == "none" or rtimer.repeated:
+							print "[AutoTimer] Won't modify existing timer because either no modification allowed or repeated timer"
+							break
+
+						if hasattr(rtimer, "isAutoTimer"):
+								print "[AutoTimer] Modifying existing AutoTimer!"
+						else:
+							if config.plugins.autotimer.refresh.value != "all":
+								print "[AutoTimer] Won't modify existing timer because it's no timer set by us"
+								break
+
+							print "[AutoTimer] Warning, we're messing with a timer which might not have been set by us"
+
+						newEntry = rtimer
+						modified += 1
+
+						# Modify values saved in timer
+						newEntry.name = name
+						newEntry.description = description
+						newEntry.begin = int(begin)
+						newEntry.end = int(end)
+						newEntry.service_ref = ServiceReference(serviceref)
+
+						break
+					elif timer.avoidDuplicateDescription == 1 and rtimer.description == description:
+						oldExists = True
+						print "[AutoTimer] We found a timer with same description, skipping event"
+						break
+
+				# We found no timer we want to edit
+				if newEntry is None:
+					# But there is a match
+					if oldExists:
+						continue
+
+					# We want to search for possible doubles
+					if timer.avoidDuplicateDescription == 2:
+						# I thinks thats the fastest way to do this, though it's a little ugly
+						try:
+							for list in recorddict.values():
+								for rtimer in list:
+									if rtimer.description == description:
+										raise AutoTimerIgnoreTimerException("We found a timer with same description, skipping event")
+						except AutoTimerIgnoreTimerException, etite:
+							print etite
+							continue
+
+					if timer.checkCounter(timestamp):
+						continue
+
+					print "[AutoTimer] Adding an event."
+					newEntry = RecordTimerEntry(ServiceReference(serviceref), begin, end, name, description, eit)
+
+					# Mark this entry as AutoTimer (only AutoTimers will have this Attribute set)
+					newEntry.isAutoTimer = True
+
+				# Apply afterEvent
+ 				if timer.hasAfterEvent():
+ 					afterEvent = timer.getAfterEventTimespan(localtime(end))
+ 					if afterEvent is None:
+ 						afterEvent = timer.getAfterEvent()
+ 					if afterEvent is not None:
+ 						newEntry.afterEvent = afterEvent
+
+				newEntry.dirname = timer.destination
+				newEntry.justplay = timer.justplay
+				newEntry.tags = timer.tags
+
+				if oldExists:
+					# XXX: this won't perform a sanity check, but do we actually want to do so?
+					NavigationInstance.instance.RecordTimer.timeChanged(newEntry)
+				else:
+					conflicts = NavigationInstance.instance.RecordTimer.record(newEntry)
+					if conflicts and config.plugins.autotimer.disabled_on_conflict.value:
+						newEntry.disabled = True
+						# We might want to do the sanity check locally so we don't run it twice - but I consider this workaround a hack anyway
+						conflicts = NavigationInstance.instance.RecordTimer.record(newEntry)
+					if conflicts is None:
+						timer.decrementCounter()
+						new += 1
+						recorddict.setdefault(serviceref, []).append(newEntry)
+
+		return (total, new, modified, timers)
+
Index: /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/AutoTimerComponent.py
===================================================================
--- /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/AutoTimerComponent.py	(revision 8760)
+++ /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/AutoTimerComponent.py	(revision 8760)
@@ -0,0 +1,609 @@
+# Format counter
+from time import strftime
+
+# regular expression
+from re import compile as re_compile
+
+# Alternatives and service restriction
+from enigma import eServiceReference, eServiceCenter
+
+class AutoTimerComponent(object):
+	"""AutoTimer Component which also handles validity checks"""
+
+	"""
+	 Initiate
+	"""
+	def __init__(self, id, name, match, enabled, *args, **kwargs):
+		self.id = id
+		self._afterevent = []
+		self.setValues(name, match, enabled, *args, **kwargs)
+
+	"""
+	 Unsets all Attributes
+	"""
+	def clear(self, id = -1, enabled = False):
+		self.id = id
+		self.setValues('', '', enabled)
+
+	"""
+	 Create a deep copy of this instance
+	"""
+	def clone(self):
+		return self.__deepcopy__({})
+
+	"""
+	 Hook needed for WebIf
+	"""
+	def getEntry(self):
+		return self
+
+	"""
+	 Keeps init small and helps setting many values at once
+	"""
+	def setValues(self, name, match, enabled, timespan = None, services = None, offset = None, \
+			afterevent = [], exclude = None, maxduration = None, destination = None, \
+			include = None, matchCount = 0, matchLeft = 0, matchLimit = '', matchFormatString = '', \
+			lastBegin = 0, justplay = False, avoidDuplicateDescription = 0, bouquets = None, \
+			tags = None, encoding = 'UTF-8', searchType = "partial", searchCase = "insensitive", \
+			overrideAlternatives = False):
+		self.name = name
+		self.match = match
+		self.enabled = enabled
+		self.timespan = timespan
+		self.services = services
+		self.offset = offset
+		self.afterevent = afterevent
+		self.exclude = exclude
+		self.maxduration = maxduration
+		self.destination = destination
+		self.include = include
+		self.matchCount = matchCount
+		self.matchLeft = matchLeft
+		self.matchLimit = matchLimit
+		self.matchFormatString = matchFormatString
+		self.lastBegin = lastBegin
+		self.justplay = justplay
+		self.avoidDuplicateDescription = avoidDuplicateDescription
+		self.bouquets = bouquets
+		self.tags = tags or []
+		self.encoding = encoding
+		self.searchType = searchType
+		self.searchCase = searchCase
+		self.overrideAlternatives = overrideAlternatives
+
+### Attributes / Properties
+
+	def setAfterEvent(self, afterevent):
+		if afterevent is not self._afterevent:
+			del self._afterevent[:]
+
+		Len = len(afterevent)
+		if Len:
+			for x in range(0, Len):
+				action, timespan = afterevent[x]
+
+				# Remove original entry
+				del afterevent[x]
+				# If the second argument is a tuple we assume the entry is already parsed
+				if isinstance(timespan, tuple):
+					self._afterevent.insert(x, (action, timespan))
+				elif timespan is None:
+					self._afterevent.insert(x, (action, (None,)))
+				else:
+					self._afterevent.insert(x, (action, self.calculateDayspan(*timespan)))
+
+	afterevent = property(lambda self: self._afterevent, setAfterEvent)
+
+	def setBouquets(self, bouquets):
+		if bouquets:
+			self._bouquets = bouquets
+		else:
+			self._bouquets = []
+
+	bouquets = property(lambda self: self._bouquets , setBouquets)
+
+	def setEncoding(self, encoding):
+		if encoding:
+			self._encoding = encoding
+
+	encoding = property(lambda self: self._encoding, setEncoding)
+
+	def setExclude(self, exclude):
+		if exclude:
+			self._exclude = (
+				[re_compile(x) for x in exclude[0]],
+				[re_compile(x) for x in exclude[1]],
+				[re_compile(x) for x in exclude[2]],
+				exclude[3]
+			)
+		else:
+			self._exclude = ([], [], [], [])
+
+	exclude = property(lambda self: self._exclude, setExclude)
+
+	def setInclude(self, include):
+		if include:
+			self._include = (
+				[re_compile(x) for x in include[0]],
+				[re_compile(x) for x in include[1]],
+				[re_compile(x) for x in include[2]],
+				include[3]
+			)
+		else:
+			self._include = ([], [], [], [])
+
+	include = property(lambda self: self._include, setInclude)
+
+	def setSearchCase(self, case):
+		assert case in ("sensitive", "insensitive"), "search case must be sensitive or insensitive"
+		self._searchCase = case
+
+	searchCase = property(lambda self: self._searchCase, setSearchCase)
+
+	def setSearchType(self, type):
+		assert type in ("exact", "partial"), "search type must be exact or partial"
+		self._searchType = type
+
+	searchType = property(lambda self: self._searchType, setSearchType)
+
+	def setServices(self, services):
+		if services:
+			self._services = services
+		else:
+			self._services = []
+
+	services = property(lambda self: self._services, setServices)
+
+	def setTimespan(self, timespan):
+		if timespan is None or len(timespan) and timespan[0] is None:
+			self._timespan = (None,)
+		else:
+			self._timespan = self.calculateDayspan(*timespan)
+
+	timespan = property(lambda self: self._timespan, setTimespan)
+
+### See if Attributes are set
+
+	def hasAfterEvent(self):
+		return len(self.afterevent)
+
+	def hasAfterEventTimespan(self):
+		for afterevent in self.afterevent:
+			if afterevent[1][0] is not None:
+				return True
+		return False
+
+	def hasCounter(self):
+		return self.matchCount != 0
+
+	def hasCounterFormatString(self):
+		return self.matchFormatString != ''
+
+	def hasDestination(self):
+		return self.destination is not None
+
+	def hasDuration(self):
+		return self.maxduration is not None
+
+	def hasTags(self):
+		return len(self.tags) > 0
+
+	def hasTimespan(self):
+		return self.timespan[0] is not None
+
+	def hasOffset(self):
+		return self.offset is not None
+
+### Helper
+
+	"""
+	 Returns a tulple of (input begin, input end, begin earlier than end)
+	"""
+	def calculateDayspan(self, begin, end, ignore = None):
+		if end[0] < begin[0] or (end[0] == begin[0] and end[1] <= begin[1]):
+			return (begin, end, True)
+		else:
+			return (begin, end, False)
+
+	"""
+	 Returns if a given timestruct is in a timespan
+	"""
+	def checkAnyTimespan(self, time, begin = None, end = None, haveDayspan = False):
+		if begin is None:
+			return False
+
+		# Check if we span a day
+		if haveDayspan:
+			# Check if begin of event is later than our timespan starts
+			if time.tm_hour > begin[0] or (time.tm_hour == begin[0] and time.tm_min >= begin[1]):
+				# If so, event is in our timespan
+				return False
+			# Check if begin of event is earlier than our timespan end
+			if time.tm_hour < end[0] or (time.tm_hour == end[0] and time.tm_min <= end[1]):
+				# If so, event is in our timespan
+				return False
+			return True
+		else:
+			# Check if event begins earlier than our timespan starts
+			if time.tm_hour < begin[0] or (time.tm_hour == begin[0] and time.tm_min < begin[1]):
+				# Its out of our timespan then
+				return True
+			# Check if event begins later than our timespan ends
+			if time.tm_hour > end[0] or (time.tm_hour == end[0] and time.tm_min > end[1]):
+				# Its out of our timespan then
+				return True
+			return False
+
+	"""
+	 Returns a list of all allowed services by listing the bouquets
+	"""
+	def getFullServices(self):
+		list = self.services[:]
+
+		from enigma import eServiceReference, eServiceCenter
+		serviceHandler = eServiceCenter.getInstance()
+		for bouquet in self.bouquets:
+			myref = eServiceReference(str(bouquet))
+			mylist = serviceHandler.list(myref)
+			if mylist is not None:
+				while 1:
+					s = mylist.getNext()
+					# TODO: I wonder if its sane to assume we get services here (and not just new lists)
+					# We can ignore markers & directorys here because they won't match any event's service :-)
+					if s.valid():
+						# strip all after last :
+						value = s.toString()
+						pos = value.rfind(':')
+						if pos != -1:
+							value = value[:pos+1]
+
+						list.append(value)
+					else:
+						break
+
+		return list
+
+	"""
+	 Called when a timer based on this component was added
+	"""
+	def update(self, begin, timestamp):
+		# Only update limit when we have new begin
+		if begin > self.lastBegin:
+			self.lastBegin = begin
+
+			# Update Counter:
+			# %m is Month, %U is week (sunday), %W is week (monday)
+			newLimit = strftime(self.matchFormatString, timestamp)
+
+			if newLimit != self.matchLimit:
+				self.matchLeft = self.matchCount
+				self.matchLimit = newLimit
+
+### Makes saving Config easier
+
+	getAvoidDuplicateDescription = lambda self: self.avoidDuplicateDescription
+
+	getBouquets = lambda self: self._bouquets
+
+	getCompleteAfterEvent = lambda self: self._afterevent
+
+	getCounter = lambda self: self.matchCount
+	getCounterFormatString = lambda self: self.matchFormatString
+	getCounterLeft = lambda self: self.matchLeft
+	getCounterLimit = lambda self: self.matchLimit
+
+	# XXX: as this function was not added by me (ritzMo) i'll leave it like this but i'm not really sure if this is right ;-)
+	getDestination = lambda self: self.destination is not None
+
+	getDuration = lambda self: self.maxduration/60
+
+	getEnabled = lambda self: self.enabled and "yes" or "no"
+
+	getExclude = lambda self: self._exclude
+	getExcludedDays = lambda self: self.exclude[3]
+	getExcludedDescription = lambda self: [x.pattern for x in self.exclude[2]]
+	getExcludedShort = lambda self: [x.pattern for x in self.exclude[1]]
+	getExcludedTitle = lambda self: [x.pattern for x in self.exclude[0]]
+
+	getInclude = lambda self: self._include
+	getIncludedTitle = lambda self: [x.pattern for x in self.include[0]]
+	getIncludedShort = lambda self: [x.pattern for x in self.include[1]]
+	getIncludedDescription = lambda self: [x.pattern for x in self.include[2]]
+	getIncludedDays = lambda self: self.include[3]
+
+	getJustplay = lambda self: self.justplay and "1" or "0"
+
+	getLastBegin = lambda self: self.lastBegin
+
+	getMatch = lambda self: self.match
+	getName = lambda self: self.name
+
+	getOffsetBegin = lambda self: self.offset[0]/60
+	getOffsetEnd = lambda self: self.offset[1]/60
+
+	getOverrideAlternatives = lambda self: self.overrideAlternatives and "1" or "0"
+
+	getServices = lambda self: self._services
+
+	getTags = lambda self: self.tags
+
+	getTimespan = lambda self: self._timespan
+	getTimespanBegin = lambda self: '%02d:%02d' % (self.timespan[0][0], self.timespan[0][1])
+	getTimespanEnd = lambda self: '%02d:%02d' % (self.timespan[1][0], self.timespan[1][1])
+
+	isOffsetEqual = lambda self: self.offset[0] == self.offset[1]
+
+### Actual functionality
+
+	def applyOffset(self, begin, end):
+		if self.offset is None:
+			return (begin, end)
+		return (begin - self.offset[0], end + self.offset[1])
+
+	def checkCounter(self, timestamp):
+		# 0-Count is considered "unset"
+		if self.matchCount == 0:
+			return False
+
+		# Check if event is in current timespan (we can only manage one!)
+		limit = strftime(self.matchFormatString, timestamp)
+		if limit != self.matchLimit:
+			return True
+
+		if self.matchLeft > 0:
+			return False
+		return True
+
+	def checkDuration(self, length):
+		if self.maxduration is None:
+			return False
+		return length > self.maxduration
+
+	def checkExcluded(self, title, short, extended, dayofweek):
+		if self.exclude[3]:
+			list = [x for x in self.exclude[3]]
+			if "weekend" in list:
+				list.extend(["5", "6"])
+			if "weekday" in list:
+				list.extend(["0", "1", "2", "3", "4"])
+			if dayofweek in list:
+				return True
+
+		for exclude in self.exclude[0]:
+			if exclude.search(title):
+				return True
+		for exclude in self.exclude[1]:
+			if exclude.search(short):
+				return True
+		for exclude in self.exclude[2]:
+			if exclude.search(extended):
+				return True
+		return False
+
+	def checkFilter(self, title, short, extended, dayofweek):
+		if self.checkExcluded(title, short, extended, dayofweek):
+			return True
+
+		return self.checkIncluded(title, short, extended, dayofweek)
+
+	def checkIncluded(self, title, short, extended, dayofweek):
+		if self.include[3]:
+			list = [x for x in self.include[3]]
+			if "weekend" in list:
+				list.extend(["5", "6"])
+			if "weekday" in list:
+				list.extend(["0", "1", "2", "3", "4"])
+			if dayofweek not in list:
+				return True
+
+		for include in self.include[0]:
+			if not include.search(title):
+				return True
+		for include in self.include[1]:
+			if not include.search(short):
+				return True
+		for include in self.include[2]:
+			if not include.search(extended):
+				return True
+
+		return False
+
+	def checkServices(self, check_service):
+		services = self.services
+		bouquets = self.bouquets
+		if services or bouquets:
+			addbouquets = []
+
+			for service in services:
+				if service == check_service:
+					return False
+
+				myref = eServiceReference(str(service))
+				if myref.flags & eServiceReference.isGroup:
+					addbouquets.append(service)
+
+			serviceHandler = eServiceCenter.getInstance()
+			for bouquet in bouquets + addbouquets:
+				myref = eServiceReference(str(bouquet))
+				mylist = serviceHandler.list(myref)
+				if mylist is not None:
+					while 1:
+						s = mylist.getNext()
+						# TODO: I wonder if its sane to assume we get services here (and not just new lists)
+						# We can ignore markers & directorys here because they won't match any event's service :-)
+						if s.valid():
+							# strip all after last :
+							value = s.toString()
+							pos = value.rfind(':')
+							if pos != -1:
+								value = value[:pos+1]
+
+							if value == check_service:
+								return False
+						else:
+							break
+			return True
+		return False
+
+	"""
+	Return alternative service including a given ref.
+	Note that this only works for alternatives that the autotimer is restricted to.
+	"""
+	def getAlternative(self, override_service):
+		services = self.services
+		if services:
+			serviceHandler = eServiceCenter.getInstance()
+
+			for service in services:
+				myref = eServiceReference(str(service))
+				if myref.flags & eServiceReference.isGroup:
+					mylist = serviceHandler.list(myref)
+					if mylist is not None:
+						while 1:
+							s = mylist.getNext()
+							if s.valid():
+								# strip all after last :
+								value = s.toString()
+								pos = value.rfind(':')
+								if pos != -1:
+									value = value[:pos+1]
+
+								if value == override_service:
+									return service
+							else:
+								break
+		return override_service
+
+	def checkTimespan(self, begin):
+		return self.checkAnyTimespan(begin, *self.timespan)
+
+	def decrementCounter(self):
+		if self.matchCount and self.matchLeft > 0:
+			self.matchLeft -= 1
+
+	def getAfterEvent(self):
+		for afterevent in self.afterevent:
+			if afterevent[1][0] is None:
+				return afterevent[0]
+		return None
+
+	def getAfterEventTimespan(self, end):
+		for afterevent in self.afterevent:
+			if not self.checkAnyTimespan(end, *afterevent[1]):
+				return afterevent[0]
+		return None
+
+### Misc
+
+	def __copy__(self):
+		return self.__class__(
+			self.id,
+			self.name,
+			self.match,
+			self.enabled,
+			timespan = self.timespan,
+			services = self.services,
+			offset = self.offset,
+			afterevent = self.afterevent,
+			exclude = (self.getExcludedTitle(), self.getExcludedShort(), self.getExcludedDescription(), self.getExcludedDays()),
+			maxduration = self.maxduration,
+			destination = self.destination,
+			include = (self.getIncludedTitle(), self.getIncludedShort(), self.getIncludedDescription(), self.getIncludedDays()),
+			matchCount = self.matchCount,
+			matchLeft = self.matchLeft,
+			matchLimit = self.matchLimit,
+			matchFormatString = self.matchFormatString,
+			lastBegin = self.lastBegin,
+			justplay = self.justplay,
+			avoidDuplicateDescription = self.avoidDuplicateDescription,
+			bouquets = self.bouquets,
+			tags = self.tags,
+			encoding = self.encoding,
+			searchType = self.searchType,
+			searchCase = self.searchCase,
+			overrideAlternatives = self.overrideAlternatives
+		)
+
+	def __deepcopy__(self, memo):
+		return self.__class__(
+			self.id,
+			self.name,
+			self.match,
+			self.enabled,
+			timespan = self.timespan,
+			services = self.services[:],
+			offset = self.offset and self.offset[:],
+			afterevent = self.afterevent[:],
+			exclude = (self.getExcludedTitle(), self.getExcludedShort(), self.getExcludedDescription(), self.exclude[3][:]),
+			maxduration = self.maxduration,
+			destination = self.destination,
+			include = (self.getIncludedTitle(), self.getIncludedShort(), self.getIncludedDescription(), self.include[3][:]),
+			matchCount = self.matchCount,
+			matchLeft = self.matchLeft,
+			matchLimit = self.matchLimit,
+			matchFormatString = self.matchFormatString,
+			lastBegin = self.lastBegin,
+			justplay = self.justplay,
+			avoidDuplicateDescription = self.avoidDuplicateDescription,
+			bouquets = self.bouquets[:],
+			tags = self.tags[:],
+			encoding = self.encoding,
+			searchType = self.searchType,
+			searchCase = self.searchCase,
+			overrideAlternatives = self.overrideAlternatives
+		)
+
+	def __eq__(self, other):
+		if isinstance(other, AutoTimerComponent):
+			return self.id == other.id
+		return False
+
+	def __lt__(self, other):
+		if isinstance(other, AutoTimerComponent):
+			return self.name.lower() < other.name.lower()
+		return False
+
+	def __ne__(self, other):
+		return not self.__eq__(other)
+
+	def __repr__(self):
+		return ''.join((
+			'<AutomaticTimer ',
+			self.name,
+			' (',
+			', '.join((
+					str(self.match),
+					str(self.encoding),
+					str(self.searchCase),
+					str(self.searchType),
+			 		str(self.timespan),
+			 		str(self.services),
+			 		str(self.offset),
+			 		str(self.afterevent),
+			 		str(([x.pattern for x in self.exclude[0]],
+						[x.pattern for x in self.exclude[1]],
+						[x.pattern for x in self.exclude[2]],
+						self.exclude[3]
+					)),
+			 		str(([x.pattern for x in self.include[0]],
+						[x.pattern for x in self.include[1]],
+						[x.pattern for x in self.include[2]],
+						self.include[3]
+					)),
+			 		str(self.maxduration),
+			 		str(self.enabled),
+			 		str(self.destination),
+			 		str(self.matchCount),
+			 		str(self.matchLeft),
+			 		str(self.matchLimit),
+			 		str(self.matchFormatString),
+			 		str(self.lastBegin),
+			 		str(self.justplay),
+			 		str(self.avoidDuplicateDescription),
+					str(self.bouquets),
+					str(self.tags),
+					str(self.overrideAlternatives),
+			 )),
+			 ")>"
+		))
+
Index: /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/AutoTimerConfiguration.py
===================================================================
--- /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/AutoTimerConfiguration.py	(revision 8760)
+++ /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/AutoTimerConfiguration.py	(revision 8760)
@@ -0,0 +1,708 @@
+# -*- coding: UTF-8 -*-
+# for localized messages
+from . import _
+
+from AutoTimerComponent import AutoTimerComponent
+from RecordTimer import AFTEREVENT
+from Tools.XMLTools import stringToXML
+from ServiceReference import ServiceReference
+
+from enigma import eServiceReference
+
+CURRENT_CONFIG_VERSION = "5"
+
+def getValue(definitions, default):
+	# Initialize Output
+	ret = ""
+
+	# How many definitions are present
+	if isinstance(definitions, list):
+		Len = len(definitions)
+		if Len > 0:
+			childNodes = definitions[Len-1].text
+		else:
+			childNodes = ""
+	else:
+		ret = definitions.text
+
+	# Return stripped output or (if empty) default
+	return ret.strip() or default
+
+def parseConfig(configuration, list, version = None, uniqueTimerId = 0, defaultTimer = None):
+	if version != CURRENT_CONFIG_VERSION:
+		parseConfigOld(configuration, list, uniqueTimerId)
+		return
+
+	if defaultTimer is not None:
+		# Read in defaults for a new timer
+		for defaults in configuration.findall("defaults"):
+			parseEntry(defaults, defaultTimer, True)
+
+	for timer in configuration.findall("timer"):
+		uniqueTimerId += 1
+		baseTimer = AutoTimerComponent(
+			uniqueTimerId,
+			'',
+			'',
+			True
+		)
+
+		if parseEntry(timer, baseTimer):
+			list.append(baseTimer)
+
+def parseEntry(element, baseTimer, defaults = False):
+	if not defaults:
+		# Read out match
+		baseTimer.match = element.get("match", "").encode("UTF-8")
+		if not baseTimer.match:
+			print '[AutoTimer] Erroneous config is missing attribute "match", skipping entry'
+			return False
+
+		# Read out name
+		baseTimer.name = element.get("name", "").encode("UTF-8")
+		if not baseTimer.name:
+			print '[AutoTimer] Timer is missing attribute "name", defaulting to match'
+			baseTimer.name = baseTimer.match
+
+		# Read out enabled
+		enabled = element.get("enabled", "yes")
+		if enabled == "no":
+			baseTimer.enabled = False
+		elif enabled == "yes":
+			baseTimer.enabled = True
+		else:
+			print '[AutoTimer] Erroneous config contains invalid value for "enabled":', enabled,', disabling'
+			baseTimer.enabled = False
+
+	# Read out encoding (won't change if no value is set)
+	baseTimer.encoding = element.get("encoding")
+
+	# Read out search type/case
+	baseTimer.searchType = element.get("searchType", baseTimer.searchType)
+	baseTimer.searchCase = element.get("searchCase", baseTimer.searchCase)
+
+	# Read out if we should change to alternative services
+	baseTimer.overrideAlternatives = int(element.get("overrideAlternatives", baseTimer.overrideAlternatives))
+
+	# Read out timespan
+	start = element.get("from")
+	end = element.get("to")
+	if start and end:
+		start = [int(x) for x in start.split(':')]
+		end = [int(x) for x in end.split(':')]
+		baseTimer.timespan = (start, end)
+
+	# Read out max length
+	maxduration = element.get("maxduration")
+	if maxduration:
+		baseTimer.maxduration = int(maxduration)*60
+
+	# Read out recording path
+	default = baseTimer.destination or ""
+	baseTimer.destination = element.get("location", default).encode("UTF-8") or None
+
+	# Read out offset
+	offset = element.get("offset")
+	if offset:
+		offset = offset.split(",")
+		if len(offset) == 1:
+			before = after = int(offset[0] or 0) * 60
+		else:
+			before = int(offset[0] or 0) * 60
+			after = int(offset[1] or 0) * 60
+		baseTimer.offset = (before, after)
+
+	# Read out counter
+	baseTimer.matchCount = int(element.get("counter", 0))
+	baseTimer.matchFormatString = element.get("counterFormat", "")
+	if not defaults:
+		baseTimer.matchLeft = int(element.get("left", baseTimer.matchCount))
+		baseTimer.matchLimit = element.get("lastActivation", "")
+		baseTimer.lastBegin = int(element.get("lastBegin", 0))
+
+	# Read out justplay
+	baseTimer.justplay = int(element.get("justplay", 0))
+
+	# Read out avoidDuplicateDescription
+	baseTimer.avoidDuplicateDescription = int(element.get("avoidDuplicateDescription", 0))
+
+	# Read out allowed services
+	l = element.findall("serviceref")
+	if l:
+		servicelist = []
+
+		for service in l:
+			value = service.text
+			if value:
+				myref = eServiceReference(str(value))
+				if not (myref.flags & eServiceReference.isGroup):
+					# strip all after last :
+					pos = value.rfind(':')
+					if pos != -1:
+						value = value[:pos+1]
+
+				servicelist.append(value)
+		baseTimer.services = servicelist
+
+	# Read out allowed bouquets
+	l = element.findall("bouquet")
+	if l:
+		bouquets = []
+		for bouquet in l:
+			value = bouquet.text
+			if value:
+				bouquets.append(value)
+		baseTimer.bouquets = bouquets
+
+	# Read out afterevent
+	l = element.findall("afterevent")
+	if l:
+		idx = {
+			"none": AFTEREVENT.NONE,
+			"deepstandby": AFTEREVENT.DEEPSTANDBY,
+			"shutdown": AFTEREVENT.DEEPSTANDBY,
+			"standby": AFTEREVENT.STANDBY,
+			"auto": AFTEREVENT.AUTO
+		}
+		afterevents = []
+		for afterevent in l:
+			value = afterevent.text
+
+			if idx.has_key(value):
+				value = idx[value]
+			else:
+				print '[AutoTimer] Erroneous config contains invalid value for "afterevent":', afterevent,', ignoring definition'
+				continue
+
+			start = afterevent.get("from")
+			end = afterevent.get("to")
+			if start and end:
+				start = [int(x) for x in start.split(':')]
+				end = [int(x) for x in end.split(':')]
+				afterevents.append((value, (start, end)))
+			else:
+				afterevents.append((value, None))
+		baseTimer.afterevent = afterevents
+
+	# Read out exclude
+	l = element.findall("exclude")
+	idx = {"title": 0, "shortdescription": 1, "description": 2, "dayofweek": 3}
+	if l:
+		excludes = ([], [], [], [])
+		for exclude in l:
+			where = exclude.get("where")
+			value = exclude.text
+			if not (value and where):
+				continue
+
+			if idx.has_key(where):
+				excludes[idx[where]].append(value.encode("UTF-8"))
+		baseTimer.exclude = excludes
+
+	# Read out includes (use same idx)
+	l = element.findall("include")
+	if l:
+		includes = ([], [], [], [])
+		for include in l:
+			where = include.get("where")
+			value = include.text
+			if not (value and where):
+				continue
+
+			if idx.has_key(where):
+				includes[idx[where]].append(value.encode("UTF-8"))
+		baseTimer.include = includes
+
+	# Read out recording tags
+	l =  element.findall("tag")
+	if l:
+		tags = []
+		for tag in l:
+			value = tag.text
+			if not value:
+				continue
+
+			tags.append(value.encode("UTF-8"))
+		baseTimer.tags = tags
+
+	return True
+
+def parseConfigOld(configuration, list, uniqueTimerId = 0):
+	print "[AutoTimer] Trying to parse old config"
+
+	# Iterate Timers
+	for timer in configuration.findall("timer"):
+		# Increment uniqueTimerId
+		uniqueTimerId += 1
+
+		# Get name (V2+)
+		name = timer.get("name")
+		if name:
+			name = name.encode("UTF-8")
+		# Get name (= match) (V1)
+		else:
+			# Read out name
+			name = getValue(timer.findall("name"), "").encode("UTF-8")
+
+		if not name:
+			print '[AutoTimer] Erroneous config is missing attribute "name", skipping entry'
+			continue
+
+		# Read out match (V3+)
+		match = timer.get("match")
+		if match:
+			# Read out match
+			match = match.encode("UTF-8")
+			if not match:
+				print '[AutoTimer] Erroneous config contains empty attribute "match", skipping entry'
+				continue
+		# V2-
+		else:
+			# Setting match to name
+			match = name
+
+
+		# See if Timer is ensabled (V2+)
+		enabled = timer.get("enabled")
+		if enabled:
+			if enabled == "no":
+				enabled = False
+			elif enabled == "yes":
+				enabled = True
+			else:
+				print '[AutoTimer] Erroneous config contains invalid value for "enabled":', enabled,', skipping entry'
+				enabled = False
+		# V1
+		else:
+			elements = timer.findall("enabled")
+			if elements:
+				if getValue(elements, "yes") == "no":
+					enabled = False
+				else:
+					enabled = True
+			else:
+				enabled = True
+
+		# Read out timespan (V4+; Falling back on missing definition should be OK)
+		start = timer.get("from")
+		end = timer.get("to")
+		if start and end:
+			start = [int(x) for x in start.split(':')]
+			end = [int(x) for x in end.split(':')]
+			timetuple = (start, end)
+		# V3-
+		else:
+			elements = timer.findall("timespan")
+			Len = len(elements)
+			if Len:
+				# Read out last definition
+				start = elements[Len-1].get("from")
+				end = elements[Len-1].get("to")
+				if start and end:
+					start = [int(x) for x in start.split(':')]
+					end = [int(x) for x in end.split(':')]
+					timetuple = (start, end)
+				else:
+					print '[AutoTimer] Erroneous config contains invalid definition of "timespan", ignoring definition'
+					timetuple = None
+			else:
+				timetuple = None
+
+		# Read out allowed services (V*)
+		elements = timer.findall("serviceref")
+		if elements:
+			servicelist = []
+			for service in elements:
+				value = service.text
+				if value:
+					myref = eServiceReference(str(value))
+					if not (myref.flags & eServiceReference.isGroup):
+						# strip all after last :
+						pos = value.rfind(':')
+						if pos != -1:
+							value = value[:pos+1]
+
+					servicelist.append(value)
+		else:
+			servicelist = None
+
+		# Read out allowed bouquets (V* though officially supported since V4)
+		bouquets = []
+		for bouquet in timer.findall("bouquet"):
+			value = bouquet.text
+			if value:
+				bouquets.append(value)
+
+		# Read out offset (V4+)
+		offset = timer.get("offset")
+		if offset:
+			offset = offset.split(",")
+			if len(offset) == 1:
+				before = after = int(offset[0] or 0) * 60
+			else:
+				before = int(offset[0] or 0) * 60
+				after = int(offset[1] or 0) * 60
+			offset = (before, after)
+		# V3-
+		else:
+			elements = timer.findall("offset")
+			Len = len(elements)
+			if Len:
+				value = elements[Len-1].get("both")
+				if value == '':
+					before = int(elements[Len-1].get("before", 0)) * 60
+					after = int(elements[Len-1].get("after", 0)) * 60
+				else:
+					before = after = int(value) * 60
+				offset = (before, after)
+			else:
+				offset = None
+
+		# Read out counter
+		counter = int(timer.get("counter", '0'))
+		counterLeft = int(timer.get("left", counter))
+		counterLimit = timer.get("lastActivation")
+		counterFormat = timer.get("counterFormat", "")
+		lastBegin = int(timer.get("lastBegin", 0))
+
+		# Read out justplay
+		justplay = int(timer.get("justplay", '0'))
+
+		# Read out avoidDuplicateDescription
+		avoidDuplicateDescription = int(timer.get("avoidDuplicateDescription", 0))
+
+		# Read out afterevent (compatible to V* though behaviour for V3- is different as V4+ allows multiple afterevents while the last definication was chosen before)
+		idx = {
+			"none": AFTEREVENT.NONE,
+			"deepstandby": AFTEREVENT.DEEPSTANDBY,
+			"shutdown": AFTEREVENT.DEEPSTANDBY,
+			"standby": AFTEREVENT.STANDBY,
+			"auto": AFTEREVENT.AUTO
+		}
+		afterevent = []
+		for element in timer.findall("afterevent"):
+			value = element.text
+
+			if idx.has_key(value):
+				value = idx[value]
+			else:
+				print '[AutoTimer] Erroneous config contains invalid value for "afterevent":', afterevent,', ignoring definition'
+				continue
+
+			start = element.get("from")
+			end = element.get("to")
+			if start and end:
+				start = [int(x) for x in start.split(':')]
+				end = [int(x) for x in end.split(':')]
+				afterevent.append((value, (start, end)))
+			else:
+				afterevent.append((value, None))
+
+		# Read out exclude (V*)
+		idx = {"title": 0, "shortdescription": 1, "description": 2, "dayofweek": 3}
+		excludes = ([], [], [], [])
+		for exclude in timer.findall("exclude"):
+			where = exclude.get("where")
+			value = exclude.text
+			if not (value and where):
+				continue
+
+			if idx.has_key(where):
+				excludes[idx[where]].append(value.encode("UTF-8"))
+
+		# Read out includes (use same idx) (V4+ feature, should not harm V3-)
+		includes = ([], [], [], [])
+		for include in timer.findall("include"):
+			where = include.get("where")
+			value = include.text
+			if not (value and where):
+				continue
+
+			if idx.has_key(where):
+				includes[idx[where]].append(value.encode("UTF-8"))
+
+		# Read out max length (V4+)
+		maxlen = timer.get("maxduration")
+		if maxlen:
+			maxlen = int(maxlen)*60
+		# V3-
+		else:
+			elements = timer.findall("maxduration")
+			if elements:
+				maxlen = getValue(elements, None)
+				if maxlen is not None:
+					maxlen = int(maxlen)*60
+			else:
+				maxlen = None
+
+		# Read out recording path
+		destination = timer.get("destination", "").encode("UTF-8") or None
+
+		# Read out recording tags
+		tags = []
+		for tag in timer.findall("tag"):
+			value = tag.text
+			if not value:
+				continue
+
+			tags.append(value.encode("UTF-8"))
+
+		# Finally append timer
+		list.append(AutoTimerComponent(
+				uniqueTimerId,
+				name,
+				match,
+				enabled,
+				timespan = timetuple,
+				services = servicelist,
+				offset = offset,
+				afterevent = afterevent,
+				exclude = excludes,
+				include = includes,
+				maxduration = maxlen,
+				destination = destination,
+				matchCount = counter,
+				matchLeft = counterLeft,
+				matchLimit = counterLimit,
+				matchFormatString = counterFormat,
+				lastBegin = lastBegin,
+				justplay = justplay,
+				avoidDuplicateDescription = avoidDuplicateDescription,
+				bouquets = bouquets,
+				tags = tags
+		))
+
+def writeConfig(filename, defaultTimer, timers):
+	# Generate List in RAM
+	list = ['<?xml version="1.0" ?>\n<autotimer version="', CURRENT_CONFIG_VERSION, '">\n\n']
+
+	# This gets deleted afterwards if we do not have set any defaults
+	list.append(' <defaults')
+
+	# Timespan
+	if defaultTimer.hasTimespan():
+		list.extend((' from="', defaultTimer.getTimespanBegin(), '" to="', defaultTimer.getTimespanEnd(), '"'))
+
+	# Duration
+	if defaultTimer.hasDuration():
+		list.extend((' maxduration="', str(defaultTimer.getDuration()), '"'))
+
+	# Destination
+	if defaultTimer.hasDestination():
+		list.extend((' location="', stringToXML(defaultTimer.destination), '"'))
+
+	# Offset
+	if defaultTimer.hasOffset():
+		if defaultTimer.isOffsetEqual():
+			list.extend((' offset="', str(defaultTimer.getOffsetBegin()), '"'))
+		else:
+			list.extend((' offset="', str(defaultTimer.getOffsetBegin()), ',', str(defaultTimer.getOffsetEnd()), '"'))
+
+	# Counter
+	if defaultTimer.hasCounter():
+		list.extend((' counter="', str(defaultTimer.getCounter()), '"'))
+		if defaultTimer.hasCounterFormatString():
+			list.extend((' counterFormat="', str(defaultTimer.getCounterFormatString()), '"'))
+
+	# Duplicate Description
+	if defaultTimer.getAvoidDuplicateDescription():
+		list.append(' avoidDuplicateDescription="1" ')
+
+	# Only display justplay if true
+	if defaultTimer.justplay:
+		list.extend((' justplay="', str(defaultTimer.getJustplay()), '"'))
+
+	# Only display encoding if != utf-8
+	if defaultTimer.encoding != 'UTF-8':
+		list.extend((' encoding="', str(defaultTimer.encoding), '"'))
+
+	# Only display searchType if exact
+	if defaultTimer.searchType == "exact":
+		list.extend((' searchType="', str(defaultTimer.searchType), '"'))
+
+	# Only display searchCase if sensitive
+	if defaultTimer.searchCase == "sensitive":
+		list.extend((' searchCase="', str(defaultTimer.searchCase), '"'))
+
+	# Close still opened defaults tag
+	list.append('>\n')
+
+	# Services
+	for serviceref in defaultTimer.services:
+		list.extend(('  <serviceref>', serviceref, '</serviceref>'))
+		ref = ServiceReference(str(serviceref))
+		list.extend((' <!-- ', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), ' -->\n'))
+
+	# Bouquets
+	for bouquet in defaultTimer.bouquets:
+		list.extend(('  <bouquet>', str(bouquet), '</bouquet>'))
+		ref = ServiceReference(str(bouquet))
+		list.extend((' <!-- ', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), ' -->\n'))
+
+	# AfterEvent
+	if defaultTimer.hasAfterEvent():
+		idx = {
+			AFTEREVENT.NONE: "none",
+			AFTEREVENT.STANDBY: "standby",
+			AFTEREVENT.DEEPSTANDBY: "shutdown",
+			AFTEREVENT.AUTO: "auto"
+		}
+		for afterevent in defaultTimer.afterevent:
+			action, timespan = afterevent
+			list.append('  <afterevent')
+			if timespan[0] is not None:
+				list.append(' from="%02d:%02d" to="%02d:%02d"' % (timespan[0][0], timespan[0][1], timespan[1][0], timespan[1][1]))
+			list.extend(('>', idx[action], '</afterevent>\n'))
+
+	# Excludes
+	for title in defaultTimer.getExcludedTitle():
+		list.extend(('  <exclude where="title">', stringToXML(title), '</exclude>\n'))
+	for short in defaultTimer.getExcludedShort():
+		list.extend(('  <exclude where="shortdescription">', stringToXML(short), '</exclude>\n'))
+	for desc in defaultTimer.getExcludedDescription():
+		list.extend(('  <exclude where="description">', stringToXML(desc), '</exclude>\n'))
+	for day in defaultTimer.getExcludedDays():
+		list.extend(('  <exclude where="dayofweek">', stringToXML(day), '</exclude>\n'))
+
+	# Includes
+	for title in defaultTimer.getIncludedTitle():
+		list.extend(('  <include where="title">', stringToXML(title), '</include>\n'))
+	for short in defaultTimer.getIncludedShort():
+		list.extend(('  <include where="shortdescription">', stringToXML(short), '</include>\n'))
+	for desc in defaultTimer.getIncludedDescription():
+		list.extend(('  <include where="description">', stringToXML(desc), '</include>\n'))
+	for day in defaultTimer.getIncludedDays():
+		list.extend(('  <include where="dayofweek">', stringToXML(day), '</include>\n'))
+
+	# Tags
+	for tag in defaultTimer.tags:
+		list.extend(('  <tag>', stringToXML(tag), '</tag>\n'))
+
+	# Keep the list clean
+	if len(list) == 5:
+		list.pop() # >
+		list.pop() # <defaults
+	else:
+		list.append(' </defaults>\n\n')
+
+	# Iterate timers
+	for timer in timers:
+		# Common attributes (match, enabled)
+		list.extend((' <timer name="', stringToXML(timer.name), '" match="', stringToXML(timer.match), '" enabled="', timer.getEnabled(), '"'))
+
+		# Timespan
+		if timer.hasTimespan():
+			list.extend((' from="', timer.getTimespanBegin(), '" to="', timer.getTimespanEnd(), '"'))
+
+		# Duration
+		if timer.hasDuration():
+			list.extend((' maxduration="', str(timer.getDuration()), '"'))
+
+		# Destination
+		if timer.hasDestination():
+			list.extend((' location="', stringToXML(timer.destination), '"'))
+
+		# Offset
+		if timer.hasOffset():
+			if timer.isOffsetEqual():
+				list.extend((' offset="', str(timer.getOffsetBegin()), '"'))
+			else:
+				list.extend((' offset="', str(timer.getOffsetBegin()), ',', str(timer.getOffsetEnd()), '"'))
+
+		# Counter
+		if timer.hasCounter():
+			list.extend((' lastBegin="', str(timer.getLastBegin()), '" counter="', str(timer.getCounter()), '" left="', str(timer.getCounterLeft()) ,'"'))
+			if timer.hasCounterFormatString():
+				list.extend((' lastActivation="', str(timer.getCounterLimit()), '"'))
+				list.extend((' counterFormat="', str(timer.getCounterFormatString()), '"'))
+
+		# Duplicate Description
+		if timer.getAvoidDuplicateDescription():
+			list.extend((' avoidDuplicateDescription="', str(timer.getAvoidDuplicateDescription()), '"'))
+
+		# Only display justplay if true
+		if timer.justplay:
+			list.extend((' justplay="', str(timer.getJustplay()), '"'))
+
+		# Only display encoding if != utf-8
+		if timer.encoding != 'UTF-8':
+			list.extend((' encoding="', str(timer.encoding), '"'))
+
+		# Only display searchType if exact
+		if timer.searchType == "exact":
+			list.extend((' searchType="', str(timer.searchType), '"'))
+
+		# Only display searchCase if sensitive
+		if timer.searchCase == "sensitive":
+			list.extend((' searchCase="', str(timer.searchCase), '"'))
+
+		# Only display overrideAlternatives if true
+		if timer.overrideAlternatives:
+			list.extend((' overrideAlternatives="', str(timer.getOverrideAlternatives()), '"'))
+
+		# Close still opened timer tag
+		list.append('>\n')
+
+		# Services
+		for serviceref in timer.services:
+			list.extend(('  <serviceref>', serviceref, '</serviceref>'))
+			ref = ServiceReference(str(serviceref))
+			list.extend((' <!-- ', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), ' -->\n'))
+
+		# Bouquets
+		for bouquet in timer.bouquets:
+			list.extend(('  <bouquet>', str(bouquet), '</bouquet>'))
+			ref = ServiceReference(str(bouquet))
+			list.extend((' <!-- ', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), ' -->\n'))
+
+		# AfterEvent
+		if timer.hasAfterEvent():
+			idx = {
+				AFTEREVENT.NONE: "none",
+				AFTEREVENT.STANDBY: "standby",
+				AFTEREVENT.DEEPSTANDBY: "shutdown",
+				AFTEREVENT.AUTO: "auto"
+			}
+			for afterevent in timer.afterevent:
+				action, timespan = afterevent
+				list.append('  <afterevent')
+				if timespan[0] is not None:
+					list.append(' from="%02d:%02d" to="%02d:%02d"' % (timespan[0][0], timespan[0][1], timespan[1][0], timespan[1][1]))
+				list.extend(('>', idx[action], '</afterevent>\n'))
+
+		# Excludes
+		for title in timer.getExcludedTitle():
+			list.extend(('  <exclude where="title">', stringToXML(title), '</exclude>\n'))
+		for short in timer.getExcludedShort():
+			list.extend(('  <exclude where="shortdescription">', stringToXML(short), '</exclude>\n'))
+		for desc in timer.getExcludedDescription():
+			list.extend(('  <exclude where="description">', stringToXML(desc), '</exclude>\n'))
+		for day in timer.getExcludedDays():
+			list.extend(('  <exclude where="dayofweek">', stringToXML(day), '</exclude>\n'))
+
+		# Includes
+		for title in timer.getIncludedTitle():
+			list.extend(('  <include where="title">', stringToXML(title), '</include>\n'))
+		for short in timer.getIncludedShort():
+			list.extend(('  <include where="shortdescription">', stringToXML(short), '</include>\n'))
+		for desc in timer.getIncludedDescription():
+			list.extend(('  <include where="description">', stringToXML(desc), '</include>\n'))
+		for day in timer.getIncludedDays():
+			list.extend(('  <include where="dayofweek">', stringToXML(day), '</include>\n'))
+
+		# Tags
+		for tag in timer.tags:
+			list.extend(('  <tag>', stringToXML(tag), '</tag>\n'))
+
+		# End of Timer
+		list.append(' </timer>\n\n')
+
+	# End of Configuration
+	list.append('</autotimer>\n')
+
+	# Save to Flash
+	file = open(filename, 'w')
+	file.writelines(list)
+
+	file.close()
+
Index: /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/AutoTimerEditor.py
===================================================================
--- /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/AutoTimerEditor.py	(revision 8760)
+++ /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/AutoTimerEditor.py	(revision 8760)
@@ -0,0 +1,1260 @@
+# -*- coding: UTF-8 -*-
+# for localized messages
+from . import _
+
+# GUI (Screens)
+from Screens.Screen import Screen
+from Components.ConfigList import ConfigListScreen
+from Screens.ChannelSelection import SimpleChannelSelection
+from Screens.EpgSelection import EPGSelection
+from Screens.MessageBox import MessageBox
+from Screens.ChoiceBox import ChoiceBox
+
+# GUI (Summary)
+from Screens.Setup import SetupSummary
+
+# GUI (Components)
+from Components.ActionMap import ActionMap
+from Components.Button import Button
+
+# Configuration
+from Components.config import getConfigListEntry, ConfigEnableDisable, \
+	ConfigYesNo, ConfigText, ConfigClock, ConfigNumber, ConfigSelection, \
+	config, NoSave
+
+# Timer
+from RecordTimer import AFTEREVENT
+
+# Needed to convert our timestamp back and forth
+from time import localtime, mktime
+
+# Show ServiceName instead of ServiceReference
+from ServiceReference import ServiceReference
+
+# addAutotimerFromService, AutoTimerChannelSelection
+from enigma import eServiceCenter, eServiceReference, iServiceInformation
+
+# Default Record Directory
+from Tools import Directories
+
+# Tags
+from Screens.MovieSelection import getPreferredTagEditor
+
+weekdays = [
+	("0", _("Monday")),
+	("1", _("Tuesday")),
+	("2", _("Wednesday")),
+	("3", _("Thursday")),
+	("4", _("Friday")),
+	("5", _("Saturday")),
+	("6", _("Sunday")),
+	("weekend", _("Weekend")),
+	("weekday", _("Weekday"))
+]
+
+class ExtendedConfigText(ConfigText):
+	def __init__(self, default = "", fixed_size = True, visible_width = False):
+		ConfigText.__init__(self, default = default, fixed_size = fixed_size, visible_width = visible_width)
+
+		# Workaround some characters currently not "typeable" using NumericalTextInput
+		mapping = self.mapping
+		if mapping:
+			if "&" not in mapping[0]:
+				mapping[0] += "&"
+			if ";" not in mapping[0]:
+				mapping[0] += ";"
+			if "%" not in mapping[0]:
+				mapping[0] += "%"
+
+class SimpleBouquetSelection(SimpleChannelSelection):
+	def __init__(self, session, title):
+		SimpleChannelSelection.__init__(self, session, title)
+		self.skinName = "SimpleChannelSelection"
+
+	def channelSelected(self):
+		ref = self.getCurrentSelection()
+		if (ref.flags & 7) == 7:
+			self.close(ref)
+		else:
+			# We return the currently active path here
+			# Asking the user if this is what he wants might be better though
+			self.close(self.servicePath[-1])
+
+class AutoTimerChannelSelection(SimpleChannelSelection):
+	def __init__(self, session, autotimer):
+		SimpleChannelSelection.__init__(self, session, _("Channel Selection"))
+		self.skinName = "SimpleChannelSelection"
+		self.autotimer = autotimer
+
+		self["ChannelSelectEPGActions"] = ActionMap(["ChannelSelectEPGActions"],
+			{
+				"showEPGList": self.channelSelected
+			}
+		)
+
+	def channelSelected(self):
+		ref = self.getCurrentSelection()
+		if (ref.flags & 7) == 7:
+			self.enterPath(ref)
+		elif not (ref.flags & eServiceReference.isMarker):
+			self.session.open(
+				AutoTimerEPGSelection,
+				ref
+			)
+
+class AutoTimerEPGSelection(EPGSelection):
+	def __init__(self, *args):
+		EPGSelection.__init__(self, *args)
+		self.skinName = "EPGSelection"
+
+	def infoKeyPressed(self):
+		self.timerAdd()
+
+	def timerAdd(self):
+		cur = self["list"].getCurrent()
+		evt = cur[0]
+		sref = cur[1]
+		if not evt:
+			return
+
+		addAutotimerFromEvent(self.session, evt = evt, service = sref)
+
+	def onSelectionChanged(self):
+		pass
+
+class AutoTimerEditorBase:
+	""" Base Class for all Editors """
+	def __init__(self, timer, editingDefaults = False):
+		# Keep Timer
+		self.timer = timer
+		self.editingDefaults = editingDefaults
+
+		# See if we are filtering some strings
+		excludes = (
+			timer.getExcludedTitle(),
+			timer.getExcludedShort(),
+			timer.getExcludedDescription(),
+			timer.getExcludedDays()
+		)
+		includes = (
+			timer.getIncludedTitle(),
+			timer.getIncludedShort(),
+			timer.getIncludedDescription(),
+			timer.getIncludedDays()
+		)
+		if excludes[0] or excludes[1] \
+				or excludes[2] or excludes[3] \
+				or includes[0] or includes[1] \
+				or includes[2] or includes[3]:
+			self.filterSet = True
+		else:
+			self.filterSet = False
+		self.excludes = excludes
+		self.includes = includes
+
+		# See if services are restricted
+		self.services = timer.services
+		self.bouquets = timer.bouquets
+		if self.services or self.bouquets:
+			self.serviceRestriction = True
+		else:
+			self.serviceRestriction = False
+
+		self.createSetup(timer)
+
+	def createSetup(self, timer):
+		# Name
+		self.name = NoSave(ExtendedConfigText(default = timer.name, fixed_size = False))
+
+		# Match
+		self.match = NoSave(ExtendedConfigText(default = timer.match, fixed_size = False))
+
+		# Encoding
+		default = timer.encoding
+		selection = ['UTF-8', 'ISO8859-15']
+		if default not in selection:
+			selection.append(default)
+		self.encoding = NoSave(ConfigSelection(choices = selection, default = default))
+
+		# ...
+		self.searchType = NoSave(ConfigSelection(choices = [("partial", _("partial match")), ("exact", _("exact match"))], default = timer.searchType))
+		self.searchCase = NoSave(ConfigSelection(choices = [("sensitive", _("case-sensitive search")), ("insensitive", _("case-insensitive search"))], default = timer.searchCase))
+
+		# Alternatives override
+		self.overrideAlternatives = NoSave(ConfigYesNo(default = timer.overrideAlternatives))
+
+		# Justplay
+		self.justplay = NoSave(ConfigSelection(choices = [("zap", _("zap")), ("record", _("record"))], default = {0: "record", 1: "zap"}[int(timer.justplay)]))
+
+		# Timespan
+		now = [x for x in localtime()]
+		if timer.hasTimespan():
+			default = True
+			now[3] = timer.timespan[0][0]
+			now[4] = timer.timespan[0][1]
+			begin = mktime(now)
+			now[3] = timer.timespan[1][0]
+			now[4] = timer.timespan[1][1]
+			end = mktime(now)
+		else:
+			default = False
+			now[3] = 20
+			now[4] = 15
+			begin = mktime(now)
+			now[3] = 23
+			now[4] = 15
+			end = mktime(now)
+		self.timespan = NoSave(ConfigEnableDisable(default = default))
+		self.timespanbegin = NoSave(ConfigClock(default = begin))
+		self.timespanend = NoSave(ConfigClock(default = end))
+
+		# Services have their own Screen
+
+		# Offset
+		if timer.hasOffset():
+			default = True
+			begin = timer.getOffsetBegin()
+			end = timer.getOffsetEnd()
+		else:
+			default = False
+			begin = 5
+			end = 5
+		self.offset = NoSave(ConfigEnableDisable(default = default))
+		self.offsetbegin = NoSave(ConfigNumber(default = begin))
+		self.offsetend = NoSave(ConfigNumber(default = end))
+
+		# AfterEvent
+		if timer.hasAfterEvent():
+			default = {
+				None: "default",
+				AFTEREVENT.NONE: "nothing",
+				AFTEREVENT.DEEPSTANDBY: "deepstandby",
+				AFTEREVENT.STANDBY: "standby",
+				AFTEREVENT.AUTO: "auto"
+			}[timer.afterevent[0][0]]
+		else:
+			default = "default"
+		self.afterevent = NoSave(ConfigSelection(choices = [
+			("default", _("standard")), ("nothing", _("do nothing")),
+			("standby", _("go to standby")),
+			("deepstandby", _("go to deep standby")),
+			("auto", _("auto"))], default = default))
+
+		# AfterEvent (Timespan)
+		if timer.hasAfterEvent() and timer.afterevent[0][1][0] is not None:
+			default = True
+			now[3] = timer.afterevent[0][1][0][0]
+			now[4] = timer.afterevent[0][1][0][1]
+			begin = mktime(now)
+			now[3] = timer.afterevent[0][1][1][0]
+			now[4] = timer.afterevent[0][1][1][1]
+			end = mktime(now)
+		else:
+			default = False
+			now[3] = 23
+			now[4] = 15
+			begin = mktime(now)
+			now[3] = 7
+			now[4] = 0
+			end = mktime(now)
+		self.afterevent_timespan = NoSave(ConfigEnableDisable(default = default))
+		self.afterevent_timespanbegin = NoSave(ConfigClock(default = begin))
+		self.afterevent_timespanend = NoSave(ConfigClock(default = end))
+
+		# Enabled
+		self.enabled = NoSave(ConfigYesNo(default = timer.enabled))
+
+		# Maxduration
+		if timer.hasDuration():
+			default = True
+			duration = timer.getDuration()
+		else:
+			default = False
+			duration =70
+		self.duration = NoSave(ConfigEnableDisable(default = default))
+		self.durationlength = NoSave(ConfigNumber(default = duration))
+
+		# Counter
+		if timer.hasCounter():
+			default = timer.matchCount
+		else:
+			default = 0
+		self.counter = NoSave(ConfigNumber(default = default))
+		self.counterLeft = NoSave(ConfigNumber(default = timer.matchLeft))
+		default = timer.getCounterFormatString()
+		selection = [("", _("Never")), ("%m", _("Monthly")), ("%U", _("Weekly (Sunday)")), ("%W", _("Weekly (Monday)"))]
+		if default not in ('', '%m', '%U', '%W'):
+			selection.append((default, _("Custom (%s)") % (default)))
+		self.counterFormatString = NoSave(ConfigSelection(selection, default = default))
+
+		# Avoid Duplicate Description
+		self.avoidDuplicateDescription = NoSave(ConfigSelection([
+				("0", _("No")),
+				("1", _("On same service")),
+				("2", _("On any service")),
+			],
+			default = str(timer.getAvoidDuplicateDescription())
+		))
+
+		# Custom Location
+		if timer.hasDestination():
+			default = True
+		else:
+			default = False
+
+		self.useDestination = NoSave(ConfigYesNo(default = default))
+
+		default = timer.destination or Directories.resolveFilename(Directories.SCOPE_HDD)
+		choices = config.movielist.videodirs.value
+
+		if default not in choices:
+			choices.append(default)
+		self.destination = NoSave(ConfigSelection(default = default, choices = choices))
+
+		# Tags
+		self.timerentry_tags = timer.tags
+		self.tags = NoSave(ConfigSelection(choices = [len(self.timerentry_tags) == 0 and _("None") or ' '.join(self.timerentry_tags)]))
+
+	def pathSelected(self, res):
+		if res is not None:
+			# I'm pretty sure this will always fail
+			if config.movielist.videodirs.value != self.destination.choices:
+					self.destination.setChoices(config.movielist.videodirs.value, default = res)
+			self.destination.value = res
+
+	def chooseDestination(self):
+		from Screens.LocationBox import MovieLocationBox
+
+		self.session.openWithCallback(
+			self.pathSelected,
+			MovieLocationBox,
+			_("Choose target folder"),
+			self.destination.value,
+			minFree = 100 # Same requirement as in Screens.TimerEntry
+		)
+
+	def tagEditFinished(self, ret):
+		if ret is not None:
+			self.timerentry_tags = ret
+			self.tags.setChoices([len(ret) == 0 and _("None") or ' '.join(ret)])
+
+	def chooseTags(self):
+		preferredTagEditor = getPreferredTagEditor()
+		if preferredTagEditor:
+			self.session.openWithCallback(
+				self.tagEditFinished,
+				preferredTagEditor,
+				self.timerentry_tags
+			)
+
+class AutoTimerEditor(Screen, ConfigListScreen, AutoTimerEditorBase):
+	"""Edit AutoTimer"""
+
+	skin = """<screen name="AutoTimerEdit" title="Edit AutoTimer" position="center,center" size="565,280">
+		<widget name="config" position="5,5" size="555,225" scrollbarMode="showOnDemand" />
+		<ePixmap position="0,235" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
+		<ePixmap position="140,235" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
+		<ePixmap position="280,235" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
+		<ePixmap position="420,235" zPosition="4" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
+		<widget name="key_red" position="0,235" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
+		<widget name="key_green" position="140,235" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
+		<widget name="key_yellow" position="280,235" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
+		<widget name="key_blue" position="420,235" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
+	</screen>"""
+
+	def __init__(self, session, timer, editingDefaults = False):
+		Screen.__init__(self, session)
+
+		AutoTimerEditorBase.__init__(self, timer, editingDefaults)
+
+		# Summary
+		self.setup_title = _("AutoTimer Editor")
+		self.onChangedEntry = []
+
+		# We might need to change shown items, so add some notifiers
+		self.timespan.addNotifier(self.reloadList, initial_call = False)
+		self.offset.addNotifier(self.reloadList, initial_call = False)
+		self.duration.addNotifier(self.reloadList, initial_call = False)
+		self.afterevent.addNotifier(self.reloadList, initial_call = False)
+		self.afterevent_timespan.addNotifier(self.reloadList, initial_call = False)
+		self.counter.addNotifier(self.reloadList, initial_call = False)
+		self.useDestination.addNotifier(self.reloadList, initial_call = False)
+
+		self.refresh()
+
+		ConfigListScreen.__init__(self, self.list, session = session, on_change = self.changed)
+
+		# Initialize Buttons
+		self["key_red"] = Button(_("Cancel"))
+		self["key_green"] = Button(_("OK"))
+		self["key_yellow"] = Button()
+ 		self["key_blue"] = Button()
+
+		# Set Button texts
+		self.renameServiceButton()
+		self.renameFilterButton()
+
+		# Define Actions
+		self["actions"] = ActionMap(["SetupActions", "ColorActions"],
+			{
+				"cancel": self.cancel,
+				"save": self.maybeSave,
+				"ok": self.ok,
+				"yellow": self.editFilter,
+				"blue": self.editServices
+			}, -2
+		)
+
+		# Trigger change
+		self.changed()
+
+		self.onLayoutFinish.append(self.setCustomTitle)
+
+	def setCustomTitle(self):
+		self.setTitle(_("Edit AutoTimer"))
+
+	def renameFilterButton(self):
+		if self.filterSet:
+			self["key_yellow"].setText(_("Edit Filters"))
+		else:
+			self["key_yellow"].setText(_("Add Filters"))
+
+	def renameServiceButton(self):
+		if self.serviceRestriction:
+			self["key_blue"].setText(_("Edit Services"))
+		else:
+			self["key_blue"].setText(_("Add Services"))
+
+	def changed(self):
+		for x in self.onChangedEntry:
+			try:
+				x()
+			except Exception:
+				pass
+
+	def getCurrentEntry(self):
+		return self["config"].getCurrent()[0]
+
+	def getCurrentValue(self):
+		return str(self["config"].getCurrent()[1].getText())
+
+	def createSummary(self):
+		return SetupSummary
+
+	def refresh(self):
+		# First three entries are only showed when not editing defaults
+		list = []
+		if not self.editingDefaults:
+			list.extend((
+				getConfigListEntry(_("Enabled"), self.enabled),
+				getConfigListEntry(_("Description"), self.name),
+				getConfigListEntry(_("Match Title"), self.match),
+			))
+
+		list.extend((
+			getConfigListEntry(_("EPG Encoding"), self.encoding),
+			getConfigListEntry(_("Search Type"), self.searchType),
+			getConfigListEntry(_("Search strictness"), self.searchCase),
+			getConfigListEntry(_("Timer Type"), self.justplay),
+			getConfigListEntry(_("Override found with alternative Service"), self.overrideAlternatives),
+			getConfigListEntry(_("Only match during Timespan"), self.timespan)
+		))
+
+		# Only allow editing timespan when it's enabled
+		if self.timespan.value:
+			list.extend((
+				getConfigListEntry(_("Begin of Timespan"), self.timespanbegin),
+				getConfigListEntry(_("End of Timespan"), self.timespanend)
+			))
+
+		list.append(getConfigListEntry(_("Custom offset"), self.offset))
+
+		# Only allow editing offsets when it's enabled
+		if self.offset.value:
+			list.extend((
+				getConfigListEntry(_("Offset before recording (in m)"), self.offsetbegin),
+				getConfigListEntry(_("Offset after recording (in m)"), self.offsetend)
+			))
+
+		list.append(getConfigListEntry(_("Set maximum Duration"), self.duration))
+
+		# Only allow editing maxduration when it's enabled
+		if self.duration.value:
+			list.append(getConfigListEntry(_("Maximum Duration (in m)"), self.durationlength))
+
+		list.append(getConfigListEntry(_("After event"), self.afterevent))
+
+		# Only allow setting afterevent timespan when afterevent is active
+		if self.afterevent.value != "default":
+			list.append(getConfigListEntry(_("Execute after Event during Timespan"), self.afterevent_timespan))
+
+			# Only allow editing timespan when it's enabled
+			if self.afterevent_timespan.value:
+				list.extend((
+					getConfigListEntry(_("Begin of after Event Timespan"), self.afterevent_timespanbegin),
+					getConfigListEntry(_("End of after Event Timespan"), self.afterevent_timespanend)
+				))
+
+		list.append(getConfigListEntry(_("Record a maximum of x times"), self.counter))
+
+		# Only allow setting matchLeft when counting hits
+		if self.counter.value:
+			if not self.editingDefaults:
+				list.append(getConfigListEntry(_("Ammount of recordings left"), self.counterLeft))
+			list.append(getConfigListEntry(_("Reset Count"), self.counterFormatString))
+
+		list.append(getConfigListEntry(_("Require Description to be unique"), self.avoidDuplicateDescription))
+
+		# We always add this option though its expert only in enigma2
+		list.append(getConfigListEntry(_("Use a custom location"), self.useDestination))
+		if self.useDestination.value:
+			list.append(getConfigListEntry(_("Custom Location"), self.destination))
+
+		list.append(getConfigListEntry(_("Tags"), self.tags))
+
+		self.list = list
+
+	def reloadList(self, value):
+		self.refresh()
+		self["config"].setList(self.list)
+
+	def editFilter(self):
+		self.session.openWithCallback(
+			self.editFilterCallback,
+			AutoTimerFilterEditor,
+			self.filterSet,
+			self.excludes,
+			self.includes
+		)
+
+	def editFilterCallback(self, ret):
+		if ret:
+			self.filterSet = ret[0]
+			self.excludes = ret[1]
+			self.includes = ret[2]
+			self.renameFilterButton()
+
+	def editServices(self):
+		self.session.openWithCallback(
+			self.editServicesCallback,
+			AutoTimerServiceEditor,
+			self.serviceRestriction,
+			self.services,
+			self.bouquets
+		)
+
+	def editServicesCallback(self, ret):
+		if ret:
+			self.serviceRestriction = ret[0]
+			self.services = ret[1][0]
+			self.bouquets = ret[1][1]
+			self.renameServiceButton()
+
+	def ok(self):
+		cur = self["config"].getCurrent()
+		cur = cur and cur[1]
+		if cur == self.destination:
+			self.chooseDestination()
+		elif cur == self.tags:
+			self.chooseTags()
+		else:
+			ConfigListScreen.keyOK(self)
+
+	def cancel(self):
+		if self["config"].isChanged():
+			self.session.openWithCallback(
+				self.cancelConfirm,
+				MessageBox,
+				_("Really close without saving settings?")
+			)
+		else:
+			self.close(None)
+
+	def cancelConfirm(self, ret):
+		if ret:
+			self.close(None)
+
+	def maybeSave(self):
+		if self.editingDefaults:
+			self.save()
+			return
+		# Check if any match is set
+		if not self.match.value.strip():
+			self.session.open(
+					MessageBox,
+					_("The match attribute is mandatory."),
+					type = MessageBox.TYPE_ERROR,
+					timeout = 5
+			)
+		# Check if we have a trailing whitespace
+		elif self.match.value[-1:] == " ":
+			self.session.openWithCallback(
+				self.saveCallback,
+				MessageBox,
+				_('You entered "%s" as Text to match.\nDo you want to remove trailing whitespaces?') % (self.match.value)
+			)
+		# Just save else
+		else:
+			self.save()
+
+	def saveCallback(self, ret):
+		if ret is not None:
+			if ret:
+				self.match.value = self.match.value.rstrip()
+			self.save()
+		# Don't to anything if MessageBox was canceled!
+
+	def save(self):
+		# Match
+		self.timer.match = self.match.value
+
+		# Name
+		self.timer.name = self.name.value.strip() or self.timer.match
+
+		# Encoding
+		self.timer.encoding = self.encoding.value
+
+		# ...
+		self.timer.searchType = self.searchType.value
+		self.timer.searchCase = self.searchCase.value
+
+		# Alternatives
+		self.timer.overrideAlternatives = self.overrideAlternatives.value
+
+		# Enabled
+		self.timer.enabled = self.enabled.value
+
+		# Justplay
+		self.timer.justplay = self.justplay.value == "zap"
+
+		# Timespan
+		if self.timespan.value:
+			start = self.timespanbegin.value
+			end = self.timespanend.value
+			self.timer.timespan = (start, end)
+		else:
+			self.timer.timespan = None
+
+		# Services
+		if self.serviceRestriction:
+			self.timer.services = self.services
+			self.timer.bouquets = self.bouquets
+		else:
+			self.timer.services = None
+			self.timer.bouquets = None
+
+		# Offset
+		if self.offset.value:
+			self.timer.offset = (self.offsetbegin.value*60, self.offsetend.value*60)
+		else:
+			self.timer.offset = None
+
+		# AfterEvent
+		if self.afterevent.value == "default":
+			self.timer.afterevent = []
+		else:
+			afterevent = {
+				"nothing": AFTEREVENT.NONE,
+				"deepstandby": AFTEREVENT.DEEPSTANDBY,
+				"standby": AFTEREVENT.STANDBY,
+				"auto": AFTEREVENT.AUTO
+			}[self.afterevent.value]
+			# AfterEvent Timespan
+			if self.afterevent_timespan.value:
+				start = self.afterevent_timespanbegin.value
+				end = self.afterevent_timespanend.value
+				self.timer.afterevent = [(afterevent, (start, end))]
+			else:
+				self.timer.afterevent = [(afterevent, None)]
+
+		# Maxduration
+		if self.duration.value:
+			self.timer.maxduration = self.durationlength.value*60
+		else:
+			self.timer.maxduration = None
+
+		# Ex-&Includes
+		if self.filterSet:
+			self.timer.exclude = self.excludes
+			self.timer.include = self.includes
+		else:
+			self.timer.exclude = None
+			self.timer.include = None
+
+		# Counter
+		if self.counter.value:
+			self.timer.matchCount = self.counter.value
+			if self.counterLeft.value <= self.counter.value:
+				self.timer.matchLeft = self.counterLeft.value
+			else:
+				self.timer.matchLeft = self.counter.value
+			if self.counterFormatString.value:
+				self.timer.matchFormatString = self.counterFormatString.value
+			else:
+				self.timer.matchFormatString = ''
+		else:
+			self.timer.matchCount = 0
+			self.timer.matchLeft = 0
+			self.timer.matchFormatString = ''
+
+		self.timer.avoidDuplicateDescription = int(self.avoidDuplicateDescription.value)
+
+		if self.useDestination.value:
+			self.timer.destination = self.destination.value
+		else:
+			self.timer.destination = None
+
+		self.timer.tags = self.timerentry_tags
+
+		# Close
+		self.close(self.timer)
+
+class AutoTimerFilterEditor(Screen, ConfigListScreen):
+	"""Edit AutoTimer Filter"""
+
+	skin = """<screen name="AutoFilterEditor" title="Edit AutoTimer Filters" position="center,center" size="565,245">
+		<widget name="config" position="5,5" size="555,200" scrollbarMode="showOnDemand" />
+		<ePixmap position="5,205" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
+		<ePixmap position="145,205" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
+		<ePixmap position="285,205" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
+		<ePixmap position="425,205" zPosition="4" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
+		<widget name="key_red" position="5,205" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
+		<widget name="key_green" position="145,205" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
+		<widget name="key_yellow" position="285,205" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
+		<widget name="key_blue" position="425,205" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
+	</screen>"""
+
+	def __init__(self, session, filterset, excludes, includes):
+		Screen.__init__(self, session)
+
+		# Summary
+		self.setup_title = _("AutoTimer Filters")
+		self.onChangedEntry = []
+
+		self.typeSelection = NoSave(ConfigSelection(choices = [
+			("title", _("in Title")),
+			("short", _("in Shortdescription")),
+			("desc", _("in Description")),
+			("day", _("on Weekday"))]
+		))
+		self.typeSelection.addNotifier(self.refresh, initial_call = False)
+
+		self.enabled = NoSave(ConfigEnableDisable(default = filterset))
+
+		self.excludes = excludes
+		self.includes = includes
+
+		self.reloadList()
+
+		ConfigListScreen.__init__(self, self.list, session = session, on_change = self.changed)
+
+		# Initialize Buttons
+		self["key_red"] = Button(_("Cancel"))
+		self["key_green"] = Button(_("Save"))
+		self["key_yellow"] = Button(_("delete"))
+		self["key_blue"] = Button(_("New"))
+
+		# Define Actions
+		self["actions"] = ActionMap(["SetupActions", "ColorActions"],
+			{
+				"cancel": self.cancel,
+				"save": self.save,
+				"yellow": self.remove,
+				"blue": self.new
+			}
+		)
+
+		# Trigger change
+		self.changed()
+
+		self.onLayoutFinish.append(self.setCustomTitle)
+
+	def setCustomTitle(self):
+		self.setTitle(_("Edit AutoTimer Filters"))
+
+
+	def changed(self):
+		for x in self.onChangedEntry:
+			try:
+				x()
+			except Exception:
+				pass
+
+	def getCurrentEntry(self):
+		return self["config"].getCurrent()[0]
+
+	def getCurrentValue(self):
+		return str(self["config"].getCurrent()[1].getText())
+
+	def createSummary(self):
+		return SetupSummary
+
+	def saveCurrent(self):
+		del self.excludes[self.idx][:]
+		del self.includes[self.idx][:]
+
+		# Warning, accessing a ConfigListEntry directly might be considered evil!
+
+		idx = -1
+		for item in self["config"].getList()[:]:
+			idx += 1
+			# Skip empty entries (and those which are no filters)
+			if item[1].value == "" or idx < 2:
+				continue
+			elif idx < self.lenExcludes:
+				self.excludes[self.idx].append(item[1].value.encode("UTF-8"))
+			else:
+				self.includes[self.idx].append(item[1].value.encode("UTF-8"))
+
+	def refresh(self, *args, **kwargs):
+		self.saveCurrent()
+
+		self.reloadList()
+		self["config"].setList(self.list)
+
+	def reloadList(self):
+		self.list = [
+			getConfigListEntry(_("Enable Filtering"), self.enabled),
+			getConfigListEntry(_("Filter"), self.typeSelection)
+		]
+
+		if self.typeSelection.value == "day":
+			self.idx = 3
+
+			# Weekdays are presented as ConfigSelection
+			self.list.extend([
+				getConfigListEntry(_("Exclude"), NoSave(ConfigSelection(choices = weekdays, default = x)))
+					for x in self.excludes[3]
+			])
+			self.lenExcludes = len(self.list)
+			self.list.extend([
+				getConfigListEntry(_("Include"), NoSave(ConfigSelection(choices = weekdays, default = x)))
+					for x in self.includes[3]
+			])
+			return
+		elif self.typeSelection.value == "title":
+			self.idx = 0
+		elif self.typeSelection.value == "short":
+			self.idx = 1
+		else: # self.typeSelection.value == "desc":
+			self.idx = 2
+
+		self.list.extend([
+			getConfigListEntry(_("Exclude"), NoSave(ExtendedConfigText(default = x, fixed_size = False)))
+				for x in self.excludes[self.idx]
+		])
+		self.lenExcludes = len(self.list)
+		self.list.extend([
+			getConfigListEntry(_("Include"), NoSave(ExtendedConfigText(default = x, fixed_size = False)))
+				for x in self.includes[self.idx]
+		])
+
+	def remove(self):
+		idx = self["config"].getCurrentIndex()
+		if idx and idx > 1:
+			if idx < self.lenExcludes:
+				self.lenExcludes -= 1
+
+			list = self["config"].getList()
+			list.remove(self["config"].getCurrent())
+			self["config"].setList(list)
+
+	def new(self):
+		self.session.openWithCallback(
+			self.typeSelected,
+			ChoiceBox,
+			_("Select type of Filter"),
+			[
+				(_("Exclude"), 0),
+				(_("Include"), 1),
+			]
+		)
+
+	def typeSelected(self, ret):
+		if ret is not None:
+			list = self["config"].getList()
+
+			if ret[1] == 0:
+				pos = self.lenExcludes
+				self.lenExcludes += 1
+				text = ret[0]
+			else:
+				pos = len(self.list)
+				text = ret[0]
+
+			if self.typeSelection.value == "day":
+				entry = getConfigListEntry(text, NoSave(ConfigSelection(choices = weekdays)))
+			else:
+				entry = getConfigListEntry(text, NoSave(ExtendedConfigText(fixed_size = False)))
+
+			list.insert(pos, entry)
+			self["config"].setList(list)
+
+	def cancel(self):
+		if self["config"].isChanged():
+			self.session.openWithCallback(
+				self.cancelConfirm,
+				MessageBox,
+				_("Really close without saving settings?")
+			)
+		else:
+			self.close(None)
+
+	def cancelConfirm(self, ret):
+		if ret:
+			self.close(None)
+
+	def save(self):
+		self.refresh()
+
+		self.close((
+			self.enabled.value,
+			self.excludes,
+			self.includes
+		))
+
+class AutoTimerServiceEditor(Screen, ConfigListScreen):
+	"""Edit allowed Services of a AutoTimer"""
+
+	skin = """<screen name="AutoTimerServiceEditor" title="Edit AutoTimer Services" position="center,center" size="565,245">
+		<widget name="config" position="5,5" size="555,200" scrollbarMode="showOnDemand" />
+		<ePixmap position="5,205" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
+		<ePixmap position="145,205" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
+		<ePixmap position="285,205" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
+		<ePixmap position="425,205" zPosition="4" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
+		<widget name="key_red" position="5,205" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
+		<widget name="key_green" position="145,205" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
+		<widget name="key_yellow" position="285,205" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
+		<widget name="key_blue" position="425,205" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
+	</screen>"""
+
+	def __init__(self, session, servicerestriction, servicelist, bouquetlist):
+		Screen.__init__(self, session)
+
+		# Summary
+		self.setup_title = _("AutoTimer Services")
+		self.onChangedEntry = []
+
+		self.services = (
+			servicelist[:],
+			bouquetlist[:]
+		)
+
+		self.enabled = NoSave(ConfigEnableDisable(default = servicerestriction))
+		self.typeSelection = NoSave(ConfigSelection(choices = [
+			("channels", _("Channels")),
+			("bouquets", _("Bouquets"))]
+		))
+		self.typeSelection.addNotifier(self.refresh, initial_call = False)
+
+		self.reloadList()
+
+		ConfigListScreen.__init__(self, self.list, session = session, on_change = self.changed)
+
+		# Initialize Buttons
+		self["key_red"] = Button(_("Cancel"))
+		self["key_green"] = Button(_("OK"))
+		self["key_yellow"] = Button(_("delete"))
+		self["key_blue"] = Button(_("New"))
+
+		# Define Actions
+		self["actions"] = ActionMap(["SetupActions", "ColorActions"],
+			{
+				"cancel": self.cancel,
+				"save": self.save,
+				"yellow": self.remove,
+				"blue": self.new
+			}
+		)
+
+		# Trigger change
+		self.changed()
+
+		self.onLayoutFinish.append(self.setCustomTitle)
+
+	def setCustomTitle(self):
+		self.setTitle(_("Edit AutoTimer Services"))
+
+	def saveCurrent(self):
+		del self.services[self.idx][:]
+
+		# Warning, accessing a ConfigListEntry directly might be considered evil!
+
+		myl = self["config"].getList()[:]
+		myl.pop(0) # Enabled
+		myl.pop(0) # Type
+		for item in myl:
+			self.services[self.idx].append(item[1].value)
+
+	def refresh(self, *args, **kwargs):
+		self.saveCurrent()
+
+		self.reloadList()
+		self["config"].setList(self.list)
+
+	def reloadList(self):
+		self.list = [
+			getConfigListEntry(_("Enable Service Restriction"), self.enabled),
+			getConfigListEntry(_("Editing"), self.typeSelection)
+		]
+
+		if self.typeSelection.value == "channels":
+			self.idx = 0
+		else: # self.typeSelection.value == "bouquets":
+			self.idx = 1
+
+		self.list.extend([
+			getConfigListEntry(_("Record on"), NoSave(ConfigSelection(choices = [(str(x), ServiceReference(str(x)).getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', ''))])))
+				for x in self.services[self.idx]
+		])
+
+	def changed(self):
+		for x in self.onChangedEntry:
+			try:
+				x()
+			except Exception:
+				pass
+
+	def getCurrentEntry(self):
+		return self["config"].getCurrent()[0]
+
+	def getCurrentValue(self):
+		return str(self["config"].getCurrent()[1].getText())
+
+	def createSummary(self):
+		return SetupSummary
+
+	def remove(self):
+		if self["config"].getCurrentIndex() != 0:
+			list = self["config"].getList()
+			list.remove(self["config"].getCurrent())
+			self["config"].setList(list)
+
+	def new(self):
+		if self.typeSelection.value == "channels":
+			self.session.openWithCallback(
+				self.finishedServiceSelection,
+				SimpleChannelSelection,
+				_("Select channel to record on")
+			)
+		else: # self.typeSelection.value == "bouquets":
+			self.session.openWithCallback(
+				self.finishedServiceSelection,
+				SimpleBouquetSelection,
+				_("Select bouquet to record on")
+			)
+
+	def finishedServiceSelection(self, *args):
+		if args:
+			list = self["config"].getList()
+			sname = args[0].toString()
+
+			if self.typeSelection.value == "channels" and not (args[0].flags & eServiceReference.isGroup):
+				# strip all after last : when adding a (non alternative) channel
+				pos = sname.rfind(':')
+				if pos != -1:
+					sname = sname[:pos+1]
+
+			list.append(getConfigListEntry(_("Record on"), NoSave(ConfigSelection(choices = [(sname, ServiceReference(args[0]).getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', ''))]))))
+			self["config"].setList(list)
+
+	def cancel(self):
+		if self["config"].isChanged():
+			self.session.openWithCallback(
+				self.cancelConfirm,
+				MessageBox,
+				_("Really close without saving settings?")
+			)
+		else:
+			self.close(None)
+
+	def cancelConfirm(self, ret):
+		if ret:
+			self.close(None)
+
+	def save(self):
+		self.refresh()
+
+		self.close((
+			self.enabled.value,
+			self.services
+		))
+
+def addAutotimerFromSearchString(session, match):
+	from AutoTimerComponent import AutoTimerComponent
+	from AutoTimerImporter import AutoTimerImporter
+	from plugin import autotimer
+
+	# Create instance if needed
+	if autotimer is None:
+		from AutoTimer import AutoTimer
+		autotimer = AutoTimer()
+		autotimer.readXml()
+
+	session.openWithCallback(
+		importerCallback,
+		AutoTimerImporter,
+		AutoTimerComponent(
+			autotimer.getUniqueId(),
+			match,
+			'',		# Match
+			True	# Enabled
+		),
+		match,		# Proposed Match
+		None,		# Proposed Begin
+		None,		# Proposed End
+		None,		# Proposed Disabled
+		None,		# Proposed ServiceReference
+		None,		# Proposed afterEvent
+		None,		# Proposed justplay
+		None,		# Proposed dirname, can we get anything useful here?
+		[]			# Proposed tags
+	)
+
+def addAutotimerFromEvent(session, evt = None, service = None):
+	from AutoTimerComponent import AutoTimerComponent
+	from AutoTimerImporter import AutoTimerImporter
+	from plugin import autotimer
+
+	# Create instance if needed
+	if autotimer is None:
+		from AutoTimer import AutoTimer
+		autotimer = AutoTimer()
+		autotimer.readXml()
+
+	match = evt and evt.getEventName() or ""
+	name = match or "New AutoTimer"
+	sref = None
+	if service is not None:
+		service = str(service)
+		myref = eServiceReference(service)
+		if not (myref.flags & eServiceReference.isGroup):
+			# strip all after last :
+			pos = service.rfind(':')
+			if pos != -1:
+				service = service[:pos+1]
+
+		sref = ServiceReference(myref)
+	if evt:
+		# timespan defaults to +- 1h
+		begin = evt.getBeginTime()-3600
+		end = begin + evt.getDuration()+7200
+	else:
+		begin = end = 0
+
+	# XXX: we might want to make sure that we actually collected any data because the importer does not do so :-)
+
+	session.openWithCallback(
+		importerCallback,
+		AutoTimerImporter,
+		AutoTimerComponent(
+			autotimer.getUniqueId(),
+			name,
+			'',		# Match
+			True	# Enabled
+		),
+		match,		# Proposed Match
+		begin,		# Proposed Begin
+		end,		# Proposed End
+		None,		# Proposed Disabled
+		sref,		# Proposed ServiceReference
+		None,		# Proposed afterEvent
+		None,		# Proposed justplay
+		None,		# Proposed dirname, can we get anything useful here?
+		[]			# Proposed tags
+	)
+
+def addAutotimerFromService(session, service = None):
+	from AutoTimerComponent import AutoTimerComponent
+	from AutoTimerImporter import AutoTimerImporter
+	from plugin import autotimer
+
+	# Create instance if needed
+	if autotimer is None:
+		from AutoTimer import AutoTimer
+		autotimer = AutoTimer()
+		autotimer.readXml()
+
+	serviceHandler = eServiceCenter.getInstance()
+	info = serviceHandler.info(service)
+
+	match = info and info.getName(service) or ""
+	name = match or "New AutoTimer"
+	sref = info and info.getInfoString(service, iServiceInformation.sServiceref)
+	if sref:
+		# strip all after last :
+		pos = sref.rfind(':')
+		if pos != -1:
+			sref = sref[:pos+1]
+
+		sref = ServiceReference(sref)
+	if info:
+		begin = info.getInfo(service, iServiceInformation.sTimeCreate)
+		end = begin + info.getLength(service)
+	else:
+		begin = end = 0
+
+	from os.path import dirname
+	path = dirname(service.getPath())
+	if not path == '/':
+		path += '/'
+
+	tags = info.getInfoString(service, iServiceInformation.sTags)
+	tags = tags and tags.split(' ') or []
+
+	# XXX: we might want to make sure that we actually collected any data because the importer does not do so :-)
+
+	session.openWithCallback(
+		importerCallback,
+		AutoTimerImporter,
+		AutoTimerComponent(
+			autotimer.getUniqueId(),
+			name,
+			'',		# Match
+			True	# Enabled
+		),
+		match,		# Proposed Match
+		begin,		# Proposed Begin
+		end,		# Proposed End
+		None,		# Proposed Disabled
+		sref,		# Proposed ServiceReference
+		None,		# Proposed afterEvent
+		None,		# Proposed justplay
+		path,		# Proposed dirname
+		tags		# Proposed tags
+	)
+
+def importerCallback(ret):
+	if ret:
+		ret, session = ret
+
+		session.openWithCallback(
+			editorCallback,
+			AutoTimerEditor,
+			ret
+		)
+	else:
+		# Remove instance if not running in background
+		if not config.plugins.autotimer.autopoll.value:
+			from plugin import autotimer
+			autotimer = None
+
+def editorCallback(ret):
+	if ret:
+		from plugin import autotimer
+
+		# Create instance if needed (should have been created by addAutotimerFrom* above though)
+		if autotimer is None:
+			from AutoTimer import AutoTimer
+			autotimer = AutoTimer()
+			autotimer.readXml()
+
+		autotimer.add(ret)
+
+		# Save modified xml
+		autotimer.writeXml()
+
+	# Remove instance if not running in background
+	if not config.plugins.autotimer.autopoll.value:
+		autotimer = None
+
Index: /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/AutoTimerImporter.py
===================================================================
--- /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/AutoTimerImporter.py	(revision 8760)
+++ /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/AutoTimerImporter.py	(revision 8760)
@@ -0,0 +1,306 @@
+# -*- coding: UTF-8 -*-
+# for localized messages
+from . import _
+
+# GUI (Screens)
+from Screens.Screen import Screen
+from Screens.MessageBox import MessageBox
+from Screens.InputBox import InputBox
+
+# GUI (Components)
+from Components.ActionMap import ActionMap
+from Components.Button import Button
+from Components.TimerList import TimerList
+from Components.SelectionList import SelectionList, SelectionEntryComponent
+
+# Timer
+from RecordTimer import AFTEREVENT
+
+# Needed to convert our timestamp back and forth
+from time import localtime
+
+from enigma import eServiceReference
+
+afterevent = {
+	AFTEREVENT.NONE: _("do nothing"),
+	AFTEREVENT.DEEPSTANDBY: _("go to deep standby"),
+	AFTEREVENT.STANDBY: _("go to standby"),
+	AFTEREVENT.AUTO: _("auto")
+}
+
+class AutoTimerImportSelector(Screen):
+	def __init__(self, session, autotimer):
+		Screen.__init__(self, session)
+		self.skinName = "TimerEditList"
+
+		self.autotimer = autotimer
+
+		self.list = []
+		self.fillTimerList()
+
+		self["timerlist"] = TimerList(self.list)
+
+		self["key_red"] = Button(_("Cancel"))
+		self["key_green"] = Button(_("OK"))
+		self["key_yellow"] = Button("")
+		self["key_blue"] = Button("")
+
+		self["actions"] = ActionMap(["OkCancelActions", "ColorActions"],
+		{
+			"ok": self.openImporter,
+			"cancel": self.cancel,
+			"green": self.openImporter,
+			"red": self.cancel
+		}, -1)
+		self.onLayoutFinish.append(self.setCustomTitle)
+
+	def setCustomTitle(self):
+		self.setTitle(_("Select a Timer to Import"))
+
+	def fillTimerList(self):
+		l = self.list
+		del l[:]
+
+		for timer in self.session.nav.RecordTimer.timer_list:
+			l.append((timer, False))
+
+		for timer in self.session.nav.RecordTimer.processed_timers:
+			l.append((timer, True))
+		l.sort(key = lambda x: x[0].begin)
+
+	def importerClosed(self, ret):
+		ret = ret and ret[0]
+		if ret is not None:
+			ret.name = ret.match
+		self.close(ret)
+
+	def openImporter(self):
+		cur=self["timerlist"].getCurrent()
+		if cur:
+			self.session.openWithCallback(
+				self.importerClosed,
+				AutoTimerImporter,
+				self.autotimer,
+				cur.name,
+				cur.begin,
+				cur.end,
+				cur.disabled,
+				cur.service_ref,
+				cur.afterEvent,
+				cur.justplay,
+				cur.dirname,
+				cur.tags
+			)
+
+	def cancel(self):
+		self.close(None)
+
+class AutoTimerImporter(Screen):
+	"""Import AutoTimer from Timer"""
+
+	skin = """<screen name="AutoTimerImporter" title="Import AutoTimer" position="center,center" size="565,280">
+		<widget name="list" position="5,5" size="555,225" scrollbarMode="showOnDemand" />
+		<ePixmap position="0,235" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
+		<ePixmap position="140,235" zPosition="4" size="140,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
+		<ePixmap position="280,235" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
+		<ePixmap position="420,235" zPosition="4" size="140,40" pixmap="skin_default/buttons/blue.png" transparent="1" alphatest="on" />
+		<widget name="key_red" position="0,235" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
+		<widget name="key_green" position="140,235" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
+		<widget name="key_yellow" position="280,235" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
+		<widget name="key_blue" position="420,235" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
+	</screen>"""
+
+	def __init__(self, session, autotimer, name, begin, end, disabled, sref, afterEvent, justplay, dirname, tags):
+		Screen.__init__(self, session)
+
+		# Keep AutoTimer
+		self.autotimer = autotimer
+
+		# Initialize Buttons
+		self["key_red"] = Button(_("Cancel"))
+		self["key_green"] = Button(_("OK"))
+		self["key_yellow"] = Button()
+ 		self["key_blue"] = Button()
+
+		list = []
+
+		if disabled is not None:
+			list.append(
+				SelectionEntryComponent(
+					': '.join((_("Enabled"), {True: _("disable"), False: _("enable")}[bool(disabled)])),
+					not disabled,
+					0,
+					True
+			))
+
+		if name != "":
+			list.extend([
+				SelectionEntryComponent(
+					_("Match title: %s") % (name),
+					name,
+					1,
+					True
+				),
+				SelectionEntryComponent(
+					_("Exact match"),
+					True,
+					8,
+					True
+				)
+			])
+
+		if begin and end:
+			begin = localtime(begin)
+			end = localtime(end)
+			list.append(
+				SelectionEntryComponent(
+					_("Match Timespan: %02d:%02d - %02d:%02d") % (begin[3], begin[4], end[3], end[4]),
+					((begin[3], begin[4]), (end[3], end[4])),
+					2,
+					True
+			))
+
+		if sref:
+			list.append(
+				SelectionEntryComponent(
+					_("Only on Service: %s") % (sref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')),
+					str(sref),
+					3,
+					True
+			))
+
+		if afterEvent is not None:
+			list.append(
+				SelectionEntryComponent(
+					': '.join((_("After event"), afterevent[afterEvent])),
+					afterEvent,
+					4,
+					True
+			))
+
+		if justplay is not None:
+			list.append(
+				SelectionEntryComponent(
+					': '.join((_("Timer Type"), {0: _("record"), 1: _("zap")}[int(justplay)])),
+					int(justplay),
+					5,
+					True
+			))
+
+		if dirname is not None:
+			list.append(
+				SelectionEntryComponent(
+					': '.join((_("Location"), dirname or "/hdd/movie/")),
+					dirname,
+					6,
+					True
+			))
+
+		if tags:
+			list.append(
+				SelectionEntryComponent(
+					': '.join((_("Tags"), ', '.join(tags))),
+					tags,
+					7,
+					True
+			))
+
+		self["list"] = SelectionList(list)
+
+		# Define Actions
+		self["actions"] = ActionMap(["OkCancelActions", "ColorActions"],
+		{
+			"ok": self["list"].toggleSelection,
+			"cancel": self.cancel,
+			"red": self.cancel,
+			"green": self.accept
+		}, -1)
+
+		self.onLayoutFinish.append(self.setCustomTitle)
+
+	def setCustomTitle(self):
+		self.setTitle(_("Import AutoTimer"))
+
+	def cancel(self):
+		self.session.openWithCallback(
+			self.cancelConfirm,
+			MessageBox,
+			_("Really close without saving settings?")
+		)
+
+	def cancelConfirm(self, ret):
+		if ret:
+			self.close(None)
+
+	def gotCustomMatch(self, ret):
+		if ret:
+			self.autotimer.match = ret
+			# Check if we have a trailing whitespace
+			if ret[-1:] == " ":
+				self.session.openWithCallback(
+					self.trailingWhitespaceRemoval,
+					MessageBox,
+					_('You entered "%s" as Text to match.\nDo you want to remove trailing whitespaces?') % (ret)
+				)
+			# Just confirm else
+			else:
+				self.close((
+				self.autotimer,
+				self.session
+			))
+
+	def trailingWhitespaceRemoval(self, ret):
+		if ret is not None:
+			if ret:
+				self.autotimer.match = self.autotimer.match.rstrip()
+			self.close((
+				self.autotimer,
+				self.session
+			))
+
+	def accept(self):
+		list = self["list"].getSelectionsList()
+		autotimer = self.autotimer
+
+		for item in list:
+			if item[2] == 0: # Enable
+				autotimer.enabled = item[1]
+			elif item[2] == 1: # Match
+				autotimer.match = item[1]
+			elif item[2] == 2: # Timespan
+				autotimer.timespan = item[1]
+			elif item[2] == 3: # Service
+				value = item[1]
+
+				myref = eServiceReference(value)
+				if not (myref.flags & eServiceReference.isGroup):
+					# strip all after last :
+					pos = value.rfind(':')
+					if pos != -1:
+						value = value[:pos+1]
+
+				autotimer.services = [value]
+			elif item[2] == 4: # AfterEvent
+				autotimer.afterevent = [(item[1], None)]
+			elif item[2] == 5: # Justplay
+				autotimer.justplay = item[1]
+			elif item[2] == 6: # Location
+				autotimer.destination = item[1]
+			elif item[2] == 7: # Tags
+				autotimer.tags = item[1]
+			elif item[2] == 8: # Exact match
+				autotimer.searchType = "exact"
+				autotimer.searchCase = "sensitive"
+
+		if autotimer.match == "":
+			self.session.openWithCallback(
+					self.gotCustomMatch,
+					InputBox,
+					title = _("Please provide a Text to match")
+			)
+		else:
+			self.close((
+				autotimer,
+				self.session
+			))
+
Index: /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/AutoTimerList.py
===================================================================
--- /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/AutoTimerList.py	(revision 8760)
+++ /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/AutoTimerList.py	(revision 8760)
@@ -0,0 +1,66 @@
+# -*- coding: UTF-8 -*-
+# for localized messages
+from . import _
+
+# GUI (Components)
+from Components.MenuList import MenuList
+from enigma import eListboxPythonMultiContent, gFont, RT_HALIGN_LEFT
+
+from skin import parseColor, parseFont
+
+class AutoTimerList(MenuList):
+	"""Defines a simple Component to show Timer name"""
+
+	def __init__(self, entries):
+		MenuList.__init__(self, entries, False, content = eListboxPythonMultiContent)
+
+		self.l.setFont(0, gFont("Regular", 22))
+		self.l.setBuildFunc(self.buildListboxEntry)
+		self.l.setItemHeight(25)
+		self.colorDisabled = 12368828
+
+	def applySkin(self, desktop, parent):
+		attribs = [ ] 
+		if self.skinAttributes is not None:
+			for (attrib, value) in self.skinAttributes:
+				if attrib == "font":
+					self.l.setFont(0, parseFont(value, ((1,1),(1,1))))
+				elif attrib == "itemHeight":
+					self.l.setItemHeight(int(value))
+				elif attrib == "colorDisabled":
+					self.colorDisabled = int(parseColor(value))
+				else:
+					attribs.append((attrib, value))
+		self.skinAttributes = attribs
+		return MenuList.applySkin(self, desktop, parent)
+
+	#
+	#  | <Name of AutoTimer> |
+	#
+	def buildListboxEntry(self, timer):
+		size = self.l.getItemSize()
+
+		color = None
+		if not timer.enabled:
+			color = self.colorDisabled
+
+		return [
+			None,
+			(eListboxPythonMultiContent.TYPE_TEXT, 5, 0, size.width() - 5, size.height(), 0, RT_HALIGN_LEFT, timer.name, color, color)
+		]
+
+	def getCurrent(self):
+		cur = self.l.getCurrentSelection()
+		return cur and cur[0]
+
+	def moveToEntry(self, entry):
+		if entry is None:
+			return
+
+		idx = 0
+		for x in self.list:
+			if x[0] == entry:
+				self.instance.moveSelectionTo(idx)
+				break
+			idx += 1
+
Index: /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/AutoTimerOverview.py
===================================================================
--- /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/AutoTimerOverview.py	(revision 8760)
+++ /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/AutoTimerOverview.py	(revision 8760)
@@ -0,0 +1,243 @@
+# for localized messages
+from . import _
+
+# GUI (Screens)
+from Screens.Screen import Screen
+from Screens.HelpMenu import HelpableScreen
+from Screens.MessageBox import MessageBox
+from Screens.ChoiceBox import ChoiceBox
+from AutoTimerEditor import AutoTimerEditor, AutoTimerChannelSelection
+from AutoTimerSettings import AutoTimerSettings
+from AutoTimerPreview import AutoTimerPreview
+from AutoTimerImporter import AutoTimerImportSelector
+from AutoTimerWizard import AutoTimerWizard
+
+# GUI (Components)
+from AutoTimerList import AutoTimerList
+from Components.ActionMap import HelpableActionMap
+from Components.Button import Button
+from Components.config import config
+
+# Plugin
+from AutoTimerComponent import AutoTimerComponent
+
+class AutoTimerOverview(Screen, HelpableScreen):
+	"""Overview of AutoTimers"""
+
+	skin = """<screen name="AutoTimerOverview" position="center,center" size="460,265" title="AutoTimer Overview">
+			<widget name="entries" position="5,5" size="450,200" scrollbarMode="showOnDemand" />
+			<ePixmap position="0,220" zPosition="1" size="140,40" pixmap="skin_default/buttons/green.png" alphatest="on" />
+			<ePixmap position="140,220" zPosition="1" size="140,40" pixmap="skin_default/buttons/yellow.png" alphatest="on" />
+			<ePixmap position="280,220" zPosition="1" size="140,40" pixmap="skin_default/buttons/blue.png" alphatest="on" />
+			<ePixmap position="422,230" zPosition="1" size="35,25" pixmap="skin_default/buttons/key_menu.png" alphatest="on" />
+			<widget name="key_green" position="0,220" zPosition="2" size="140,40" halign="center" valign="center" font="Regular;21" transparent="1" shadowColor="black" shadowOffset="-1,-1" />
+			<widget name="key_yellow" position="140,220" zPosition="2" size="140,40" halign="center" valign="center" font="Regular;21" transparent="1" shadowColor="black" shadowOffset="-1,-1" />
+			<widget name="key_blue" position="280,220" zPosition="2" size="140,40" halign="center" valign="center" font="Regular;21" transparent="1" shadowColor="black" shadowOffset="-1,-1" />
+		</screen>"""
+
+	def __init__(self, session, autotimer):
+		Screen.__init__(self, session)
+		HelpableScreen.__init__(self)
+
+		# Save autotimer
+		self.autotimer = autotimer
+
+		self.changed = False
+
+		# Button Labels
+		self["key_green"] = Button(_("Save"))
+		self["key_yellow"] = Button(_("Delete"))
+		self["key_blue"] = Button(_("Add"))
+
+		# Create List of Timers
+		self["entries"] = AutoTimerList(autotimer.getSortedTupleTimerList())
+
+		# Define Actions
+		self["OkCancelActions"] = HelpableActionMap(self, "OkCancelActions",
+			{
+				"ok": (self.ok, _("Edit selected AutoTimer")),
+				"cancel": (self.cancel, _("Close and forget changes")),
+			}
+		)
+
+		self["MenuActions"] = HelpableActionMap(self, "MenuActions",
+			{
+				"menu": (self.menu, _("Open Context Menu"))
+			}
+		)
+
+		self["ColorActions"] = HelpableActionMap(self, "ColorActions",
+			{
+				"green": (self.save, _("Close and save changes")),
+				"yellow": (self.remove, _("Remove selected AutoTimer")),
+				"blue": (self.add, _("Add new AutoTimer")),
+			}
+		)
+
+		self.onLayoutFinish.append(self.setCustomTitle)
+
+	def setCustomTitle(self):
+		self.setTitle(_("AutoTimer Overview"))
+
+	def add(self):
+		newTimer = self.autotimer.defaultTimer.clone()
+		newTimer.id = self.autotimer.getUniqueId()
+
+		if config.plugins.autotimer.editor.value == "wizard":
+			self.session.openWithCallback(
+				self.addCallback,
+				AutoTimerWizard,
+				newTimer
+			)
+		else:
+			self.session.openWithCallback(
+				self.addCallback,
+				AutoTimerEditor,
+				newTimer
+			)
+
+	def editCallback(self, ret):
+		if ret:
+			self.changed = True
+			self.refresh()
+
+	def addCallback(self, ret):
+		if ret:
+			self.changed = True
+			self.autotimer.add(ret)
+			self.refresh()
+
+	def importCallback(self, ret):
+		if ret:
+			self.session.openWithCallback(
+				self.addCallback,
+				AutoTimerEditor,
+				ret
+			)
+
+	def refresh(self, res = None):
+		# Re-assign List
+		cur = self["entries"].getCurrent()
+		self["entries"].setList(self.autotimer.getSortedTupleTimerList())
+		self["entries"].moveToEntry(cur)
+
+	def ok(self):
+		# Edit selected Timer
+		current = self["entries"].getCurrent()
+		if current is not None:
+			self.session.openWithCallback(
+				self.editCallback,
+				AutoTimerEditor,
+				current
+			)
+
+	def remove(self):
+		# Remove selected Timer
+		cur = self["entries"].getCurrent()
+		if cur is not None:
+			self.session.openWithCallback(
+				self.removeCallback,
+				MessageBox,
+				_("Do you really want to delete %s?") % (cur.name),
+			)
+
+	def removeCallback(self, ret):
+		cur = self["entries"].getCurrent()
+		if ret and cur:
+			self.autotimer.remove(cur.id)
+			self.refresh()
+
+	def cancel(self):
+		if self.changed:
+			self.session.openWithCallback(
+				self.cancelConfirm,
+				MessageBox,
+				_("Really close without saving settings?")
+			)
+		else:
+			self.close(None)
+
+	def cancelConfirm(self, ret):
+		if ret:
+			# Invalidate config mtime to force re-read on next run
+			self.autotimer.configMtime = -1
+
+			# Close and indicated that we canceled by returning None
+			self.close(None)
+
+	def menu(self):
+		list = [
+			(_("Preview"), "preview"),
+			(_("Import existing Timer"), "import"),
+			(_("Import from EPG"), "import_epg"),
+			(_("Setup"), "setup"),
+			(_("Edit new timer defaults"), "defaults"),
+		]
+
+		if config.plugins.autotimer.editor.value == "wizard":
+			list.append((_("Create a new timer using the classic editor"), "newplain"))
+		else:
+			list.append((_("Create a new timer using the wizard"), "newwizard"))
+
+		self.session.openWithCallback(
+			self.menuCallback,
+			ChoiceBox,
+			list = list,
+		)
+
+	def menuCallback(self, ret):
+		ret = ret and ret[1]
+		if ret:
+			if ret == "preview":
+				total, new, modified, timers = self.autotimer.parseEPG(simulateOnly = True)
+				self.session.open(
+					AutoTimerPreview,
+					timers
+				)
+			elif ret == "import":
+				newTimer = self.autotimer.defaultTimer.clone()
+				newTimer.id = self.autotimer.getUniqueId()
+
+				self.session.openWithCallback(
+					self.importCallback,
+					AutoTimerImportSelector,
+					newTimer
+				)
+			elif ret == "import_epg":
+				self.session.open(
+					AutoTimerChannelSelection,
+					self.autotimer
+				)
+			elif ret == "setup":
+				self.session.open(
+					AutoTimerSettings
+				)
+			elif ret == "defaults":
+				self.session.open(
+					AutoTimerEditor,
+					self.autotimer.defaultTimer,
+					editingDefaults = True
+				)
+			elif ret == "newwizard":
+				newTimer = self.autotimer.defaultTimer.clone()
+				newTimer.id = self.autotimer.getUniqueId()
+
+				self.session.openWithCallback(
+					self.addCallback, # XXX: we could also use importCallback... dunno what seems more natural
+					AutoTimerWizard,
+					newTimer
+				)
+			elif ret == "newplain":
+				newTimer = self.autotimer.defaultTimer.clone()
+				newTimer.id = self.autotimer.getUniqueId()
+
+				self.session.openWithCallback(
+					self.addCallback,
+					AutoTimerEditor,
+					newTimer
+				)
+
+	def save(self):
+		# Just close here, saving will be done by cb
+		self.close(self.session)
+
Index: /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/AutoTimerPreview.py
===================================================================
--- /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/AutoTimerPreview.py	(revision 8760)
+++ /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/AutoTimerPreview.py	(revision 8760)
@@ -0,0 +1,106 @@
+# -*- coding: UTF-8 -*-
+# for localized messages
+from . import _
+
+# GUI (Screens)
+from Screens.Screen import Screen
+
+# GUI (Components)
+from Components.ActionMap import ActionMap
+from Components.Button import Button
+from Components.Sources.List import List
+
+from ServiceReference import ServiceReference
+from Tools.FuzzyDate import FuzzyTime
+
+class AutoTimerPreview(Screen):
+	"""Preview Timers which would be set"""
+
+	skin = """<screen name="AutoTimerPreview" title="Preview AutoTimer" position="center,center" size="565,265">
+		<widget source="timerlist" render="Listbox" position="5,5" size="555,210" scrollbarMode="showOnDemand">
+			<convert type="TemplatedMultiContent">
+				{"template": [
+						MultiContentEntryText(pos=(2,2), size=(550,24), text = 3, font = 0, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER),
+						MultiContentEntryText(pos=(2,26), size=(555,30), text = 0, font = 1, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER),
+						MultiContentEntryText(pos=(2,50), size=(400,20), text = 4, font = 1, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER),
+						MultiContentEntryText(pos=(290,50), size=(233,20), text = 2, font = 1, flags = RT_HALIGN_RIGHT|RT_VALIGN_CENTER),
+					],
+				 "fonts": [gFont("Regular", 20),gFont("Regular", 18)],
+				 "itemHeight": 72
+				}
+			</convert>
+		</widget>
+		<ePixmap position="0,220" zPosition="4" size="140,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
+		<ePixmap position="280,220" zPosition="4" size="140,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
+		<widget name="key_red" position="0,220" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
+		<widget name="key_yellow" position="280,220" zPosition="5" size="140,40" valign="center" halign="center" font="Regular;21" transparent="1" foregroundColor="white" shadowColor="black" shadowOffset="-1,-1" />
+	</screen>"""
+
+	def __init__(self, session, timers):
+		Screen.__init__(self, session)
+
+		# Sort timers by begin
+		timers.sort(key = lambda x: x[1])
+		self.sort_type = 0
+
+		# name, begin, end, serviceref, timername -> name, begin, timername, sname, timestr
+		self.timers = [
+			(x[0], x[1], x[4],
+			ServiceReference(x[3]).getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '').encode('utf-8', 'ignore'),
+			(("%s, %s ... %s (%d " + _("mins") + ")") % (FuzzyTime(x[1]) + FuzzyTime(x[2])[1:] + ((x[2] - x[1]) / 60,))))
+			for x in timers
+		]
+
+		self["timerlist"] = List(self.timers)
+
+		# Initialize Buttons
+		self["key_red"] = Button(_("Cancel"))
+		self["key_yellow"] = Button()
+
+		self.setSortDescription()
+
+		# Define Actions
+		self["actions"] = ActionMap(["SetupActions", "ColorActions"],
+			{
+				"cancel": self.cancel,
+				"save": self.save,
+				"yellow": self.sort
+			}
+		)
+
+		self.onLayoutFinish.append(self.setCustomTitle)
+
+	def setCustomTitle(self):
+		self.setTitle(_("Preview AutoTimer"))
+
+	def setSortDescription(self):
+		if self.sort_type == 1:
+			self["key_yellow"].setText(_("Sort Time"))
+		else:
+			self["key_yellow"].setText(_("Sort AutoTimer"))
+
+	def sort(self):
+		timers = self.timers
+		if timers:
+			current = self["timerlist"].current
+			idx = 0
+			for timer in timers:
+				if timer == current:
+					break
+				idx += 1
+			if self.sort_type == 1:
+				timers.sort(key=lambda x: x[1])
+				self.sort_type = 0
+			else:
+				timers.sort(key = lambda x: x[4].lower())
+				self.sort_type = 1
+			self["timerlist"].updateList(timers)
+			self["timerlist"].index = idx
+			self.setSortDescription()
+
+	def cancel(self):
+		self.close(None)
+
+	def save(self):
+		self.close(True)
+
Index: /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/AutoTimerResource.py
===================================================================
--- /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/AutoTimerResource.py	(revision 8760)
+++ /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/AutoTimerResource.py	(revision 8760)
@@ -0,0 +1,27 @@
+from twisted.web2 import resource, responsecode, http
+from AutoTimer import AutoTimer
+from . import _
+
+# pretty basic resource which is just present to have a way to start a
+# forced run through the webif
+class AutoTimerResource(resource.Resource):
+	def __init__(self):
+		resource.Resource.__init__(self)
+
+	def render(self, req):
+		from plugin import autotimer
+		remove = False
+		if autotimer is None:
+			autotimer = AutoTimer()
+			remove = True
+
+		if req.args.has_key("parse"):
+			ret = autotimer.parseEPG()
+			output = _("Found a total of %d matching Events.\n%d Timer were added and %d modified.") % (ret[0], ret[1], ret[2])
+		else:
+			output = "unknown command"
+
+		if remove:
+			autotimer = None
+		return http.Response(responsecode.OK ,stream = output)
+
Index: /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/AutoTimerSettings.py
===================================================================
--- /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/AutoTimerSettings.py	(revision 8760)
+++ /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/AutoTimerSettings.py	(revision 8760)
@@ -0,0 +1,79 @@
+# for localized messages
+from . import _
+
+# GUI (Screens)
+from Screens.Screen import Screen
+from Components.ConfigList import ConfigListScreen
+
+# GUI (Summary)
+from Screens.Setup import SetupSummary
+
+# GUI (Components)
+from Components.ActionMap import ActionMap
+from Components.Label import Label
+from Components.Sources.StaticText import StaticText
+from Components.Pixmap import Pixmap
+
+# Configuration
+from Components.config import config, getConfigListEntry
+
+class AutoTimerSettings(Screen, ConfigListScreen):
+	def __init__(self, session):
+		Screen.__init__(self, session)
+		self.skinName = "Setup"
+
+		# Summary
+		self.setup_title = _("AutoTimer Settings")
+		self.onChangedEntry = []
+
+		ConfigListScreen.__init__(
+			self,
+			[
+				getConfigListEntry(_("Poll automatically"), config.plugins.autotimer.autopoll),
+				getConfigListEntry(_("Poll Interval (in h)"), config.plugins.autotimer.interval),
+				getConfigListEntry(_("Show in Extensionmenu"), config.plugins.autotimer.show_in_extensionsmenu),
+				getConfigListEntry(_("Modify existing Timers"), config.plugins.autotimer.refresh),
+				getConfigListEntry(_("Guess existing Timer based on Begin/End"), config.plugins.autotimer.try_guessing),
+				getConfigListEntry(_("Add timer as disabled on conflict"), config.plugins.autotimer.disabled_on_conflict),
+				getConfigListEntry(_("Editor for new AutoTimers"), config.plugins.autotimer.editor),
+			],
+			session = session,
+			on_change = self.changed
+		)
+
+		# Initialize widgets
+		self["key_green"] = StaticText(_("OK"))
+		self["key_red"] = StaticText(_("Cancel"))
+
+		# Define Actions
+		self["actions"] = ActionMap(["SetupActions"],
+			{
+				"cancel": self.keyCancel,
+				"save": self.keySave,
+			}
+		)
+
+		# Trigger change
+		self.changed()
+
+		self.onLayoutFinish.append(self.setCustomTitle)
+
+	def setCustomTitle(self):
+		self.setTitle(_("Configure AutoTimer behavior"))
+
+	def changed(self):
+		for x in self.onChangedEntry:
+			try:
+				x()
+			except Exception:
+				pass
+
+	def getCurrentEntry(self):
+		return self["config"].getCurrent()[0]
+
+	def getCurrentValue(self):
+		return str(self["config"].getCurrent()[1].getText())
+
+	def createSummary(self):
+		return SetupSummary
+
Index: /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/AutoTimerWizard.py
===================================================================
--- /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/AutoTimerWizard.py	(revision 8760)
+++ /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/AutoTimerWizard.py	(revision 8760)
@@ -0,0 +1,175 @@
+# l10n
+from . import _
+
+# GUI (Screens)
+from Screens.WizardLanguage import WizardLanguage
+from Screens.Rc import Rc
+from AutoTimerEditor import AutoTimerEditorBase, AutoTimerServiceEditor, \
+		AutoTimerFilterEditor
+
+# GUI (Components)
+from Components.ActionMap import ActionMap
+from Components.Pixmap import Pixmap
+
+# Configuration
+from Components.config import getConfigListEntry, KEY_0, KEY_DELETE, \
+		KEY_BACKSPACE
+
+# Wizard XML Path
+from Tools import Directories
+
+class AutoTimerWizard(WizardLanguage, AutoTimerEditorBase, Rc):
+	STEP_ID_BASIC = 2
+	STEP_ID_TIMESPAN = 5
+	STEP_ID_SERVICES = 7
+	STEP_ID_FILTER = 8
+
+	def __init__(self, session, newTimer):
+		self.xmlfile = Directories.resolveFilename(Directories.SCOPE_PLUGINS, "Extensions/AutoTimer/autotimerwizard.xml")
+
+		WizardLanguage.__init__(self, session, showSteps = True, showStepSlider = True)
+		AutoTimerEditorBase.__init__(self, newTimer)
+		Rc.__init__(self)
+
+		self.skinName = "StartWizard"
+		self["wizard"] = Pixmap()
+
+		self.doCancel = False
+		self.emptyMatch = False
+		self.tailingWhitespacesMatch = False
+
+		# We might need to change shown items, so add some notifiers
+		self.timespan.addNotifier(self.regenTimespanList, initial_call = False)
+		self.generateTimespanList()
+
+		self.servicesDlg = self.session.instantiateDialog(
+				AutoTimerServiceEditor,
+				self.serviceRestriction, self.services, self.bouquets
+		)
+
+		self.filterDlg = self.session.instantiateDialog(
+				AutoTimerFilterEditor,
+				self.filterSet, self.excludes, self.includes
+		)
+
+		self["TextEntryActions"] = ActionMap(["TextEntryActions"],
+			{
+				"deleteForward": self.deleteForward,
+				"deleteBackward": self.deleteBackward
+			}, -2
+		)
+
+	def getTranslation(self, text):
+		return _(text)
+
+	def regenTimespanList(self, *args, **kwargs):
+		self.generateTimespanList()
+		if self.currStep == AutoTimerWizard.STEP_ID_TIMESPAN:
+			self["config"].setList(self.timespanList)
+
+	def generateTimespanList(self):
+		self.timespanList = [
+			getConfigListEntry(_("Only match during Timespan"), self.timespan)
+		]
+
+		# Only allow editing timespan when it's enabled
+		if self.timespan.value:
+			self.timespanList.extend([
+				getConfigListEntry(_("Begin of Timespan"), self.timespanbegin),
+				getConfigListEntry(_("End of Timespan"), self.timespanend)
+			])
+
+	def getConfigList(self):
+		if self.currStep == AutoTimerWizard.STEP_ID_BASIC: # Basic
+			return [
+				getConfigListEntry(_("Enabled"), self.enabled),
+				getConfigListEntry(_("Description"), self.name),
+				getConfigListEntry(_("Match Title"), self.match),
+				getConfigListEntry(_("Timer Type"), self.justplay),
+			]
+		elif self.currStep == AutoTimerWizard.STEP_ID_TIMESPAN: # Timespan
+			return self.timespanList
+		elif self.currStep == AutoTimerWizard.STEP_ID_SERVICES: # Services
+			return self.servicesDlg["config"].getList()
+		elif self.currStep == AutoTimerWizard.STEP_ID_FILTER: # Filters
+			return self.filterDlg["config"].getList()
+		return []
+
+	def selectionMade(self):
+		timer = self.timer
+		if self.currStep == AutoTimerWizard.STEP_ID_BASIC: # Basic
+			timer.enabled = self.enabled.value
+			timer.name = self.name.value.strip() or self.match.value
+			timer.match = self.match.value
+			timer.justplay = self.justplay.value == "zap"
+			self.emptyMatch = not timer.match.strip()
+			self.trailingWhitespacesMatch = (timer.match[-1:] == " ")
+		elif self.currStep == AutoTimerWizard.STEP_ID_TIMESPAN: # Timespan
+			if self.timespan.value:
+				start = self.timespanbegin.value
+				end = self.timespanend.value
+				timer.timespan = (start, end)
+			else:
+				timer.timespan = None
+		elif self.currStep == AutoTimerWizard.STEP_ID_SERVICES: # Services
+			self.servicesDlg.refresh()
+
+			if self.servicesDlg.enabled.value:
+				timer.services = self.servicesDlg.services[0]
+				timer.bouquets = self.servicesDlg.services[1]
+			else:
+				timer.services = []
+				timer.bouquets = []
+		elif self.currStep == AutoTimerWizard.STEP_ID_FILTER: # Filters
+			self.filterDlg.refresh()
+
+			if self.filterDlg.enabled.value:
+				timer.includes = self.filterDlg.includes
+				timer.excludes = self.filterDlg.excludes
+			else:
+				timer.includes = []
+				timer.excludes = []
+
+	def keyNumberGlobal(self, number):
+		if self.currStep == AutoTimerWizard.STEP_ID_BASIC or self.currStep == AutoTimerWizard.STEP_ID_TIMESPAN:
+			self["config"].handleKey(KEY_0 + number)
+		else:
+			WizardLanguage.keyNumberGlobal(self, number)
+
+	def blue(self):
+		if self.currStep == AutoTimerWizard.STEP_ID_SERVICES:
+			self.servicesDlg.new()
+		elif self.currStep == AutoTimerWizard.STEP_ID_FILTER:
+			self.filterDlg.new()
+
+	def yellow(self):
+		if self.currStep == AutoTimerWizard.STEP_ID_SERVICES:
+			self.servicesDlg.remove()
+		elif self.currStep == AutoTimerWizard.STEP_ID_FILTER:
+			self.filterDlg.remove()
+
+	def maybeRemoveWhitespaces(self):
+		# XXX: Hack alert
+		if self["list"].current[1] == "removeTrailingWhitespaces":
+			print "Next step would be to remove trailing whitespaces, removing them and redirecting to 'conf2'"
+			self.timer.match = self.timer.match.rstrip()
+			self.match.value = self.match.value.rstrip()
+			self.currStep = self.getStepWithID("conf2")
+		self.trailingWhitespacesMatch = False
+
+	def deleteForward(self):
+		self["config"].handleKey(KEY_DELETE)
+
+	def deleteBackward(self):
+		self["config"].handleKey(KEY_BACKSPACE)
+
+	def cancel(self):
+		self.doCancel = True
+		self.currStep = len(self.wizard)
+
+	def close(self, *args, **kwargs):
+		if self.doCancel:
+			WizardLanguage.close(self, None)
+		else:
+			WizardLanguage.close(self, self.timer)
+
Index: /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/LICENSE
===================================================================
--- /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/LICENSE	(revision 8760)
+++ /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/LICENSE	(revision 8760)
@@ -0,0 +1,12 @@
+All Files of this Software are licensed under the Creative Commons 
+Attribution-NonCommercial-ShareAlike 3.0 Unported 
+License if not stated otherwise in a Files Head. To view a copy of this license, visit
+http://creativecommons.org/licenses/by-nc-sa/3.0/ or send a letter to Creative
+Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.
+
+Alternatively, this plugin may be distributed and executed on hardware which
+is licensed by Dream Multimedia GmbH.
+
+This plugin is NOT free software. It is open source, you are allowed to
+modify it (if you keep the license), but it may not be commercially 
+distributed other than under the conditions noted above.
Index: /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/__init__.py
===================================================================
--- /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/__init__.py	(revision 8760)
+++ /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/__init__.py	(revision 8760)
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+from Components.Language import language
+from Tools.Directories import resolveFilename, SCOPE_PLUGINS, SCOPE_LANGUAGE
+from os import environ as os_environ
+import gettext
+
+def localeInit():
+	lang = language.getLanguage()[:2] # getLanguage returns e.g. "fi_FI" for "language_country"
+	os_environ["LANGUAGE"] = lang # Enigma doesn't set this (or LC_ALL, LC_MESSAGES, LANG). gettext needs it!
+	gettext.bindtextdomain("AutoTimer", resolveFilename(SCOPE_PLUGINS, "Extensions/AutoTimer/locale"))
+
+def _(txt):
+	t = gettext.dgettext("AutoTimer", txt)
+	if t == txt:
+		print "[AutoTimer] fallback to default translation for", txt
+		t = gettext.gettext(txt)
+	return t
+
+localeInit()
+language.addCallback(localeInit)
+
Index: /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/autotimerwizard.xml
===================================================================
--- /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/autotimerwizard.xml	(revision 8760)
+++ /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/autotimerwizard.xml	(revision 8760)
@@ -0,0 +1,151 @@
+<wizard>
+		<step id="welcome">
+			<text value="Welcome.\n\nThis Wizard will help you to create a new AutoTimer by providing descriptions for common settings." />
+			<list>
+				<listentry caption="Create a new AutoTimer." step="conf1" />
+				<listentry caption="Abort this Wizard." step="abort" />
+			</list>
+			<code>
+self.clearSelectedKeys()
+self.selectKey("OK")
+			</code>
+		</step>
+
+		<!-- Name / Match / Enabled / Justplay -->
+		<step id="conf1" nextstep="conf2">
+			<text value="You can set the basic properties of an AutoTimer here.\nWhile 'Name' is just a human-readable name displayed in the Overview, 'Match in title' is what is looked for in the EPG." />
+			<config type="dynamic" source="getConfigList" evaluation="selectionMade" />
+			<code>
+self.clearSelectedKeys()
+self.selectKey("OK")
+			</code>
+			<!-- A sanity check is done in selectionMade combined with some helper versions of conf2 -->
+		</step>
+
+		<!-- Sanity check basic properties
+			They ruin the wizards history for now but better than nothing :D -->
+		<step id="conf2" nextstep="conf1">
+			<condition>
+self.condition = self.emptyMatch
+			</condition>
+			<text value="You did not provide a valid 'Match in title' Attribute for your new AutoTimer.\nAs this is a mandatory Attribute you cannot continue without doing so." />
+			<code>
+self.clearSelectedKeys()
+self.selectKey("OK")
+			</code>
+		</step>
+
+		<step id="conf2">
+			<condition>
+self.condition = self.trailingWhitespacesMatch
+			</condition>
+			<text value="Your 'Match in title' Attribute ends with a Whitespace.\nPlease confirm if this was intentional, if not they will be removed." />
+			<list>
+				<listentry caption="Yes, keep them." step="conf2" />
+				<listentry caption="No, remove them." step="removeTrailingWhitespaces" />
+			</list>
+			<code>
+self.clearSelectedKeys()
+self.selectKey("OK")
+			</code>
+			<code pos="after">
+self.maybeRemoveWhitespaces()
+			</code>
+		</step>
+
+		<!-- Timespan -->
+		<step id="conf2" nextstep="conf4">
+			<condition>
+self.condition = not self.emptyMatch and not self.trailingWhitespacesMatch
+			</condition>
+			<text value="The Timespan of an AutoTimer is the first 'advanced' attribute. If a timespan is specified an event will only match this AutoTimer if it lies inside of this timespan." />
+			<config type="dynamic" source="getConfigList" evaluation="selectionMade" />
+			<code>
+self.clearSelectedKeys()
+self.selectKey("OK")
+self.selectKey("LEFT")
+self.selectKey("RIGHT")
+			</code>
+		</step>
+
+		<!-- Offset / AfterEvent / Match --> <!-- CURRENTLY INACTIVE -->
+		<step id="conf3" nextstep="conf4">
+			<text value="" />
+			<config type="dynamic" source="getConfigList" evaluation="selectionMade" />
+			<code>
+self.clearSelectedKeys()
+self.selectKey("OK")
+			</code>
+		</step>
+
+		<!-- Services/Bouquets -->
+		<step id="conf4" nextstep="conf5">
+			<text value="It's possible to restrict an AutoTimer to certain Services or Bouquets or to deny specific ones.\nAn Event will only match this AutoTimer if it's on a specific and not denied Service (inside a Bouquet).\nPress BLUE to add a new restriction and YELLOW to remove the selected one." />
+			<config type="dynamic" source="getConfigList" evaluation="selectionMade" />
+			<code>
+self.clearSelectedKeys()
+self.selectKey("LEFT")
+self.selectKey("RIGHT")
+self.selectKey("BLUE")
+self.selectKey("YELLOW")
+			</code>
+		</step>
+
+		<!-- Include / Exclude -->
+		<step id="conf5" nextstep="justbeforeend">
+			<text value="Filters are another powerful tool when matching events. An AutoTimer can be restricted to certain Weekdays or only match an event with a text inside eg it's Description.\nPress BLUE to add a new restriction and YELLOW to remove the selected one." />
+			<config type="dynamic" source="getConfigList" evaluation="selectionMade" />
+			<code>
+self.clearSelectedKeys()
+self.selectKey("LEFT")
+self.selectKey("RIGHT")
+self.selectKey("BLUE")
+self.selectKey("YELLOW")
+			</code>
+		</step>
+
+		<!-- Maxduration / avoidDuplicate / Destination ?! -->
+		<!--
+		<step id="conf6" nextstep="end">
+			<text value="" />
+			<config type="dynamic" source="getConfigList" evaluation="selectionMade" />
+			<code>
+self.clearSelectedKeys()
+self.selectKey("OK")
+			</code>
+		</step>
+		-->
+
+		<step id="justbeforeend">
+			<text value="You successfully configured a new AutoTimer. Do you want to add it to the list?\n\nYou can go back a step by pressing EXIT on your remote." />
+			<list>
+				<listentry caption="Yes" step="end" />
+				<listentry caption="No" step="abort" />
+			</list>
+			<code>
+self.clearSelectedKeys()
+self.selectKey("OK")
+self.selectKey("EXIT")
+			</code>
+		</step>
+
+		<step id="abort">
+			<text value="The Timer will not be added to the List.\nPlease press OK to close this Wizard." />
+			<code>
+self.clearSelectedKeys()
+self.selectKey("OK")
+			</code>
+			<code pos="after">
+self.cancel()
+			</code>
+		</step>
+
+		<step id="end">
+			<text value="Thank you for using the wizard. Your new AutoTimer has been added to the List.\nPlease press OK to continue." />
+			<code>
+self.clearSelectedKeys()
+self.selectKey("OK")
+			</code>
+		</step>
+</wizard>
+
Index: /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/maintainer.info
===================================================================
--- /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/maintainer.info	(revision 8760)
+++ /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/maintainer.info	(revision 8760)
@@ -0,0 +1,2 @@
+moritz.venn@freaque.net
+AutoTimer
Index: /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/plugin.py
===================================================================
--- /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/plugin.py	(revision 8760)
+++ /ipk/source.sh4/swapepg_autotimer_1_0/var/swap/extensions/AutoTimer/plugin.py	(revision 8760)
@@ -0,0 +1,170 @@
+# for localized messages
+from . import _
+
+# GUI (Screens)
+from Screens.MessageBox import MessageBox
+
+# Config
+from Components.config import config, ConfigSubsection, ConfigEnableDisable, \
+	ConfigNumber, ConfigSelection, ConfigYesNo
+
+# Plugin
+from Components.PluginComponent import plugins
+from Plugins.Plugin import PluginDescriptor
+
+# Initialize Configuration
+config.plugins.autotimer = ConfigSubsection()
+config.plugins.autotimer.autopoll = ConfigEnableDisable(default = False)
+config.plugins.autotimer.interval = ConfigNumber(default = 3)
+config.plugins.autotimer.refresh = ConfigSelection(choices = [
+		("none", _("None")),
+		("auto", _("Only AutoTimers created during this Session")),
+		("all", _("All non-repeating Timers"))
+	], default = "none"
+)
+config.plugins.autotimer.try_guessing = ConfigEnableDisable(default = True)
+config.plugins.autotimer.editor = ConfigSelection(choices = [
+		("plain", _("Classic")),
+		("wizard", _("Wizard"))
+	], default = "wizard"
+)
+config.plugins.autotimer.disabled_on_conflict = ConfigEnableDisable(default = False)
+config.plugins.autotimer.show_in_extensionsmenu = ConfigYesNo(default = False)
+
+autotimer = None
+autopoller = None
+
+# Autostart
+def autostart(reason, **kwargs):
+	global autotimer
+	global autopoller
+
+	# Startup
+	if config.plugins.autotimer.autopoll.value and reason == 0:
+		# Initialize AutoTimer
+		from AutoTimer import AutoTimer
+		autotimer = AutoTimer()
+
+		# Start Poller
+		from AutoPoller import AutoPoller
+		autopoller = AutoPoller()
+		autopoller.start()
+	# Shutdown
+	elif reason == 1:
+		# Stop Poller
+		if autopoller is not None:
+			autopoller.stop()
+			autopoller = None
+
+		if autotimer is not None:
+			# We re-read the config so we won't save wrong information
+			try:
+				autotimer.readXml()
+			except:
+				# XXX: we should at least dump the error
+				pass
+
+			# Save xml
+			autotimer.writeXml()
+
+			# Remove AutoTimer
+			autotimer = None
+
+# Mainfunction
+def main(session, **kwargs):
+	global autotimer
+	global autopoller
+
+	if autotimer is None:
+		from AutoTimer import AutoTimer
+		autotimer = AutoTimer()
+
+	try:
+		autotimer.readXml()
+	except SyntaxError, se:
+		session.open(
+			MessageBox,
+			_("Your config file is not well-formed:\n%s") % (str(se)),
+			type = MessageBox.TYPE_ERROR,
+			timeout = 10
+		)
+		return
+
+	# Do not run in background while editing, this might screw things up
+	if autopoller is not None:
+		autopoller.stop()
+
+	from AutoTimerOverview import AutoTimerOverview
+	session.openWithCallback(
+		editCallback,
+		AutoTimerOverview,
+		autotimer
+	)
+
+def editCallback(session):
+	global autotimer
+	global autopoller
+
+	# XXX: canceling of GUI (Overview) won't affect config values which might have been changed - is this intended?
+
+	# Don't parse EPG if editing was canceled
+	if session is not None:
+		# Poll EPGCache
+		ret = autotimer.parseEPG()
+		session.open(
+			MessageBox,
+			_("Found a total of %d matching Events.\n%d Timer were added and %d modified.") % (ret[0], ret[1], ret[2]),
+			type = MessageBox.TYPE_INFO,
+			timeout = 10
+		)
+
+		# Save xml
+		autotimer.writeXml()
+
+	# Start autopoller again if wanted
+	if config.plugins.autotimer.autopoll.value:
+		if autopoller is None:
+			from AutoPoller import AutoPoller
+			autopoller = AutoPoller()
+		autopoller.start(initial = False)
+	# Remove instance if not running in background
+	else:
+		autopoller = None
+		autotimer = None
+
+# Movielist
+def movielist(session, service, **kwargs):
+	from AutoTimerEditor import addAutotimerFromService
+	addAutotimerFromService(session, service)
+
+# Event Info
+def eventinfo(session, servicelist, **kwargs):
+	from AutoTimerEditor import AutoTimerEPGSelection
+	ref = session.nav.getCurrentlyPlayingServiceReference()
+	session.open(AutoTimerEPGSelection, ref)
+
+# XXX: we need this helper function to identify the descriptor
+# Extensions menu
+def extensionsmenu(session, **kwargs):
+	main(session, **kwargs)
+
+def housekeepingExtensionsmenu(el):
+	if el.value:
+		plugins.addPlugin(extDescriptor)
+	else:
+		plugins.removePlugin(extDescriptor)
+
+config.plugins.autotimer.show_in_extensionsmenu.addNotifier(housekeepingExtensionsmenu, initial_call = False, immediate_feedback = False)
+extDescriptor = PluginDescriptor(name="AutoTimer", description = _("Edit Timers and scan for new Events"), where = PluginDescriptor.WHERE_EXTENSIONSMENU, fnc = extensionsmenu)
+
+def Plugins(**kwargs):
+	l = [
+		PluginDescriptor(where = PluginDescriptor.WHERE_AUTOSTART, fnc = autostart),
+		PluginDescriptor(name="AutoTimer", description = _("Edit Timers and scan for new Events"), where = PluginDescriptor.WHERE_PLUGINMENU, icon = "plugin.png", fnc = main),
+		PluginDescriptor(name="AutoTimer", description= _("Add AutoTimer..."), where = PluginDescriptor.WHERE_MOVIELIST, fnc = movielist),
+		PluginDescriptor(name="AutoTimer", description= _("Add AutoTimer..."), where = PluginDescriptor.WHERE_EVENTINFO, fnc = eventinfo),
+	]
+	if config.plugins.autotimer.show_in_extensionsmenu.value:
+		l.append(extDescriptor)
+	return l
+
