From 046310e3051a490a5d423af43a22c9bbb9802129 Mon Sep 17 00:00:00 2001 From: kos Date: Fri, 9 Dec 2011 13:21:56 +0900 Subject: [PATCH] fix blindscan blindscan and add firmwareupgrade plugin --- configure.ac | 2 + .../Plugins/SystemPlugins/Blindscan/plugin.py | 73 ++- .../SystemPlugins/FirmwareUpgrade/Makefile.am | 7 + .../SystemPlugins/FirmwareUpgrade/__init__.py | 0 .../Plugins/SystemPlugins/FirmwareUpgrade/_fu.so | Bin 0 -> 51460 bytes .../Plugins/SystemPlugins/FirmwareUpgrade/fu.py | 92 ++++ .../SystemPlugins/FirmwareUpgrade/meta/Makefile.am | 3 + .../meta/plugin_firmwareupgrade.xml | 16 + .../SystemPlugins/FirmwareUpgrade/plugin.py | 555 +++++++++++++++++++++ lib/python/Plugins/SystemPlugins/Makefile.am | 2 +- 10 files changed, 734 insertions(+), 16 deletions(-) create mode 100644 lib/python/Plugins/SystemPlugins/FirmwareUpgrade/Makefile.am create mode 100644 lib/python/Plugins/SystemPlugins/FirmwareUpgrade/__init__.py create mode 100644 lib/python/Plugins/SystemPlugins/FirmwareUpgrade/_fu.so create mode 100644 lib/python/Plugins/SystemPlugins/FirmwareUpgrade/fu.py create mode 100644 lib/python/Plugins/SystemPlugins/FirmwareUpgrade/meta/Makefile.am create mode 100644 lib/python/Plugins/SystemPlugins/FirmwareUpgrade/meta/plugin_firmwareupgrade.xml create mode 100644 lib/python/Plugins/SystemPlugins/FirmwareUpgrade/plugin.py diff --git a/configure.ac b/configure.ac index 5ca12a5..242f2d8 100644 --- a/configure.ac +++ b/configure.ac @@ -244,6 +244,8 @@ lib/python/Plugins/SystemPlugins/HDMICEC/components/Makefile lib/python/Plugins/SystemPlugins/HDMICEC/meta/Makefile lib/python/Plugins/SystemPlugins/LEDBrightnessSetup/Makefile lib/python/Plugins/SystemPlugins/LEDBrightnessSetup/meta/Makefile +lib/python/Plugins/SystemPlugins/FirmwareUpgrade/Makefile +lib/python/Plugins/SystemPlugins/FirmwareUpgrade/meta/Makefile lib/python/Tools/Makefile lib/service/Makefile lib/components/Makefile diff --git a/lib/python/Plugins/SystemPlugins/Blindscan/plugin.py b/lib/python/Plugins/SystemPlugins/Blindscan/plugin.py index c456f26..fb22f1e 100644 --- a/lib/python/Plugins/SystemPlugins/Blindscan/plugin.py +++ b/lib/python/Plugins/SystemPlugins/Blindscan/plugin.py @@ -20,7 +20,7 @@ from enigma import eTimer, eDVBFrontendParametersSatellite, eComponentScan, eDVB class Blindscan(ConfigListScreen, Screen): skin=""" - + @@ -29,8 +29,8 @@ class Blindscan(ConfigListScreen, Screen): - - + + """ def __init__(self, session): @@ -43,6 +43,8 @@ class Blindscan(ConfigListScreen, Screen): for slot in nimmanager.nim_slots: if slot.isCompatible("DVB-S"): self.satList.append(nimmanager.getSatListForNim(slot.slot)) + else: + self.satList.append(None) # make config self.createConfig() @@ -61,7 +63,7 @@ class Blindscan(ConfigListScreen, Screen): "cancel": self.keyCancel, }, -2) self["key_red"] = StaticText(_("Exit")) - self["key_green"] = StaticText("Start") + self["key_green"] = StaticText("Scan") self["key_blue"] = StaticText("Scan All") self["introduction"] = Label(_("Press Green/OK to start the scan")) self.createSetup() @@ -79,6 +81,17 @@ class Blindscan(ConfigListScreen, Screen): self["key_blue"] = StaticText(" ") self["introduction"] = Label(_("Please setup your tuner configuration.")) + self.i2c_mapping_table = None + self.makeNimSocket() + + def makeNimSocket(self): + self.i2c_mapping_table = {0:2, 1:3, 2:1, 3:0} + + def getNimSocket(self, slot_number): + if slot_number < 0 or slot_number > 3: + return -1 + return self.i2c_mapping_table[slot_number] + def keyNone(self): None def callbackNone(self, *retval): @@ -150,6 +163,8 @@ class Blindscan(ConfigListScreen, Screen): self.blindscan_stop_frequency = ConfigInteger(default = 2150*1000000) self.blindscan_start_symbol = ConfigInteger(default = 2*1000000) self.blindscan_stop_symbol = ConfigInteger(default = 45*1000000) + self.scan_clearallservices = ConfigYesNo(default = False) + self.scan_onlyfree = ConfigYesNo(default = False) # collect all nims which are *not* set to "nothing" nim_list = [] @@ -162,8 +177,8 @@ class Blindscan(ConfigListScreen, Screen): root_id = nimmanager.sec.getRoot(n.slot_id, int(n.config.connectedTo.value)) if n.type == nimmanager.nim_slots[root_id].type: # check if connected from a DVB-S to DVB-S2 Nim or vice versa continue - nim_list.append((str(n.slot), n.friendly_full_description)) - + if n.isCompatible("DVB-S"): + nim_list.append((str(n.slot), n.friendly_full_description)) self.scan_nims = ConfigSelection(choices = nim_list) # sat @@ -185,6 +200,17 @@ class Blindscan(ConfigListScreen, Screen): self.scan_satselection.append(getConfigSatlist(defaultSat["orbpos"], self.satList[slot.slot])) return True + def getSelectedSatIndex(self, v): + index = 0 + none_cnt = 0 + for n in self.satList: + if self.satList[index] == None: + none_cnt = none_cnt + 1 + if index == int(v): + return (index-none_cnt) + index = index + 1 + return -1 + def createSetup(self): self.list = [] self.multiscanlist = [] @@ -203,13 +229,15 @@ class Blindscan(ConfigListScreen, Screen): self.scan_networkScan.value = False if nim.isCompatible("DVB-S") : - self.list.append(getConfigListEntry(_('Satellite'), self.scan_satselection[index_to_scan])) + self.list.append(getConfigListEntry(_('Satellite'), self.scan_satselection[self.getSelectedSatIndex(index_to_scan)])) self.list.append(getConfigListEntry(_('Scan start frequency'), self.blindscan_start_frequency)) self.list.append(getConfigListEntry(_('Scan stop frequency'), self.blindscan_stop_frequency)) self.list.append(getConfigListEntry(_("Polarity"), self.scan_sat.polarization)) self.list.append(getConfigListEntry(_("Scan band"), self.blindscan_hi)) self.list.append(getConfigListEntry(_('Scan start symbolrate'), self.blindscan_start_symbol)) self.list.append(getConfigListEntry(_('Scan stop symbolrate'), self.blindscan_stop_symbol)) + self.list.append(getConfigListEntry(_("Clear before scan"), self.scan_clearallservices)) + self.list.append(getConfigListEntry(_("Only Free scan"), self.scan_onlyfree)) self["config"].list = self.list self["config"].l.setList(self.list) @@ -271,8 +299,9 @@ class Blindscan(ConfigListScreen, Screen): self.tmp_tplist=[] tmp_pol = [] tmp_band = [] - tmp_list=[self.satList[0][self.scan_satselection[0].index]] - + idx_selected_sat = int(self.getSelectedSatIndex(self.scan_nims.value)) + tmp_list=[self.satList[int(self.scan_nims.value)][self.scan_satselection[idx_selected_sat].index]] + if self.blindscan_hi.value == "hi_low" : tmp_band=["low","high"] else: @@ -288,16 +317,16 @@ class Blindscan(ConfigListScreen, Screen): def keyGoAll(self): if self.checkSettings() == False: return - self.tmp_tplist=[] tmp_list=[] tmp_band=["low","high"] tmp_pol=["horizontal","vertical"] + for slot in nimmanager.nim_slots: - if slot.isCompatible("DVB-S"): - for s in self.satList[slot.slot]: + device_name = "/dev/dvb/adapter0/frontend%d" % (slot.slot) + if slot.isCompatible("DVB-S") and int(self.scan_nims.value) == slot.slot: + for s in self.satList[slot.slot]: tmp_list.append(s) - self.doRun(tmp_list, tmp_pol, tmp_band) def doRun(self, tmp_list, tmp_pol, tmp_band): @@ -375,7 +404,11 @@ class Blindscan(ConfigListScreen, Screen): 0) self.tuner.tune(returnvalue) - cmd = "vuplus_blindscan %d %d %d %d %d %d %d" % (self.blindscan_start_frequency.value/1000000, self.blindscan_stop_frequency.value/1000000, self.blindscan_start_symbol.value/1000000, self.blindscan_stop_symbol.value/1000000, tab_pol[pol], tab_hilow[band], self.feid) + if self.getNimSocket(self.feid) < 0: + print "can't find i2c number!!" + return + + cmd = "vuplus_blindscan %d %d %d %d %d %d %d %d" % (self.blindscan_start_frequency.value/1000000, self.blindscan_stop_frequency.value/1000000, self.blindscan_start_symbol.value/1000000, self.blindscan_stop_symbol.value/1000000, tab_pol[pol], tab_hilow[band], self.feid, self.getNimSocket(self.feid)) print "prepared command : [%s]" % (cmd) self.blindscan_container = eConsoleAppContainer() self.blindscan_container.appClosed.append(self.blindscanContainerClose) @@ -434,6 +467,8 @@ class Blindscan(ConfigListScreen, Screen): self.blindscan_session.close(True) def blindscanContainerAvail(self, str): + print str + #if str.startswith("OK"): self.full_data = self.full_data + str def blindscanSessionNone(self, *val): @@ -469,7 +504,15 @@ class Blindscan(ConfigListScreen, Screen): def startScan(self, tlist, feid, networkid = 0): self.scan_session = None - self.session.open(ServiceScan, [{"transponders": tlist, "feid": feid, "flags": 0, "networkid": networkid}]) + + flags = 0 + if self.scan_clearallservices.value: + flags |= eComponentScan.scanRemoveServices + else: + flags |= eComponentScan.scanDontRemoveUnscanned + if self.scan_onlyfree.value: + flags |= eComponentScan.scanOnlyFree + self.session.open(ServiceScan, [{"transponders": tlist, "feid": feid, "flags": flags, "networkid": networkid}]) def main(session, **kwargs): session.open(Blindscan) diff --git a/lib/python/Plugins/SystemPlugins/FirmwareUpgrade/Makefile.am b/lib/python/Plugins/SystemPlugins/FirmwareUpgrade/Makefile.am new file mode 100644 index 0000000..e47942c --- /dev/null +++ b/lib/python/Plugins/SystemPlugins/FirmwareUpgrade/Makefile.am @@ -0,0 +1,7 @@ +installdir = $(pkglibdir)/python/Plugins/SystemPlugins/FirmwareUpgrade + +SUBDIRS = meta + +install_PYTHON = __init__.py \ + plugin.py + diff --git a/lib/python/Plugins/SystemPlugins/FirmwareUpgrade/__init__.py b/lib/python/Plugins/SystemPlugins/FirmwareUpgrade/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lib/python/Plugins/SystemPlugins/FirmwareUpgrade/_fu.so b/lib/python/Plugins/SystemPlugins/FirmwareUpgrade/_fu.so new file mode 100644 index 0000000000000000000000000000000000000000..32b39c0b4f5d9820216e32f7c130ee9b0aa670b3 GIT binary patch literal 51460 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 G`2PVXpb@PA literal 0 HcmV?d00001 diff --git a/lib/python/Plugins/SystemPlugins/FirmwareUpgrade/fu.py b/lib/python/Plugins/SystemPlugins/FirmwareUpgrade/fu.py new file mode 100644 index 0000000..1cc0778 --- /dev/null +++ b/lib/python/Plugins/SystemPlugins/FirmwareUpgrade/fu.py @@ -0,0 +1,92 @@ +# This file was automatically generated by SWIG (http://www.swig.org). +# Version 1.3.39 +# +# Do not make changes to this file unless you know what you are doing--modify +# the SWIG interface file instead. +# This file is compatible with both classic and new-style classes. + +from sys import version_info +if version_info >= (2,6,0): + def swig_import_helper(): + from os.path import dirname + import imp + fp = None + try: + fp, pathname, description = imp.find_module('_fu', [dirname(__file__)]) + except ImportError: + import _fu + return _fu + if fp is not None: + try: + _mod = imp.load_module('_fu', fp, pathname, description) + finally: + fp.close() + return _mod + _fu = swig_import_helper() + del swig_import_helper +else: + import _fu +del version_info +try: + _swig_property = property +except NameError: + pass # Python < 2.2 doesn't have 'property'. +def _swig_setattr_nondynamic(self,class_type,name,value,static=1): + if (name == "thisown"): return self.this.own(value) + if (name == "this"): + if type(value).__name__ == 'SwigPyObject': + self.__dict__[name] = value + return + method = class_type.__swig_setmethods__.get(name,None) + if method: return method(self,value) + if (not static) or hasattr(self,name): + self.__dict__[name] = value + else: + raise AttributeError("You cannot add attributes to %s" % self) + +def _swig_setattr(self,class_type,name,value): + return _swig_setattr_nondynamic(self,class_type,name,value,0) + +def _swig_getattr(self,class_type,name): + if (name == "thisown"): return self.this.own() + method = class_type.__swig_getmethods__.get(name,None) + if method: return method(self) + raise AttributeError(name) + +def _swig_repr(self): + try: strthis = "proxy of " + self.this.__repr__() + except: strthis = "" + return "<%s.%s; %s >" % (self.__class__.__module__, self.__class__.__name__, strthis,) + +try: + _object = object + _newclass = 1 +except AttributeError: + class _object : pass + _newclass = 0 + + +class FU(_object): + __swig_setmethods__ = {} + __setattr__ = lambda self, name, value: _swig_setattr(self, FU, name, value) + __swig_getmethods__ = {} + __getattr__ = lambda self, name: _swig_getattr(self, FU, name) + __repr__ = _swig_repr + def __init__(self): + this = _fu.new_FU() + try: self.this.append(this) + except: self.this = this + def getStatus(self): return _fu.FU_getStatus(self) + def getInterval(self): return _fu.FU_getInterval(self) + def checkError(self): return _fu.FU_checkError(self) + def getErrorMessage(self, *args): return _fu.FU_getErrorMessage(self, *args) + def startUpgrade(self, *args): return _fu.FU_startUpgrade(self, *args) + def getDataPath(self): return _fu.FU_getDataPath(self) + def getDevicePath(self): return _fu.FU_getDevicePath(self) + __swig_destroy__ = _fu.delete_FU + __del__ = lambda self : None; +FU_swigregister = _fu.FU_swigregister +FU_swigregister(FU) + + + diff --git a/lib/python/Plugins/SystemPlugins/FirmwareUpgrade/meta/Makefile.am b/lib/python/Plugins/SystemPlugins/FirmwareUpgrade/meta/Makefile.am new file mode 100644 index 0000000..42b7bc7 --- /dev/null +++ b/lib/python/Plugins/SystemPlugins/FirmwareUpgrade/meta/Makefile.am @@ -0,0 +1,3 @@ +installdir = $(datadir)/meta + +dist_install_DATA = plugin_firmwareupgrade.xml diff --git a/lib/python/Plugins/SystemPlugins/FirmwareUpgrade/meta/plugin_firmwareupgrade.xml b/lib/python/Plugins/SystemPlugins/FirmwareUpgrade/meta/plugin_firmwareupgrade.xml new file mode 100644 index 0000000..46d7b4f --- /dev/null +++ b/lib/python/Plugins/SystemPlugins/FirmwareUpgrade/meta/plugin_firmwareupgrade.xml @@ -0,0 +1,16 @@ + + + + + + oskwon + FirmwareUpgrade + enigma2-plugin-systemplugins-firmwareupgrade + Upgrade your system Firmware + Upgrade your internal system Firmware. + + + + + + diff --git a/lib/python/Plugins/SystemPlugins/FirmwareUpgrade/plugin.py b/lib/python/Plugins/SystemPlugins/FirmwareUpgrade/plugin.py new file mode 100644 index 0000000..ff593de --- /dev/null +++ b/lib/python/Plugins/SystemPlugins/FirmwareUpgrade/plugin.py @@ -0,0 +1,555 @@ +import os, urllib +from urllib import urlretrieve + +from Plugins.Plugin import PluginDescriptor + +from Components.config import config, getConfigListEntry, ConfigSubsection, ConfigText, ConfigSelection, ConfigYesNo,ConfigText +from Components.ConfigList import ConfigListScreen +from Components.ActionMap import ActionMap +from Components.Sources.StaticText import StaticText +from Components.Pixmap import Pixmap +from Components.Label import Label + +from Components.FileList import FileList +from Components.Slider import Slider + +from Screens.Screen import Screen +from Screens.MessageBox import MessageBox + +from enigma import ePoint, eConsoleAppContainer, eTimer +from Tools.Directories import resolveFilename, SCOPE_PLUGINS + +fwlist = None +fwdata = None +if os.path.exists("/proc/stb/info/vumodel"): + vumodel = open("/proc/stb/info/vumodel") + info = vumodel.read().strip() + vumodel.close() + + if info == "ultimo": + fwlist= [ + ("fp", _("Front Processor")) + ,("fpga", _("FPGA")) + ] + fwdata= { + "fp" : ["http://archive.vuplus.com/download/fp", "fp.files", "/dev/bcm_mu;"] + ,"fpga" : ["http://archive.vuplus.com/download/fpga", "fpga.files", "/dev/fpga_dp;/dev/misc/dp;"] + } + elif info == "uno": + fwlist= [ + ("fpga", _("FPGA")) + ] + fwdata= { + "fpga" : ["http://archive.vuplus.com/download/fpga", "fpga.file", "/dev/fpga_dp;/dev/misc/dp;"] + } + +class UpgradeStatus(Screen): + skin = """ + + + + + + + """ + + def __init__(self, session, parent, firmware, datafile, device): + Screen.__init__(self,session) + self.session = session + + self["actions"] = ActionMap(["OkCancelActions"], + { + "ok": self.keyExit, + }, -1) + + self.firmware = firmware + self.datafile = datafile + #print "[FirmwareUpgrade] - [%s][%s][%s]" % (self.datafile, firmware, device) + + self["name"] = Label(_(" ")) + self["info"] = StaticText(_("Can't cancel during upgrade!!")) + + self["status"] = Label(_("Status : 0%")) + + self.slider = Slider(0, 100) + self["slider"] = self.slider + + self.callback = None + + self.setTitle(firmware.upper() + " Upgrade Status") + + import fu + self.FU = fu.FU() + + self.old_status = 0 + self.status_exit = None + self.check_status = eTimer() + self.check_status.callback.append(self.cbCheckStatus) + self.check_status.start(self.FU.getInterval()) + + self.exitTimerCallCount = 0; + self.upgradeLock = True + self.FU.startUpgrade(self.datafile, device, firmware) + + def cbCheckStatus(self): + errmsg = "" + errno = self.FU.checkError() + if errno: + self.check_status.stop() + errmsg = self.FU.getErrorMessage(errno, errmsg) + print "[FirmwareUpgrade] - ERROR : [%d][%s]" % (errno, errmsg) + self.session.open(MessageBox, _(errmsg), MessageBox.TYPE_INFO, timeout = 10) + self.cbConfirmExit(False) + return + status = self.FU.getStatus() + if self.old_status > status: + self.session.open(MessageBox, _("Fail to upgrade!! Retry!!"), MessageBox.TYPE_INFO, timeout = 10) + self.slider.setValue(status) + self["status"].setText(_("%d / 100" % (status))) + if status == 100: + self.check_status.stop() + self["status"].setText(_("Success. Press OK to exit.")) + self.status_exit = eTimer() + self.status_exit.callback.append(self.cbTimerExit) + self.status_exit.start(1000) + self.upgradeLock = False + self.old_status = status + + def setCallback(self, cb): + self.callback = cb + + def cbTimerExit(self): + if self.exitTimerCallCount < 10: # exit after 10 sec. + self.exitTimerCallCount = self.exitTimerCallCount + 1 + self.setTitle("%s Upgrade Status (%d)" % (self.firmware.upper(), 10-self.exitTimerCallCount)) + return + if self.status_exit is not None: + self.status_exit.stop() + self.keyExit() + + def cbConfirmExit(self, ret): + if ret: + os.system("rm -f %s %s.md5" % (self.datafile, self.datafile)) + self.close() + + def keyExit(self): + if self.upgradeLock: + return + if self.callback is not None: + self.callback("Reboot now for a successful upgrade.", True) + self.session.openWithCallback(self.cbConfirmExit, MessageBox, _("Do you want to remove binary data?"), MessageBox.TYPE_YESNO, timeout = 10, default = False) + +class Filebrowser(Screen): + skin = """ + + + + + + + + """ + + def __init__(self, session, parent, firmware): + Screen.__init__(self, session) + self.session = session + + self["key_blue"] = StaticText(_("Download")) + + self["status"] = StaticText(_(" ")) + self["file_list"] = FileList("/", matchingPattern = "^.*") + + self["actions"] = ActionMap(["OkCancelActions", "ShortcutActions", "WizardActions", "ColorActions", ], + { + "ok": self.onClickOk, + "cancel": self.onClickCancel, + "blue": self.onClickBlue, + "up": self.onClickUp, + "down": self.onClickDown, + "left": self.onClickLeft, + "right": self.onClickRight, + }, -1) + + self.resetGUI() + self.firmware = firmware + + self.callback = None + self.timer_downloading = None + + self.downloadLock = False + self.setTitle(firmware.upper() + " File Browser") + + def resetGUI(self): + self["status"].setText("Select to press OK, Exit to press Cancel.") + + def setCallback(self, func): + self.callback = func + + def onClickOk(self): + if self.downloadLock: + return + + if self["file_list"].canDescent() : # isDir + self["file_list"].descent() + return + + # verify data + self.gbin = self["file_list"].getCurrentDirectory() + self["file_list"].getFilename() + if not os.path.exists(self.gbin): + self.session.open(MessageBox, _("Can't found binary file."), MessageBox.TYPE_INFO, timeout = 10) + return + if not os.path.exists(self.gbin+".md5"): + self.session.open(MessageBox, _("Can't found MD5 file."), MessageBox.TYPE_INFO, timeout = 10) + return + try: + def checkExt(ext): + name_ext = os.path.splitext(self["file_list"].getFilename()) + return len(name_ext)==2 and ext.startswith(name_ext[1]) + self.check_ext = False + if (self.firmware == "fp" and checkExt(".bin")) or (self.firmware == "fpga" and checkExt(".dat")): + self.check_ext = True + if self.check_ext == False: + self.session.open(MessageBox, _("You chose the incorrect file."), MessageBox.TYPE_INFO) + return + except: + self.session.open(MessageBox, _("You chose the incorrect file."), MessageBox.TYPE_INFO) + return + + if os.path.exists("/usr/bin/md5sum") == False: + self.session.open(MessageBox, _("Can't find /usr/bin/md5sum"), MessageBox.TYPE_INFO, timeout = 10) + return + md5sum_A = os.popen("md5sum %s | awk \'{print $1}\'"%(self.gbin)).readline().strip() + md5sum_B = os.popen("cat %s.md5 | awk \'{print $1}\'"%(self.gbin)).readline().strip() + #print "[FirmwareUpgrade] - Verify : file[%s], md5[%s]"%(md5sum_A,md5sum_B) + + if md5sum_A != md5sum_B: + self.session.open(MessageBox, _("Fail to verify data file. \nfile[%s]\nmd5[%s]"%(md5sum_A,md5sum_B)), MessageBox.TYPE_INFO, timeout = 10) + return + + if self.callback is not None: + self.callback(_(self.gbin)) + self.close() + + def onClickCancel(self): + self.close() + + # uri : source file url(string) + # tf : target file name(string) + # bd : target base directory(string) + # cbfunc(string) : callback function(function) + def doDownload(self, uri, tf, bd='/tmp', cbfunc=None): + tar = bd + "/" + tf + #print "[FirmwareUpgrade] - Download Info : [%s][%s]" % (uri, tar) + def doHook(blockNumber, blockSize, totalSize) : + if blockNumber*blockSize > totalSize and cbfunc is not None: + cbfunc(tar) + opener = urllib.URLopener() + try: + opener.open(uri) + except: + self.session.open(MessageBox, _("File not found in this URL:\n%s"%(uri)), MessageBox.TYPE_INFO, timeout = 10) + del opener + return False + try : + f, h = urlretrieve(uri, tar, doHook) + except IOError, msg: + self.session.open(MessageBox, _(str(msg)), MessageBox.TYPE_INFO, timeout = 10) + del opener + return False + del opener + return True + + def runDownloading(self) : + self.timer_downloading.stop() + machine = str(open("/proc/stb/info/vumodel").read().strip()) + + def cbDownloadDone(tar): + try: + if os.path.splitext(tar)[1] != ".files": + self["status"].setText("Downloaded : %s\nSelect to press OK, Exit to press Cancel."%(tar)) + except: + pass + # target + global fwdata + root_uri = fwdata[self.firmware][0] + root_file = fwdata[self.firmware][1] + if not self.doDownload("%s/%s"%(root_uri, root_file), root_file, cbfunc=cbDownloadDone): + self.resetGUI() + self.downloadLock = False + return + + target_path = "" + for l in file("/tmp/"+root_file).readlines(): + if l.startswith(machine): + try: + target_path = l.split("=")[1].strip() + except: + target_path = "" + pass + if target_path == "": + self.session.open(MessageBox, _("Firmware does not exist."), MessageBox.TYPE_INFO) + self.resetGUI() + self.downloadLock = False + return + + self.guri = "%s/vu%s/%s"%(root_uri, machine, target_path) + self.gbin = os.path.basename(target_path) + #print "[FirmwareUpgrade] - uri[%s], data[%s], data_path[%s]" % (self.gbin, self.guri, target_path) + os.system("rm -f /tmp/" + root_file) + + # md5 + if not self.doDownload(self.guri+".md5", self.gbin+".md5", cbfunc=cbDownloadDone): + self.resetGUI() + self.downloadLock = False + return + # data + if not self.doDownload(self.guri, self.gbin, cbfunc=cbDownloadDone): + self.resetGUI() + self.downloadLock = False + return + + t = '' + self["file_list"].changeDir("/tmp/") + self["file_list"].moveToIndex(0) + while cmp(self["file_list"].getFilename(), self.gbin) != 0 : + self["file_list"].down() + if cmp(t, self["file_list"].getFilename()) == 0: + break + t = self["file_list"].getFilename() + + del self.timer_downloading + self.timer_downloading = None + self.downloadLock = False + + def onClickBlue(self): + if self.downloadLock: + return + self.downloadLock = True + if not os.path.exists("/proc/stb/info/vumodel"): + self.session.open(MessageBox, _("Can't found model name."), MessageBox.TYPE_INFO, timeout = 10) + self.downloadLock = False + return + self["status"].setText("Please wait during download.") + self.timer_downloading = eTimer() + self.timer_downloading.callback.append(self.runDownloading) + self.timer_downloading.start(1000) + + def onClickUp(self): + if self.downloadLock: + return + self.resetGUI() + self["file_list"].up() + + def onClickDown(self): + if self.downloadLock: + return + self.resetGUI() + self["file_list"].down() + + def onClickLeft(self): + if self.downloadLock: + return + self.resetGUI() + self["file_list"].pageUp() + + def onClickRight(self): + if self.downloadLock: + return + self.resetGUI() + self["file_list"].pageDown() + + def keyNone(self): + None + +class FirmwareUpgrade(Screen, ConfigListScreen): + skin = """ + + + + + + + + + + + """ + + def __init__(self, session): + Screen.__init__(self, session) + self.session = session + + self["shortcuts"] = ActionMap(["ShortcutActions", "SetupActions" ], + { + "ok": self.keyGreen, + "cancel": self.keyRed, + "red": self.keyRed, + "green": self.keyGreen, + "blue": self.keyBlue, + }, -2) + + self.list = [] + self.updateFilePath = "" + + self.rebootLock = False + self.rebootMessage = "" + self.cbRebootCallCount = 0; + + ConfigListScreen.__init__(self, self.list, session=self.session) + self["key_red"] = StaticText(_("Close")) + + self.logmode = None + self.old_blue_clicked = 0 + self.fileopenmode = False + self.upgrade_auto_run_timer = eTimer() + self.upgrade_auto_run_timer.callback.append(self.keyGreen) + + global fwlist + if fwlist is None: + self["key_green"] = StaticText(_(" ")) + self["status"] = StaticText(_("This plugin is supported only the Ultimo/Uno.")) + else: + self["key_green"] = StaticText(_("Upgrade")) + self["status"] = StaticText(_(" ")) + self.setupUI() + + def setupUI(self): + global fwlist + self.list = [] + self._item_firmware = ConfigSelection(default=fwlist[0][0], choices=fwlist) + self._entry_firmware = getConfigListEntry(_("Firmware"), self._item_firmware) + self.list.append(self._entry_firmware) + self["config"].list = self.list + self["config"].l.setList(self.list) + self.setupStatus() + + def setupStatus(self,message=None,reboot=False): + self.updateFilePath = "" + if message is not None: + self.rebootLock = reboot + self["status"].setText(message) + if reboot: + self.rebootMessage = message + self.reboot_timer = eTimer() + self.reboot_timer.callback.append(self.cbReboot) + self.reboot_timer.start(500) + return + if not self.rebootLock: + self["status"].setText("Press the Green/OK button") + + def cbReboot(self): + if self.cbRebootCallCount < 6: + self.cbRebootCallCount = self.cbRebootCallCount + 1 + self["status"].setText("%s (%d)"%(self.rebootMessage, 6-self.cbRebootCallCount)) + return + from Screens.Standby import TryQuitMainloop + self.session.open(TryQuitMainloop, 2) + + # filebrowser window callback function + def cbSetStatus(self, data=None): + if data is not None: + self["status"].setText("Press the Green/OK button, if you want to upgrade to this file:\n%s\n" % (data)) + self.updateFilePath = data + if self.fileopenmode == False: + self.upgrade_auto_run_timer.start(1000) + + # upgrade window callback function + def cbFinishedUpgrade(self,message=None,reboot=False): + self.setupStatus(message=message,reboot=reboot) + + def cbRunUpgrade(self, ret): + if ret == False: + return + + if self.updateFilePath == "": + self.session.open(MessageBox, _("No selected binary data!!"), MessageBox.TYPE_INFO, timeout = 10) + return + device = None + for d in fwdata[self._item_firmware.value][2].split(';'): + if os.path.exists(d): + device = d + if device is None: + self.session.open(MessageBox, _("Can't found device file!!"), MessageBox.TYPE_INFO, timeout = 10) + return + fbs = self.session.open(UpgradeStatus, self, self._item_firmware.value, self.updateFilePath, device) + fbs.setCallback(self.cbFinishedUpgrade) + + def doFileOpen(self): + fbs = self.session.open(Filebrowser, self, self._item_firmware.value) + fbs.setCallback(self.cbSetStatus) + + def keyLeft(self): + if self.rebootLock: + return + global fwlist + if fwlist is None: + return + ConfigListScreen.keyLeft(self) + self.setupStatus() + + def keyRight(self): + global fwlist + if fwlist is None: + return + ConfigListScreen.keyRight(self) + self.setupStatus() + + def keyGreen(self): + self.upgrade_auto_run_timer.stop() + if self.rebootLock: + return + global fwlist + if fwlist is None: + return + if self.updateFilePath == "": + #self.session.open(MessageBox, _("No selected binary data!!"), MessageBox.TYPE_INFO) + self.doFileOpen() + return + msg = "You should not be stop during the upgrade.\nDo you want to upgrade?" + self.session.openWithCallback(self.cbRunUpgrade, MessageBox, _(msg), MessageBox.TYPE_YESNO, timeout = 15, default = True) + self.fileopenmode = False + + def keyYellow(self): + if self.rebootLock: + return + global fwlist + if fwlist is None: + return + self.fileopenmode = True + self.doFileOpen() + + def keyRed(self): + if self.rebootLock: + return + self.close() + + def cbLogMode(self): + if self.old_blue_clicked: + return + self.logmode.stop() + if os.path.exists("/tmp/onlogmode"): + return + os.system("touch /tmp/onlogmode") + + def keyBlue(self): + if self.rebootLock: + return + if self.logmode is not None and self.old_blue_clicked == 0: + return + if self.old_blue_clicked: + self.old_blue_clicked = 0 + return + self.old_blue_clicked = 1 + self.logmode = eTimer() + self.logmode.callback.append(self.cbLogMode) + self.logmode.start(1000) + + def keyNone(self): + None + +def main(session, **kwargs): + session.open(FirmwareUpgrade) + +def Plugins(**kwargs): + return PluginDescriptor(name=_("Firmware Upgrade"), description="Upgrade Firmware..", where = PluginDescriptor.WHERE_PLUGINMENU, fnc=main) + diff --git a/lib/python/Plugins/SystemPlugins/Makefile.am b/lib/python/Plugins/SystemPlugins/Makefile.am index a5c4b50..802e6cc 100755 --- a/lib/python/Plugins/SystemPlugins/Makefile.am +++ b/lib/python/Plugins/SystemPlugins/Makefile.am @@ -5,7 +5,7 @@ SUBDIRS = SoftwareManager FrontprocessorUpgrade PositionerSetup Satfinder \ DefaultServicesScanner NFIFlash DiseqcTester CommonInterfaceAssignment \ CrashlogAutoSubmit CleanupWizard VideoEnhancement WirelessLan NetworkWizard \ TempFanControl FactoryTest Fancontrol FPGAUpgrade WirelessLanSetup ManualFancontrol \ - Blindscan RemoteControlCode UI3DSetup UIPositionSetup HDMICEC LEDBrightnessSetup + Blindscan RemoteControlCode UI3DSetup UIPositionSetup HDMICEC LEDBrightnessSetup FirmwareUpgrade install_PYTHON = \ __init__.py -- 2.7.4