Merge branch 'master' of git.opendreambox.org:/git/enigma2
[vuplus_dvbapp] / lib / python / Plugins / SystemPlugins / DiseqcTester / plugin.py
1 from Screens.Satconfig import NimSelection
2 from Screens.Screen import Screen
3 from Screens.TextBox import TextBox
4
5 from Plugins.Plugin import PluginDescriptor
6
7 from Components.ActionMap import ActionMap, NumberActionMap
8 from Components.NimManager import nimmanager
9 from Components.ResourceManager import resourcemanager
10 from Components.Sources.FrontendStatus import FrontendStatus
11 from Components.TuneTest import TuneTest
12 from Components.Sources.List import List
13 from Components.Sources.Progress import Progress
14 from Components.Sources.StaticText import StaticText
15 from Components.ConfigList import ConfigListScreen
16 from Components.config import getConfigListEntry, ConfigSelection
17
18 import random
19
20 # always use:
21 # setResultType(type)
22 # setResultParameter(parameter)
23 # getTextualResult()
24 class ResultParser:
25         def __init__(self):
26                 pass
27         
28         TYPE_BYORBPOS = 0
29         TYPE_BYINDEX = 1
30         TYPE_ALL = 2
31         def setResultType(self, type):
32                 self.type = type
33                 
34         def setResultParameter(self, parameter):
35                 if self.type == self.TYPE_BYORBPOS:
36                         self.orbpos = parameter
37                 elif self.type == self.TYPE_BYINDEX:
38                         self.index = parameter
39                         
40         def getTextualResultForIndex(self, index):
41                 text = ""
42                 text += "%s:\n" % self.getTextualIndexRepresentation(index)
43                 
44                 failed, successful = self.results[index]["failed"], self.results[index]["successful"]
45                 countfailed = len(failed)
46                 countsuccessful = len(successful)
47                 countall = countfailed + countsuccessful
48                 percentfailed = round(countfailed / float(countall + 0.0001) * 100)
49                 percentsuccessful = round(countsuccessful / float(countall + 0.0001) * 100)
50                 text += "Tested %d transponders\n%d (%d %%) transponders succeeded\n%d (%d %%) transponders failed\n" % (countall, countsuccessful, percentsuccessful, countfailed, percentfailed)
51                 reasons = {}
52                 if countfailed > 0:
53                         for transponder in failed:
54                                 reasons[transponder[2]] = reasons.get(transponder[2], [])
55                                 reasons[transponder[2]].append(transponder)
56                                 if transponder[2] == "pids_failed":
57                                         print transponder[2], "-", transponder[3]
58                                 
59                         text += "The %d unsuccessful tuning attempts failed for the following reasons:\n" % countfailed
60                         
61                         for reason in reasons.keys():
62                                 text += "%s: %d transponders failed\n" % (reason, len(reasons[reason]))
63                         
64                         for reason in reasons.keys():
65                                 text += "\n"
66                                 text += "%s previous planes:\n" % reason
67                                 for transponder in reasons[reason]:
68                                         if transponder[1] is not None:
69                                                 text += self.getTextualIndexRepresentation(self.getIndexForTransponder(transponder[1]))
70                                         else:
71                                                 text += "No transponder tuned"
72                                         text += " ==> " + self.getTextualIndexRepresentation(self.getIndexForTransponder(transponder[0]))
73                                         text += "\n"
74                 if countsuccessful > 0:
75                         text += "\n"
76                         text += "Successfully tuned transponders' previous planes:\n" 
77                         for transponder in successful:
78                                 if transponder[1] is not None:
79                                         text += self.getTextualIndexRepresentation(self.getIndexForTransponder(transponder[1]))
80                                 else:
81                                         text += "No transponder tuned"
82                                 text += " ==> " + self.getTextualIndexRepresentation(self.getIndexForTransponder(transponder[0]))
83                                 text += "\n"
84                 return text
85
86         def getTextualResult(self):
87                 text = ""
88                 if self.type == self.TYPE_BYINDEX:
89                         text += self.getTextualResultForIndex(self.index)
90                 elif self.type == self.TYPE_BYORBPOS:
91                         for index in self.results.keys():
92                                 if index[2] == self.orbpos:
93                                         text += self.getTextualResultForIndex(index)
94                                         text += "\n-----------------------------------------------------\n"
95                                 
96                 return text
97
98 class DiseqcTester(Screen, TuneTest, ResultParser):
99         skin = """
100                 <screen position="90,100" size="520,400" title="DiSEqC Tester" >
101                 <!--ePixmap pixmap="skin_default/icons/dish_scan.png" position="5,25" zPosition="0" size="119,110" transparent="1" alphatest="on" />
102                 <widget source="Frontend" render="Label" position="190,10" zPosition="2" size="260,20" font="Regular;19" halign="center" valign="center" transparent="1">
103                         <convert type="FrontendInfo">SNRdB</convert>
104                 </widget>
105                 <eLabel name="snr" text="SNR:" position="120,35" size="60,22" font="Regular;21" halign="right" transparent="1" />
106                 <widget source="Frontend" render="Progress" position="190,35" size="260,20" pixmap="skin_default/bar_snr.png" borderWidth="2" borderColor="#cccccc">
107                         <convert type="FrontendInfo">SNR</convert>
108                 </widget>
109                 <widget source="Frontend" render="Label" position="460,35" size="60,22" font="Regular;21">
110                         <convert type="FrontendInfo">SNR</convert>
111                 </widget>
112                 <eLabel name="agc" text="AGC:" position="120,60" size="60,22" font="Regular;21" halign="right" transparent="1" />
113                 <widget source="Frontend" render="Progress" position="190,60" size="260,20" pixmap="skin_default/bar_snr.png" borderWidth="2" borderColor="#cccccc">
114                         <convert type="FrontendInfo">AGC</convert>
115                 </widget>
116                 <widget source="Frontend" render="Label" position="460,60" size="60,22" font="Regular;21">
117                         <convert type="FrontendInfo">AGC</convert>
118                 </widget>
119                 <eLabel name="ber" text="BER:" position="120,85" size="60,22" font="Regular;21" halign="right" transparent="1" />
120                 <widget source="Frontend" render="Progress" position="190,85" size="260,20" pixmap="skin_default/bar_ber.png" borderWidth="2" borderColor="#cccccc">
121                         <convert type="FrontendInfo">BER</convert>
122                 </widget>
123                 <widget source="Frontend" render="Label" position="460,85" size="60,22" font="Regular;21">
124                         <convert type="FrontendInfo">BER</convert>
125                 </widget>
126                 <eLabel name="lock" text="Lock:" position="120,115" size="60,22" font="Regular;21" halign="right" />
127                 <widget source="Frontend" render="Pixmap" pixmap="skin_default/icons/lock_on.png" position="190,110" zPosition="1" size="38,31" alphatest="on">
128                         <convert type="FrontendInfo">LOCK</convert>
129                         <convert type="ConditionalShowHide" />
130                 </widget>
131                 <widget source="Frontend" render="Pixmap" pixmap="skin_default/icons/lock_off.png" position="190,110" zPosition="1" size="38,31" alphatest="on">
132                         <convert type="FrontendInfo">LOCK</convert>
133                         <convert type="ConditionalShowHide">Invert</convert>
134                 </widget-->
135                 <widget source="progress_list" render="Listbox" position="0,0" size="510,150" scrollbarMode="showOnDemand">
136                         <convert type="TemplatedMultiContent">
137                                 {"template": [
138                                                 MultiContentEntryText(pos = (10, 0), size = (330, 25), flags = RT_HALIGN_LEFT, text = 1), # index 1 is the index name,
139                                                 MultiContentEntryText(pos = (330, 0), size = (150, 25), flags = RT_HALIGN_RIGHT, text = 2) # index 2 is the status,
140                                         ],
141                                  "fonts": [gFont("Regular", 20)],
142                                  "itemHeight": 25
143                                 }
144                         </convert>
145                 </widget>
146                 <eLabel name="overall_progress" text="Overall progress:" position="20,162" size="480,22" font="Regular;21" halign="center" transparent="1" />
147                 <widget source="overall_progress" render="Progress" position="20,192" size="480,20" borderWidth="2" backgroundColor="#254f7497" />
148                 <eLabel name="overall_progress" text="Progress:" position="20,222" size="480,22" font="Regular;21" halign="center" transparent="1" />
149                 <widget source="sub_progress" render="Progress" position="20,252" size="480,20" borderWidth="2" backgroundColor="#254f7497" />
150                 
151                 <eLabel name="" text="Failed:" position="20,282" size="140,22" font="Regular;21" halign="left" transparent="1" />
152                 <widget source="failed_counter" render="Label" position="160,282" size="100,20" font="Regular;21" />
153                 
154                 <eLabel name="" text="Succeeded:" position="20,312" size="140,22" font="Regular;21" halign="left" transparent="1" />
155                 <widget source="succeeded_counter" render="Label" position="160,312" size="100,20" font="Regular;21" />
156                 
157                 <eLabel name="" text="With errors:" position="20,342" size="140,22" font="Regular;21" halign="left" transparent="1" />
158                 <widget source="witherrors_counter" render="Label" position="160,342" size="100,20" font="Regular;21" />
159                 
160                 <eLabel name="" text="Not tested:" position="20,372" size="140,22" font="Regular;21" halign="left" transparent="1" />
161                 <widget source="untestable_counter" render="Label" position="160,372" size="100,20" font="Regular;21" />
162                 
163                 <widget source="CmdText" render="Label" position="300,282" size="180,200" font="Regular;21" />
164                 </screen>"""
165                 
166         TEST_TYPE_QUICK = 0
167         TEST_TYPE_RANDOM = 1
168         TEST_TYPE_COMPLETE = 2
169         def __init__(self, session, feid, test_type = TEST_TYPE_QUICK, loopsfailed = 3, loopssuccessful = 1):
170                 Screen.__init__(self, session)
171                 self.feid = feid
172                 self.test_type = test_type
173                 self.loopsfailed = loopsfailed
174                 self.loopssuccessful = loopssuccessful
175                 
176                 self["actions"] = NumberActionMap(["SetupActions"],
177                 {
178                         "ok": self.select,
179                         "cancel": self.keyCancel,
180                 }, -2)
181                 
182                 TuneTest.__init__(self, feid, stopOnSuccess = self.loopssuccessful, stopOnError = self.loopsfailed)
183                 #self["Frontend"] = FrontendStatus(frontend_source = lambda : self.frontend, update_interval = 100)
184                 self["overall_progress"] = Progress()
185                 self["sub_progress"] = Progress()
186                 
187                 self["failed_counter"] = StaticText("0")
188                 self["succeeded_counter"] = StaticText("0")
189                 self["witherrors_counter"] = StaticText("0")
190                 self["untestable_counter"] = StaticText("0")
191                 
192                 self.list = []
193                 self["progress_list"] = List(self.list)
194                 self["progress_list"].onSelectionChanged.append(self.selectionChanged)
195                 
196                 self["CmdText"] = StaticText(_("Please wait while scanning is in progress..."))
197                                 
198                 self.indexlist = {}
199                 self.readTransponderList()
200                 
201                 self.running = False
202                 
203                 self.results = {}
204                 self.resultsstatus = {}
205                 
206                 self.onLayoutFinish.append(self.go)
207                 
208         def getProgressListComponent(self, index, status):
209                 return (index, self.getTextualIndexRepresentation(index), status)
210         
211         def clearProgressList(self):
212                 self.list = []
213                 self["progress_list"].list = self.list
214         
215         def addProgressListItem(self, index):
216                 if index in self.indexlist:
217                         for entry in self.list:
218                                 if entry[0] == index:
219                                         self.changeProgressListStatus(index, "working")
220                                         return
221                         self.list.append(self.getProgressListComponent(index, _("working")))
222                         self["progress_list"].list = self.list
223                         self["progress_list"].setIndex(len(self.list) - 1)
224
225         def changeProgressListStatus(self, index, status):
226                 self.newlist = []
227                 count = 0
228                 indexpos = 0
229                 for entry in self.list:
230                         if entry[0] == index:
231                                 self.newlist.append(self.getProgressListComponent(index, status))
232                                 indexpos = count
233                         else:
234                                 self.newlist.append(entry)
235                         count += 1
236                 self.list = self.newlist
237                 self["progress_list"].list = self.list
238                 self["progress_list"].setIndex(indexpos)
239
240         def readTransponderList(self):
241                 for sat in nimmanager.getSatListForNim(self.feid):
242                         for transponder in nimmanager.getTransponders(sat[0]):
243                                 #print transponder
244                                 mytransponder = (transponder[1] / 1000, transponder[2] / 1000, transponder[3], transponder[4], transponder[5], sat[0], None, None, transponder[10], transponder[11])
245                                 self.analyseTransponder(mytransponder)
246
247         def getIndexForTransponder(self, transponder):
248                 
249                 if transponder[0] < 11700:
250                         band = 1 # low
251                 else:
252                         band = 0 # high
253                 
254                 polarisation = transponder[2]
255                 
256                 sat = transponder[5]
257                 
258                 index = (band, polarisation, sat)
259                 return index
260
261         # sort the transponder into self.transponderlist
262         def analyseTransponder(self, transponder):
263                 index = self.getIndexForTransponder(transponder)
264                 if index not in self.indexlist:
265                         self.indexlist[index] = []
266                 self.indexlist[index].append(transponder)
267                 #print "self.indexlist:", self.indexlist
268         
269         # returns a string for the user representing a human readable output for index 
270         def getTextualIndexRepresentation(self, index):
271                 print "getTextualIndexRepresentation:", index
272                 text = ""
273                 
274                 text += nimmanager.getSatDescription(index[2]) + ", "
275                 
276                 if index[0] == 1:
277                         text += "Low Band, "
278                 else:
279                         text += "High Band, "
280                         
281                 if index[1] == 0:
282                         text += "H"
283                 else:
284                         text += "V"
285                 return text
286         
287         def fillTransponderList(self):
288                 self.clearTransponder()
289                 print "----------- fillTransponderList"
290                 print "index:", self.currentlyTestedIndex
291                 keys = self.indexlist.keys()
292                 if self.getContinueScanning():
293                         print "index:", self.getTextualIndexRepresentation(self.currentlyTestedIndex)
294                         for transponder in self.indexlist[self.currentlyTestedIndex]:
295                                 self.addTransponder(transponder)
296                         print "transponderList:", self.transponderlist
297                         return True
298                 else:
299                         return False
300                 
301         def progressCallback(self, progress):
302                 if progress[0] != self["sub_progress"].getRange():
303                         self["sub_progress"].setRange(progress[0])
304                 self["sub_progress"].setValue(progress[1])
305
306         # logic for scanning order of transponders
307         # on go getFirstIndex is called
308         def getFirstIndex(self):
309                 # TODO use other function to scan more randomly
310                 if self.test_type == self.TEST_TYPE_QUICK:
311                         self.myindex = 0
312                         keys = self.indexlist.keys()
313                         keys.sort(key = lambda a: a[2]) # sort by orbpos
314                         self["overall_progress"].setRange(len(keys))
315                         self["overall_progress"].setValue(self.myindex)
316                         return keys[0]
317                 elif self.test_type == self.TEST_TYPE_RANDOM:
318                         self.randomkeys = self.indexlist.keys()
319                         random.shuffle(self.randomkeys)
320                         self.myindex = 0
321                         self["overall_progress"].setRange(len(self.randomkeys))
322                         self["overall_progress"].setValue(self.myindex)
323                         return self.randomkeys[0]
324                 
325         # after each index is finished, getNextIndex is called to get the next index to scan 
326         def getNextIndex(self):
327                 # TODO use other function to scan more randomly
328                 if self.test_type == self.TEST_TYPE_QUICK:
329                         self.myindex += 1
330                         keys = self.indexlist.keys()
331                         keys.sort(key = lambda a: a[2]) # sort by orbpos
332                         
333                         self["overall_progress"].setValue(self.myindex)
334                         if self.myindex < len(keys):
335                                 return keys[self.myindex]
336                         else:
337                                 return None
338                 elif self.test_type == self.TEST_TYPE_RANDOM:
339                         self.myindex += 1
340                         keys = self.randomkeys
341                         
342                         self["overall_progress"].setValue(self.myindex)
343                         if self.myindex < len(keys):
344                                 return keys[self.myindex]
345                         else:
346                                 return None
347                                 
348         # after each index is finished and the next index is returned by getNextIndex
349         # the algorithm checks, if we should continue scanning
350         def getContinueScanning(self):
351                 if self.test_type == self.TEST_TYPE_QUICK or self.test_type == self.TEST_TYPE_RANDOM:
352                         return (self.myindex < len(self.indexlist.keys()))
353                 
354         def addResult(self, index, status, failedTune, successfullyTune):
355                 self.results[index] = self.results.get(index, {"failed": [], "successful": [], "status": None})
356                 self.resultsstatus[status] = self.resultsstatus.get(status, [])
357                 
358                 self.results[index]["status"] = status
359                 self.results[index]["failed"] = failedTune
360                 self.results[index]["successful"] = successfullyTune
361                 
362                 self.resultsstatus[status].append(index)
363         
364         def finishedChecking(self):
365                 print "finishedChecking"
366                 TuneTest.finishedChecking(self)
367
368                 if not self.results.has_key(self.currentlyTestedIndex):
369                         self.results[self.currentlyTestedIndex] = {"failed": [], "successful": [], "status": None}
370                 
371                 if len(self.failedTune) > 0 and len(self.successfullyTune) > 0:
372                         self.changeProgressListStatus(self.currentlyTestedIndex, "with errors")
373                         self["witherrors_counter"].setText(str(int(self["witherrors_counter"].getText()) + 1))
374                         self.addResult(self.currentlyTestedIndex, "with_errors", self.failedTune, self.successfullyTune)
375                 elif len(self.failedTune) == 0 and len(self.successfullyTune) == 0:
376                         self.changeProgressListStatus(self.currentlyTestedIndex, "not tested")
377                         self["untestable_counter"].setText(str(int(self["untestable_counter"].getText()) + 1))
378                         self.addResult(self.currentlyTestedIndex, "untestable", self.failedTune, self.successfullyTune)
379                 elif len(self.failedTune) > 0:
380                         self.changeProgressListStatus(self.currentlyTestedIndex, "failed")
381                         self["failed_counter"].setText(str(int(self["failed_counter"].getText()) + len(self.failedTune)))
382                         self.addResult(self.currentlyTestedIndex, "failed", self.failedTune, self.successfullyTune)
383                 else:
384                         self.changeProgressListStatus(self.currentlyTestedIndex, "successful")
385                         self["succeeded_counter"].setText(str(int(self["succeeded_counter"].getText()) + len(self.successfullyTune)))
386                         self.addResult(self.currentlyTestedIndex, "successful", self.failedTune, self.successfullyTune)
387                         
388                         
389                 #self["failed_counter"].setText(str(int(self["failed_counter"].getText()) + len(self.failedTune)))
390                 #self["succeeded_counter"].setText(str(int(self["succeeded_counter"].getText()) + len(self.successfullyTune)))
391                 #if len(self.failedTune) == 0 and len(self.successfullyTune) == 0:
392                         #self["untestable_counter"].setText(str(int(self["untestable_counter"].getText()) + 1))
393                         
394                 self.currentlyTestedIndex = self.getNextIndex()
395                 self.addProgressListItem(self.currentlyTestedIndex)
396                 
397                 if self.fillTransponderList():
398                         self.run(checkPIDs = True)
399                 else:
400                         self.running = False
401                         self["progress_list"].setIndex(0)
402                         print "results:", self.results
403                         print "resultsstatus:", self.resultsstatus
404
405         def go(self):
406                 self.running = True
407                 self["failed_counter"].setText("0")
408                 self["succeeded_counter"].setText("0")
409                 self["untestable_counter"].setText("0")
410                 self.currentlyTestedIndex = self.getFirstIndex()
411                 
412                 self.clearProgressList()
413                 self.addProgressListItem(self.currentlyTestedIndex)
414                 
415                 if self.fillTransponderList():
416                         self.run(True)
417
418         def keyCancel(self):
419                 self.close()
420                 
421         def select(self):
422                 print "selectedIndex:", self["progress_list"].getCurrent()[0]
423                 if not self.running:
424                         index = self["progress_list"].getCurrent()[0]
425                         #self.setResultType(ResultParser.TYPE_BYORBPOS)
426                         #self.setResultParameter(index[2])
427                         self.setResultType(ResultParser.TYPE_BYINDEX)
428                         self.setResultParameter(index)
429                         self.session.open(TextBox, self.getTextualResult())
430         
431         def selectionChanged(self):
432                 print "selection changed"
433                 if len(self.list) > 0 and not self.running:
434                         self["CmdText"].setText(_("Press OK to get further details for %s") % str(self["progress_list"].getCurrent()[1]))
435
436 class DiseqcTesterTestTypeSelection(Screen, ConfigListScreen):
437         skin = """<screen position="80,95" size="560,412" title="DiSEqC Tester Test Settings">
438                 <widget name="config" position="10,10" size="540,402" scrollbarMode="showOnDemand" />
439         </screen>
440         """
441         def __init__(self, session, feid):
442                 Screen.__init__(self, session)
443                 self.feid = feid
444                 
445                 self.list = []
446                 ConfigListScreen.__init__(self, self.list)
447                 
448                 self["actions"] = ActionMap(["SetupActions"],
449                 {
450                         "cancel": self.keyCancel
451                 }, -2)
452                 
453                 self.createSetup()
454                 
455         def createSetup(self):
456                 self.testtype = ConfigSelection(choices={"quick": _("Quick"), "random": _("Random")}, default = "quick")
457                 self.testtypeEntry = getConfigListEntry(_("Test Type"), self.testtype)
458                 self.list.append(self.testtypeEntry)
459                 
460                 self.loopsfailed = ConfigSelection(choices={"-1": "Every known", "1": "1", "2": "2", "3": "3", "4": "4", "5": "5", "6": "6", "7": "7", "8": "8"}, default = "3")
461                 self.loopsfailedEntry = getConfigListEntry(_("Stop testing plane after # failed transponders"), self.loopsfailed)
462                 self.list.append(self.loopsfailedEntry)
463                 
464                 self.loopssuccessful = ConfigSelection(choices={"-1": "Every known", "1": "1", "2": "2", "3": "3", "4": "4", "5": "5", "6": "6", "7": "7", "8": "8"}, default = "1")
465                 self.loopssuccessfulEntry = getConfigListEntry(_("Stop testing plane after # successful transponders"), self.loopssuccessful)
466                 self.list.append(self.loopssuccessfulEntry)
467                 
468                 self["config"].list = self.list
469                 self["config"].l.setList(self.list)
470                 
471         def keyOK(self):
472                 print self.testtype.getValue()
473                 testtype = DiseqcTester.TEST_TYPE_QUICK
474                 if self.testtype.getValue() == "quick":
475                         testtype = DiseqcTester.TEST_TYPE_QUICK
476                 elif self.testtype.getValue() == "random":
477                         testtype = DiseqcTester.TEST_TYPE_RANDOM
478                 elif self.testtype.getValue() == "complete":
479                         testtype = DiseqcTester.TEST_TYPE_COMPLETE
480                 self.session.open(DiseqcTester, feid = self.feid, test_type = testtype, loopsfailed = int(self.loopsfailed.value), loopssuccessful = int(self.loopssuccessful.value))
481         
482         def keyCancel(self):
483                 self.close()
484
485 class DiseqcTesterNimSelection(NimSelection):
486         skin = """
487                 <screen position="160,123" size="400,330" title="Choose Tuner">
488                 <widget source="nimlist" render="Listbox" position="0,0" size="380,300" scrollbarMode="showOnDemand">
489                         <convert type="TemplatedMultiContent">
490                                 {"template": [
491                                                 MultiContentEntryText(pos = (10, 5), size = (360, 30), flags = RT_HALIGN_LEFT, text = 1), # index 1 is the nim name,
492                                                 MultiContentEntryText(pos = (50, 30), size = (320, 30), font = 1, flags = RT_HALIGN_LEFT, text = 2), # index 2 is a description of the nim settings,
493                                         ],
494                                  "fonts": [gFont("Regular", 20), gFont("Regular", 15)],
495                                  "itemHeight": 70
496                                 }
497                         </convert>
498                 </widget>
499         </screen>"""
500                 
501         def __init__(self, session, args = None):
502                 NimSelection.__init__(self, session)
503
504         def setResultClass(self):
505                 #self.resultclass = DiseqcTester
506                 self.resultclass = DiseqcTesterTestTypeSelection
507                 
508         def showNim(self, nim):
509                 nimConfig = nimmanager.getNimConfig(nim.slot)
510                 if nim.isCompatible("DVB-S"):
511                         if nimConfig.configMode.value in ["loopthrough", "equal", "satposdepends", "nothing"]:
512                                 return False
513                         if nimConfig.configMode.value == "simple":
514                                 if nimConfig.diseqcMode.value == "positioner":
515                                         return True
516                         return True
517                 return False
518
519 def DiseqcTesterMain(session, **kwargs):
520         session.open(DiseqcTesterNimSelection)
521         
522 def autostart(reason, **kwargs):
523         resourcemanager.addResource("DiseqcTester", DiseqcTesterMain)
524
525 def Plugins(**kwargs):
526         return [ PluginDescriptor(name="DiSEqC Tester", description=_("Test DiSEqC settings"), where = PluginDescriptor.WHERE_PLUGINMENU, fnc=DiseqcTesterMain),
527                         PluginDescriptor(where = PluginDescriptor.WHERE_AUTOSTART, fnc = autostart)]