15 msec delay between disable tone and send diseqc
[vuplus_dvbapp] / lib / python / Plugins / SystemPlugins / PositionerSetup / plugin.py
1 from enigma import eTimer, eDVBSatelliteEquipmentControl, eDVBResourceManager, \
2         eDVBDiseqcCommand, eDVBFrontendParametersSatellite, eDVBFrontendParameters
3
4 from Screens.Screen import Screen
5 from Screens.ScanSetup import ScanSetup
6 from Screens.MessageBox import MessageBox
7 from Plugins.Plugin import PluginDescriptor
8
9 from Components.Label import Label
10 from Components.ConfigList import ConfigList
11 from Components.TunerInfo import TunerInfo
12 from Components.ActionMap import ActionMap
13 from Components.NimManager import nimmanager
14 from Components.MenuList import MenuList
15 from Components.config import ConfigSatlist, ConfigNothing, ConfigSelection, ConfigSubsection, KEY_LEFT, KEY_RIGHT, getConfigListEntry
16
17 from time import sleep
18
19 class PositionerSetup(Screen):
20         skin = """
21                 <screen position="100,100" size="560,400" title="Positioner setup..." >
22                         <widget name="list" position="100,0" size="350,155" />
23
24                         <widget name="red" position="0,155" size="140,80" backgroundColor="red" halign="center" valign="center" font="Regular;21" />
25                         <widget name="green" position="140,155" size="140,80" backgroundColor="green" halign="center" valign="center" font="Regular;21" />
26                         <widget name="yellow" position="280,155" size="140,80" backgroundColor="yellow" halign="center" valign="center" font="Regular;21" />
27                         <widget name="blue" position="420,155" size="140,80" backgroundColor="blue" halign="center" valign="center" font="Regular;21" />
28                         
29                         <widget name="snr" text="SNR:" position="0,245" size="60,22" font="Regular;21" />
30                         <widget name="agc" text="AGC:" position="0,270" size="60,22" font="Regular;21" />
31                         <widget name="ber" text="BER:" position="0,295" size="60,22" font="Regular;21" />
32                         <widget name="lock" text="Lock:" position="0,320" size="60,22" font="Regular;21" />
33                         <widget name="snr_percentage" position="220,245" size="60,22" font="Regular;21" />
34                         <widget name="agc_percentage" position="220,270" size="60,22" font="Regular;21" />
35                         <widget name="ber_value" position="220,295" size="60,22" font="Regular;21" />
36                         <widget name="lock_state" position="60,320" size="150,22" font="Regular;21" />
37                         <widget name="snr_bar" position="60,245" size="150,22" />
38                         <widget name="agc_bar" position="60,270" size="150,22" />
39                         <widget name="ber_bar" position="60,295" size="150,22" />
40
41                         <widget name="frequency" text="Frequency:" position="300,245" size="120,22" font="Regular;21" />
42                         <widget name="symbolrate" text="Symbolrate:" position="300,270" size="120,22" font="Regular;21" />
43                         <widget name="fec" text="FEC:" position="300,295" size="120,22" font="Regular;21" />
44                         <widget name="frequency_value" position="420,245" size="120,22" font="Regular;21" />
45                         <widget name="symbolrate_value" position="420,270" size="120,22" font="Regular;21" />
46                         <widget name="fec_value" position="420,295" size="120,22" font="Regular;21" />
47                 </screen>"""
48         def __init__(self, session, feid):
49                 self.skin = PositionerSetup.skin
50                 Screen.__init__(self, session)
51                 self.feid = feid
52                 self.oldref = None
53                 
54                 if not self.openFrontend():
55                         self.oldref = session.nav.getCurrentlyPlayingServiceReference()
56                         session.nav.stopService() # try to disable foreground service
57                         if not self.openFrontend():
58                                 if session.pipshown: # try to disable pip
59                                         session.pipshown = False
60                                         del session.pip
61                                         if not self.openFrontend():
62                                                 self.frontend = None # in normal case this should not happen
63                                                 self.getFrontend = None
64                 
65                 self.diseqc = Diseqc(self.frontend)
66                 self.tuner = Tuner(self.frontend)
67                 self.tuner.tune((0,0,0,0,0,0))
68                 
69                 self.createConfig()
70                 
71                 self.isMoving = False
72                 self.stopOnLock = False
73                 
74                 self.red = Label("")
75                 self["red"] = self.red
76                 self.green = Label("")
77                 self["green"] = self.green
78                 self.yellow = Label("")
79                 self["yellow"] = self.yellow
80                 self.blue = Label("")
81                 self["blue"] = self.blue
82                 
83                 self.list = []
84                 self["list"] = ConfigList(self.list)
85                 self.createSetup()
86                 
87                 self["snr"] = Label()
88                 self["agc"] = Label()
89                 self["ber"] = Label()
90                 self["lock"] = Label()
91                 self["snr_percentage"] = TunerInfo(TunerInfo.SNR_PERCENTAGE, frontendfkt = self.getFrontend)
92                 self["agc_percentage"] = TunerInfo(TunerInfo.AGC_PERCENTAGE, frontendfkt = self.getFrontend)
93                 self["ber_value"] = TunerInfo(TunerInfo.BER_VALUE, frontendfkt = self.getFrontend)
94                 self["snr_bar"] = TunerInfo(TunerInfo.SNR_BAR, frontendfkt = self.getFrontend)
95                 self["agc_bar"] = TunerInfo(TunerInfo.AGC_BAR, frontendfkt = self.getFrontend)
96                 self["ber_bar"] = TunerInfo(TunerInfo.BER_BAR, frontendfkt = self.getFrontend)
97                 self["lock_state"] = TunerInfo(TunerInfo.LOCK_STATE, frontendfkt = self.getFrontend)
98
99                 self["frequency"] = Label()
100                 self["symbolrate"] = Label()
101                 self["fec"] = Label()
102
103                 self["frequency_value"] = Label("")
104                 self["symbolrate_value"] = Label("")
105                 self["fec_value"] = Label("")
106                 
107                 self["actions"] = ActionMap(["DirectionActions", "OkCancelActions", "ColorActions"],
108                 {
109                         "ok": self.go,
110                         "cancel": self.keyCancel,
111                         "up": self.up,
112                         "down": self.down,
113                         "left": self.left,
114                         "right": self.right,
115                         "red": self.redKey,
116                         "green": self.greenKey,
117                         "yellow": self.yellowKey,
118                         "blue": self.blueKey,
119                 }, -1)
120                 
121                 self.updateColors("tune")
122                 
123                 self.statusTimer = eTimer()
124                 self.statusTimer.timeout.get().append(self.updateStatus)
125                 self.statusTimer.start(50, False)
126
127         def restartPrevService(self, yesno):
128                 if yesno:
129                         if self.frontend:
130                                 self.frontend = None
131                                 del self.raw_channel
132                         self.session.nav.playService(self.oldref)
133                 self.close(None)
134         
135         def keyCancel(self):
136                 if self.oldref:
137                         self.session.openWithCallback(self.restartPrevService, MessageBox, _("Zap back to service before positioner setup?"), MessageBox.TYPE_YESNO)
138                 else:
139                         self.restartPrevService(False)
140
141         def getFrontend(self):
142                 return self.frontend
143
144         def openFrontend(self):
145                 res_mgr = eDVBResourceManager.getInstance()
146                 if res_mgr:
147                         self.raw_channel = res_mgr.allocateRawChannel(self.feid)
148                         if self.raw_channel:
149                                 self.frontend = self.raw_channel.getFrontend()
150                                 if self.frontend:
151                                         return True
152                                 else:
153                                         print "getFrontend failed"
154                         else:
155                                 print "getRawChannel failed"
156                 else:
157                         print "getResourceManager instance failed"
158                 return False
159
160         def createConfig(self):
161                 self.positioner_tune = ConfigNothing()
162                 self.positioner_move = ConfigNothing()
163                 self.positioner_finemove = ConfigNothing()
164                 self.positioner_limits = ConfigNothing()
165                 self.positioner_goto0 = ConfigNothing()
166                 storepos = []
167                 for x in range(1,255):
168                         storepos.append(str(x))
169                 self.positioner_storage = ConfigSelection(choices = storepos)
170
171         def createSetup(self):
172                 self.list.append((_("Tune"), self.positioner_tune, "tune"))
173                 self.list.append((_("Positioner movement"), self.positioner_move, "move"))
174                 self.list.append((_("Positioner fine movement"), self.positioner_finemove, "finemove"))
175                 self.list.append((_("Set limits"), self.positioner_limits, "limits"))
176                 self.list.append((_("Positioner storage"), self.positioner_storage, "storage"))
177                 self.list.append((_("Goto 0"), self.positioner_goto0, "goto0"))
178                 self["list"].l.setList(self.list)
179
180         def go(self):
181                 pass
182
183         def getCurrentConfigPath(self):
184                 return self["list"].getCurrent()[2]
185
186         def up(self):
187                 if not self.isMoving:
188                         self["list"].instance.moveSelection(self["list"].instance.moveUp)
189                         self.updateColors(self.getCurrentConfigPath())
190
191         def down(self):
192                 if not self.isMoving:
193                         self["list"].instance.moveSelection(self["list"].instance.moveDown)
194                         self.updateColors(self.getCurrentConfigPath())
195
196         def left(self):
197                 self["list"].handleKey(KEY_LEFT)
198
199         def right(self):
200                 self["list"].handleKey(KEY_RIGHT)
201
202         def updateColors(self, entry):
203                 if entry == "tune":
204                         self.red.setText(_("Tune"))
205                         self.green.setText("")
206                         self.yellow.setText("")
207                         self.blue.setText("")
208                 elif entry == "move":
209                         if self.isMoving:
210                                 self.red.setText(_("Stop"))
211                                 self.green.setText(_("Stop"))
212                                 self.yellow.setText(_("Stop"))
213                                 self.blue.setText(_("Stop"))
214                         else:
215                                 self.red.setText(_("Move west"))
216                                 self.green.setText(_("Search west"))
217                                 self.yellow.setText(_("Search east"))
218                                 self.blue.setText(_("Move east"))
219                 elif entry == "finemove":
220                         self.red.setText("")
221                         self.green.setText(_("Step west"))
222                         self.yellow.setText(_("Step east"))
223                         self.blue.setText("")
224                 elif entry == "limits":
225                         self.red.setText(_("Limits off"))
226                         self.green.setText(_("Limit west"))
227                         self.yellow.setText(_("Limit east"))
228                         self.blue.setText(_("Limits on"))
229                 elif entry == "storage":
230                         self.red.setText("")
231                         self.green.setText(_("Store position"))
232                         self.yellow.setText(_("Goto position"))
233                         self.blue.setText("")
234                 elif entry == "goto0":
235                         self.red.setText(_("Goto 0"))
236                         self.green.setText("")
237                         self.yellow.setText("")
238                         self.blue.setText("")
239                 else:
240                         self.red.setText("")
241                         self.green.setText("")
242                         self.yellow.setText("")
243                         self.blue.setText("")
244
245         def redKey(self):
246                 entry = self.getCurrentConfigPath()
247                 if entry == "move":
248                         if self.isMoving:
249                                 self.diseqccommand("stop")
250                                 self.isMoving = False
251                                 self.stopOnLock = False
252                         else:
253                                 self.diseqccommand("moveWest", 0)
254                                 self.isMoving = True
255                         self.updateColors("move")
256                 elif entry == "limits":
257                         self.diseqccommand("limitOff")
258                 elif entry == "tune":
259                         self.session.openWithCallback(self.tune, TunerScreen, self.feid)
260                 elif entry == "goto0":
261                         print "move to position 0"
262                         self.diseqccommand("moveTo", 0)
263
264         def greenKey(self):
265                 entry = self.getCurrentConfigPath()
266                 if entry == "move":
267                         if self.isMoving:
268                                 self.diseqccommand("stop")
269                                 self.isMoving = False
270                                 self.stopOnLock = False
271                         else:
272                                 self.isMoving = True
273                                 self.stopOnLock = True
274                                 self.diseqccommand("moveWest", 0)
275                         self.updateColors("move")
276                 elif entry == "finemove":
277                         print "stepping west"
278                         self.diseqccommand("moveWest", 0xFF) # one step
279                 elif entry == "storage":
280                         print "store at position", int(self.positioner_storage.value)
281                         self.diseqccommand("store", int(self.positioner_storage.value))
282                 elif entry == "limits":
283                         self.diseqccommand("limitWest")
284
285         def yellowKey(self):
286                 entry = self.getCurrentConfigPath()
287                 if entry == "move":
288                         if self.isMoving:
289                                 self.diseqccommand("stop")
290                                 self.isMoving = False
291                                 self.stopOnLock = False
292                         else:
293                                 self.isMoving = True
294                                 self.stopOnLock = True
295                                 self.diseqccommand("moveEast", 0)
296                         self.updateColors("move")
297                 elif entry == "finemove":
298                         print "stepping east"
299                         self.diseqccommand("moveEast", 0xFF) # one step
300                 elif entry == "storage":
301                         print "move to position", int(self.positioner_storage.value)
302                         self.diseqccommand("moveTo", int(self.positioner_storage.value))
303                 elif entry == "limits":
304                         self.diseqccommand("limitEast")
305
306         def blueKey(self):
307                 entry = self.getCurrentConfigPath()
308                 if entry == "move":
309                         if self.isMoving:
310                                 self.diseqccommand("stop")
311                                 self.isMoving = False
312                                 self.stopOnLock = False
313                         else:
314                                 self.diseqccommand("moveEast", 0)
315                                 self.isMoving = True
316                         self.updateColors("move")
317                         print "moving east"
318                 elif entry == "limits":
319                         self.diseqccommand("limitOn")
320
321         def diseqccommand(self, cmd, param = 0):
322                 self.diseqc.command(cmd, param)
323                 self.tuner.retune()
324
325         def updateStatus(self):
326                 self["snr_percentage"].update()
327                 self["agc_percentage"].update()
328                 self["ber_value"].update()
329                 self["snr_bar"].update()
330                 self["agc_bar"].update()
331                 self["ber_bar"].update()
332                 self["lock_state"].update()
333                 transponderdata = self.tuner.getTransponderData()
334                 self["frequency_value"].setText(str(transponderdata["frequency"]))
335                 self["symbolrate_value"].setText(str(transponderdata["symbol_rate"]))
336                 self["fec_value"].setText(str(transponderdata["fec_inner"]))
337                 if transponderdata["tuner_locked"] == 1 and self.isMoving and self.stopOnLock:
338                         self.diseqccommand("stop")
339                         self.isMoving = False
340                         self.stopOnLock = False
341                         self.updateColors(self.getCurrentConfigPath())
342
343         def tune(self, transponder):
344                 if transponder is not None:
345                         self.tuner.tune(transponder)
346
347 class Diseqc:
348         def __init__(self, frontend):
349                 self.frontend = frontend
350
351         def command(self, what, param = 0):
352                 if self.frontend:
353                         cmd = eDVBDiseqcCommand()
354                         if what == "moveWest":
355                                 string = 'e03169' + ("%02x" % param)
356                         elif what == "moveEast":
357                                 string = 'e03168' + ("%02x" % param)
358                         elif what == "moveTo":
359                                 string = 'e0316b' + ("%02x" % param)
360                         elif what == "store":
361                                 string = 'e0316a' + ("%02x" % param)
362                         elif what == "limitOn":
363                                 string = 'e0316a00'
364                         elif what == "limitOff":
365                                 string = 'e03163'
366                         elif what == "limitEast":
367                                 string = 'e03166'
368                         elif what == "limitWest":
369                                 string = 'e03167'
370                         else:
371                                 string = 'e03160' #positioner stop
372                         
373                         print "diseqc command:",
374                         print string
375                         cmd.setCommandString(string)
376                         self.frontend.setTone(iDVBFrontend.toneOff)
377                         sleep(0.015) # wait 15msec after disable tone
378                         self.frontend.sendDiseqc(cmd)
379                         if string == 'e03160': #positioner stop
380                                 sleep(0.05)
381                                 self.frontend.sendDiseqc(cmd) # send 2nd time
382
383 class Tuner:
384         def __init__(self, frontend):
385                 self.frontend = frontend
386
387         def tune(self, transponder):
388                 print "tuning to transponder with data", transponder
389                 parm = eDVBFrontendParametersSatellite()
390                 parm.frequency = transponder[0] * 1000
391                 parm.symbol_rate = transponder[1] * 1000
392                 parm.polarisation = transponder[2]
393                 parm.fec = transponder[3]
394                 parm.inversion = transponder[4]
395                 parm.orbital_position = transponder[5]
396                 parm.system = 0  # FIXMEE !! HARDCODED DVB-S (add support for DVB-S2)
397                 parm.modulation = 1 # FIXMEE !! HARDCODED QPSK
398                 feparm = eDVBFrontendParameters()
399                 feparm.setDVBS(parm, True)
400                 self.lastparm = feparm
401                 if self.frontend:
402                         self.frontend.tune(feparm)
403
404         def retune(self):
405                 if self.frontend:
406                         self.frontend.tune(self.lastparm)
407
408         def getTransponderData(self):
409                 if self.frontend:
410                         return self.frontend.readTransponderData(True)
411                 return None
412
413 tuning = None
414
415 class TunerScreen(ScanSetup):
416         skin = """
417                 <screen position="90,100" size="520,400" title="Tune">
418                         <widget name="config" position="20,10" size="460,350" scrollbarMode="showOnDemand" />
419                         <widget name="introduction" position="20,360" size="350,30" font="Regular;23" />
420                 </screen>"""
421
422         def __init__(self, session, feid):
423                 self.feid = feid
424                 ScanSetup.__init__(self, session)
425                 self["introduction"].setText("")
426
427         def createSetup(self):
428                 self.typeOfTuningEntry = None
429                 self.satEntry = None
430                 self.list = []
431                 self.typeOfTuningEntry = getConfigListEntry(_('Tune'), tuning.type)
432                 self.list.append(self.typeOfTuningEntry)
433                 self.satEntry = getConfigListEntry(_('Satellite'), tuning.sat)
434                 self.list.append(self.satEntry)
435                 if tuning.type.value == "manual_transponder":
436                         self.list.append(getConfigListEntry(_('Frequency'), self.scan_sat.frequency))
437                         self.list.append(getConfigListEntry(_('Inversion'), self.scan_sat.inversion))
438                         self.list.append(getConfigListEntry(_('Symbol Rate'), self.scan_sat.symbolrate))
439                         self.list.append(getConfigListEntry(_("Polarity"), self.scan_sat.polarization))
440                         self.list.append(getConfigListEntry(_("FEC"), self.scan_sat.fec))
441                 elif tuning.type.value == "predefined_transponder":
442                         self.list.append(getConfigListEntry(_("Transponder"), tuning.transponder))
443                 self["config"].list = self.list
444                 self["config"].l.setList(self.list)
445
446         def newConfig(self):
447                 if self["config"].getCurrent() == self.typeOfTuningEntry:
448                         self.createSetup()
449                 elif self["config"].getCurrent() == self.satEntry:
450                         self.createSetup()
451
452         def createConfig(self, foo):
453                 global tuning
454                 if not tuning:
455                         tuning = ConfigSubsection()
456                         tuning.type = ConfigSelection(
457                                 default = "manual_transponder",
458                                 choices = { "manual_transponder" : _("Manual transponder"),
459                                                         "predefined_transponder" : _("Predefined transponder") } )
460                         tuning.sat = ConfigSatlist(list=nimmanager.getRotorSatListForNim(self.feid))
461                         tuning.sat.addNotifier(self.tuningSatChanged)
462                         self.updateTransponders()
463                         TunerScreenConfigCreated = True
464                 ScanSetup.createConfig(self, None)
465
466         def tuningSatChanged(self, *parm):
467                 self.updateTransponders()
468
469         def updateTransponders(self):
470                 if len(tuning.sat.choices):
471                         transponderlist = nimmanager.getTransponders(int(tuning.sat.value))
472                         tps = []
473                         cnt=0
474                         for x in transponderlist:
475                                 if x[3] == 0:
476                                         pol = "H"
477                                 elif x[3] == 1:
478                                         pol = "V"
479                                 elif x[3] == 2:
480                                         pol = "CL"
481                                 elif x[3] == 3:
482                                         pol = "CR"
483                                 if x[4] == 0:
484                                         fec = "FEC_AUTO"
485                                 elif x[4] == 1:
486                                         fec = "FEC_1_2"
487                                 elif x[4] == 2:
488                                         fec = "FEC_2_3"
489                                 elif x[4] == 3:
490                                         fec = "FEC_3_4"
491                                 elif x[4] == 4:
492                                         fec = "FEC_5_6"
493                                 elif x[4] == 5:
494                                         fec = "FEC_7_8"
495                                 elif x[4] == 5:
496                                         fec = "FEC_8_9"
497                                 elif x[4] == 6:
498                                         fec = "FEC_None"
499                                 tps.append(str(x[1]) + "," + str(x[2]) + "," + pol + "," + fec)
500                         tuning.transponder = ConfigSelection(choices=tps)
501
502         def keyGo(self):
503                 returnvalue = (0, 0, 0, 0, 0, 0)
504                 satpos = int(tuning.sat.value)
505                 if tuning.type.value == "manual_transponder":
506                         returnvalue = (
507                                 self.scan_sat.frequency.value,
508                                 self.scan_sat.symbolrate.value,
509                                 self.scan_sat.polarization.index,
510                                 self.scan_sat.fec.index,
511                                 self.scan_sat.inversion.index,
512                                 satpos)
513                 elif tuning.type.value == "predefined_transponder":
514                         transponder = nimmanager.getTransponders(satpos)[tuning.transponder.index]
515                         returnvalue = (int(transponder[1] / 1000), int(transponder[2] / 1000), transponder[3], transponder[4], 2, satpos)
516                 self.close(returnvalue)
517
518         def keyCancel(self):
519                 self.close(None)
520
521 class NimSelection(Screen):
522         skin = """
523                 <screen position="140,165" size="400,100" title="select Slot">
524                         <widget name="nimlist" position="20,10" size="360,75" />
525                 </screen>"""
526
527         def __init__(self, session):
528                 Screen.__init__(self, session)
529
530                 nimlist = nimmanager.getNimListOfType(nimmanager.nimType["DVB-S"])
531                 nimMenuList = []
532                 for x in nimlist:
533                         nimMenuList.append((_("NIM ") + (["A", "B", "C", "D"][x]) + ": " + nimmanager.getNimName(x) + " (" + nimmanager.getNimTypeName(x) + ")", x))
534                 
535                 self["nimlist"] = MenuList(nimMenuList)
536
537                 self["actions"] = ActionMap(["OkCancelActions"],
538                 {
539                         "ok": self.okbuttonClick ,
540                         "cancel": self.close
541                 }, -1)
542
543         def okbuttonClick(self):
544                 selection = self["nimlist"].getCurrent()
545                 self.session.open(PositionerSetup, selection[1])
546
547 def PositionerMain(session, **kwargs):
548         nimList = nimmanager.getNimListOfType(nimmanager.nimType["DVB-S"])
549         if len(nimList) == 0:
550                 session.open(MessageBox, _("No positioner capable frontend found."), MessageBox.TYPE_ERROR)
551         else:
552                 if session.nav.RecordTimer.isRecording():
553                         session.open(MessageBox, _("A recording is currently running. Please stop the recording before trying to configure the positioner."), MessageBox.TYPE_ERROR)
554                 else:
555                         usableNims = []
556                         for x in nimList:
557                                 configured_rotor_sats = nimmanager.getRotorSatListForNim(x)
558                                 if len(configured_rotor_sats) != 0:
559                                         usableNims.append(x)
560                         if len(usableNims) == 1:
561                                 session.open(PositionerSetup, usableNims[0])
562                         elif len(usableNims) > 1:
563                                 session.open(NimSelection)
564                         else:
565                                 session.open(MessageBox, _("No tuner is configured for use with a diseqc positioner!"), MessageBox.TYPE_ERROR)
566
567 def PositionerSetupStart(menuid):
568         if menuid == "scan":
569                 return [(_("Positioner setup"), PositionerMain)]
570         else:
571                 return []
572
573 def Plugins(**kwargs):
574         return PluginDescriptor(name=_("Positioner setup"), description="Setup your positioner", where = PluginDescriptor.WHERE_SETUP, fnc=PositionerSetupStart)