from datetime import datetime from glob import glob from json import loads from locale import format_string from os import listdir, remove, statvfs from os.path import basename, getmtime, isdir, isfile, join from subprocess import PIPE, Popen from urllib.request import urlopen from enigma import eAVControl, eDVBFrontendParametersSatellite, eDVBResourceManager, eGetEnigmaDebugLvl, eRTSPStreamServer, eServiceCenter, eStreamServer, eTimer, getDesktop, getE2Rev, getGStreamerVersionString, iPlayableService, iServiceInformation from ServiceReference import ServiceReference from skin import parameters from Components.About import about from Components.ActionMap import HelpableActionMap from Components.config import config from Components.Console import Console from Components.Harddisk import harddiskmanager from Components.InputDevice import remoteControl from Components.Label import Label from Components.Network import iNetwork from Components.NimManager import nimmanager from Components.Pixmap import Pixmap from Components.ScrollLabel import ScrollLabel from Components.ServiceEventTracker import ServiceEventTracker # from Components.Storage import Harddisk, storageManager from Components.SystemInfo import BoxInfo, getBoxDisplayName, getDemodVersion from Components.Sources.StaticText import StaticText from Screens.MessageBox import MessageBox from Screens.Screen import Screen, ScreenSummary from Screens.Setup import Setup from Tools.Conversions import scaleNumber, formatDate from Tools.Directories import SCOPE_SKINS, fileReadLine, fileReadLines, fileWriteLine, resolveFilename from Tools.Geolocation import geolocation from Tools.LoadPixmap import LoadPixmap from Tools.MultiBoot import MultiBoot from Tools.StbHardware import getFPVersion, getBoxProc, getHWSerial, getBoxRCType, getBoxProcType from Tools.Transponder import ConvertToHumanReadable MODULE_NAME = __name__.split(".")[-1] DISPLAY_BRAND = BoxInfo.getItem("displaybrand") DISPLAY_MODEL = BoxInfo.getItem("displaymodel") MACHINE_BUILD = BoxInfo.getItem("machinebuild") MODEL = BoxInfo.getItem("model") INFO_COLORS = ["N", "H", "S", "P", "V", "M", "F"] INFO_COLOR = { "B": None, "N": 0x00ffffff, # Normal. "H": 0x00ffffff, # Headings. "S": 0x00ffffff, # Subheadings. "P": 0x00cccccc, # Prompts. "V": 0x00cccccc, # Values. "M": 0x00ffff00, # Messages. "F": 0x0000ffff # Features. } LOG_MAX_LINES = 10000 # Maximum number of log lines to be displayed on screen. AUTO_REFRESH_TIME = 5000 # Streaming auto refresh timer (in milliseconds). # This code is all to move into SystemInfo.py when it can handle translated text... # def getBoxProcTypeName(): boxProcTypes = { "00": _("OTT Model"), "10": _("Single Tuner"), "11": _("Twin Tuner"), "12": _("Combo Tuner"), "21": _("Twin Hybrid"), "22": _("Hybrid Tuner") } procType = getBoxProcType() if procType == "unknown": return _("Unknown") return f"{procType} - {boxProcTypes.get(procType, _('Unknown'))}" welcome = [ _("Welcome to %s") % BoxInfo.getItem("displaydistro", "Enigma2") ] BoxInfo.setItem("InformationDistributionWelcome", welcome) # # End of marked code. class InformationBase(Screen): skin = """ """ def __init__(self, session): Screen.__init__(self, session, enableHelp=True) self.skinName = ["Information"] self["information"] = ScrollLabel() self["key_red"] = StaticText(_("Close")) self["key_green"] = StaticText(_("Refresh")) self["actions"] = HelpableActionMap(self, ["CancelSaveActions", "OkActions", "NavigationActions"], { "cancel": (self.keyCancel, _("Close the screen")), "close": (self.closeRecursive, _("Close the screen and exit all menus")), "save": (self.refreshInformation, _("Refresh the screen")), "ok": (self.refreshInformation, _("Refresh the screen")), "top": (self["information"].goTop, _("Move to first line / screen")), "pageUp": (self["information"].goPageUp, _("Move up a screen")), "up": (self["information"].goLineUp, _("Move up a line")), "down": (self["information"].goLineDown, _("Move down a line")), "pageDown": (self["information"].goPageDown, _("Move down a screen")), "bottom": (self["information"].goBottom, _("Move to last line / screen")) }, prio=0, description=_("Common Information Actions")) colors = parameters.get("InformationColors", (0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00cccccc, 0x00cccccc, 0x00ffff00, 0x0000ffff)) if len(colors) == len(INFO_COLORS): for index in range(len(colors)): INFO_COLOR[INFO_COLORS[index]] = colors[index] else: print(f"[Information] Warning: {len(colors)} colors are defined in the skin when {len(INFO_COLORS)} were expected!") self["information"].setText(_("Loading information, please wait...")) self.extraSpacing = config.usage.informationExtraSpacing.value self.onInformationUpdated = [self.displayInformation] self.onLayoutFinish.append(self.displayInformation) self.console = Console() self.informationTimer = eTimer() self.informationTimer.callback.append(self.fetchInformation) self.informationTimer.start(25) def keyCancel(self): self.console.killAll() self.close() def closeRecursive(self): self.console.killAll() self.close(True) def informationWindowClosed(self, *retVal): if retVal and retVal[0]: self.close(True) def fetchInformation(self): self.informationTimer.stop() for callback in self.onInformationUpdated: if callable(callback): callback() def refreshInformation(self): self.informationTimer.start(25) for callback in self.onInformationUpdated: if callable(callback): callback() def displayInformation(self): pass def getSummaryInformation(self): pass def createSummary(self): return InformationSummary def formatLine(style, left, right=None): styleLen = len(style) leftStartColor = "" if styleLen > 0 and style[0] == "B" else r"\c%08x" % (INFO_COLOR.get(style[0], "P") if styleLen > 0 else INFO_COLOR["P"]) leftEndColor = "" if leftStartColor == "" else r"\c%08x" % INFO_COLOR["N"] leftIndent = " " * int(style[1]) if styleLen > 1 and style[1].isdigit() else "" rightStartColor = "" if styleLen > 2 and style[2] == "B" else r"\c%08x" % (INFO_COLOR.get(style[2], "V") if styleLen > 2 else INFO_COLOR["V"]) rightEndColor = "" if rightStartColor == "" else r"\c%08x" % INFO_COLOR["N"] rightIndent = " " * int(style[3]) if styleLen > 3 and style[3].isdigit() else "" if right is None: colon = "" if styleLen > 0 and style[0] in ("M", "P", "V") else ":" return f"{leftIndent}{leftStartColor}{left}{colon}{leftEndColor}" return f"{leftIndent}{leftStartColor}{left}:{leftEndColor}|{rightIndent}{rightStartColor}{right}{rightEndColor}" class BenchmarkInformation(InformationBase): # This code can't be used until we find open source test code! def __init__(self, session): InformationBase.__init__(self, session) self.setTitle(_("Benchmark Information")) self.skinName.insert(0, "BenchmarkInformation") self.cpuTypes = [] self.cpuBenchmark = None self.cpuRating = None self.ramBenchmark = None def fetchInformation(self): self.informationTimer.stop() self.cpuTypes = [] lines = [] lines = fileReadLines("/proc/cpuinfo", lines, source=MODULE_NAME) for line in lines: if line.startswith("model name") or line.startswith("Processor"): # HiSilicon use the label "Processor"! self.cpuTypes.append([x.strip() for x in line.split(":")][1]) self.console.ePopen(("/usr/bin/dhry", "/usr/bin/dhry"), self.cpuBenchmarkFinished) # Serialize the tests for better accuracy. # self.console.ePopen(("/usr/bin/streambench", "/usr/bin/streambench"), self.ramBenchmarkFinished) for callback in self.onInformationUpdated: if callable(callback): callback() def cpuBenchmarkFinished(self, result, retVal, extraArgs): for line in result.split("\n"): if line.startswith("Open Vision DMIPS"): self.cpuBenchmark = int([x.strip() for x in line.split(":")][1]) if line.startswith("Open Vision CPU status"): self.cpuRating = [x.strip() for x in line.split(":")][1] # Serialize the tests for better accuracy. self.console.ePopen(("/usr/bin/streambench", "/usr/bin/streambench"), self.ramBenchmarkFinished) for callback in self.onInformationUpdated: if callable(callback): callback() def ramBenchmarkFinished(self, result, retVal, extraArgs): for line in result.split("\n"): if line.startswith("Open Vision copy rate"): self.ramBenchmark = float([x.strip() for x in line.split(":")][1]) for callback in self.onInformationUpdated: if callable(callback): callback() def refreshInformation(self): self.cpuBenchmark = None self.cpuRating = None self.ramBenchmark = None InformationBase.refreshInformation(self) def displayInformation(self): info = [] info.append(formatLine("H", _("Benchmark information for %s %s") % getBoxDisplayName())) info.append("") for index, cpu in enumerate(self.cpuTypes): info.append(formatLine("P1", _("CPU / Core %d type") % index, cpu)) info.append("") info.append(formatLine("P1", _("CPU benchmark"), _("%d DMIPS per core") % (self.cpuBenchmark if self.cpuBenchmark else _("Calculating benchmark...")))) count = len(self.cpuTypes) if count > 1: info.append(formatLine("P1", _("Total CPU benchmark"), _("%d DMIPS with %d cores") % (self.cpuBenchmark * count, count) if self.cpuBenchmark else _("Calculating benchmark..."))) info.append(formatLine("P1", _("CPU rating"), self.cpuRating if self.cpuRating else _("Calculating rating..."))) info.append("") info.append(formatLine("P1", _("RAM benchmark"), f"{self.ramBenchmark:.2f} MB/s copy rate" if self.ramBenchmark else _("Calculating benchmark..."))) self["information"].setText("\n".join(info)) def getSummaryInformation(self): return "Benchmark Information" class BuildInformation(InformationBase): def __init__(self, session): InformationBase.__init__(self, session) self.setTitle(_("Build Information")) self.skinName.insert(0, "BuildInformation") def displayInformation(self): info = [] info.append(formatLine("H", _("Build information for %s %s") % getBoxDisplayName())) info.append("") checksum = BoxInfo.getItem("checksumerror", False) if checksum: info.append(formatLine("M1", _("Error: Checksum is invalid!"))) override = BoxInfo.getItem("overrideactive", False) if override: info.append(formatLine("M1", _("Warning: Overrides are currently active!"))) if checksum or override: info.append("") for item in BoxInfo.getEnigmaInfoList(): info.append(formatLine("P1", item, BoxInfo.getItem(item))) self["information"].setText("\n".join(info)) def getSummaryInformation(self): return "Build Information" class CommitInformation(InformationBase): def __init__(self, session): InformationBase.__init__(self, session) self.setTitle(_("Commit Log Information")) self.baseTitle = _("Commit Log") self.skinName.insert(0, "CommitInformation") self["key_menu"] = StaticText(_("MENU")) self["key_yellow"] = StaticText() self["key_blue"] = StaticText() self["commitActions"] = HelpableActionMap(self, ["MenuActions", "ColorActions", "NavigationActions"], { "menu": (self.showCommitMenu, _("Show selection menu for commit logs")), "yellow": (self.previousCommitLog, _("Show previous commit log")), "blue": (self.nextCommitLog, _("Show next commit log")), "left": (self.previousCommitLog, _("Show previous commit log")), "right": (self.nextCommitLog, _("Show next commit log")) }, prio=0, description=_("Commit Information Actions")) self.commitLogs = BoxInfo.getItem("InformationCommitLogs", [("Unavailable", None)]) self.commitLogIndex = 0 self.commitLogMax = len(self.commitLogs) self.cachedCommitInfo = {} def showCommitMenu(self): choices = [(commitLog[0], index) for index, commitLog in enumerate(self.commitLogs)] self.session.openWithCallback(self.showCommitMenuCallBack, MessageBox, text=_("Select a repository commit log to view:"), list=choices, windowTitle=self.baseTitle) def showCommitMenuCallBack(self, selectedIndex): if isinstance(selectedIndex, int): self.commitLogIndex = selectedIndex self.displayInformation() self.informationTimer.start(25) def previousCommitLog(self): self.commitLogIndex = (self.commitLogIndex - 1) % self.commitLogMax self.displayInformation() self.informationTimer.start(25) def nextCommitLog(self): self.commitLogIndex = (self.commitLogIndex + 1) % self.commitLogMax self.displayInformation() self.informationTimer.start(25) def fetchInformation(self): # Should we limit the number of fetches per minute? self.informationTimer.stop() name = self.commitLogs[self.commitLogIndex][0] url = self.commitLogs[self.commitLogIndex][1] if url is None: info = [_("There are no repositories defined so commit logs are unavailable!")] else: try: log = [] with urlopen(url, timeout=10) as fd: log = loads(fd.read()) info = [] for data in log: date = datetime.strptime(data["commit"]["committer"]["date"], "%Y-%m-%dT%H:%M:%SZ").strftime(f"{config.usage.date.daylong.value} {config.usage.time.long.value}") author = data["commit"]["author"]["name"] # committer = data["commit"]["committer"]["name"] message = [x.rstrip() for x in data["commit"]["message"].split("\n")] if info: info.append("") # info.append(_("Date: %s Author: %s Commit by: %s") % (date, author, committer)) info.append(_("Date: %s Author: %s") % (date, author)) info.extend(message) if not info: info = [_("The '%s' commit log contains no information.") % name] except Exception as err: info = str(err) self.cachedCommitInfo[name] = info for callback in self.onInformationUpdated: if callable(callback): callback() def refreshInformation(self): # Should we limit the number of fetches per minute? self.cachedCommitInfo = {} InformationBase.refreshInformation(self) def displayInformation(self): name = self.commitLogs[self.commitLogIndex][0] self.setTitle(f"{self.baseTitle}: {name}") self["key_yellow"].setText(self.commitLogs[(self.commitLogIndex - 1) % self.commitLogMax][0]) self["key_blue"].setText(self.commitLogs[(self.commitLogIndex + 1) % self.commitLogMax][0]) if name in self.cachedCommitInfo: info = self.cachedCommitInfo[name] if isinstance(info, str): err = info info = [] info.append(_("Error '%s' encountered retrieving the '%s' commit log!") % (err, name)) info.append("") info.append(_("The '%s' commit log can't be retrieved, please try again later.") % name) info.append("") info.append(_("(Access to the '%s' commit log requires an Internet connection.)") % name) else: info = [_("Retrieving '%s' commit log, please wait...") % name] self["information"].setText("\n".join(info)) def getSummaryInformation(self): return "Commit Log Information" class DebugInformation(InformationBase): def __init__(self, session): InformationBase.__init__(self, session) self.setTitle(_("Debug Log Information")) self.baseTitle = _("Log") self.skinName.insert(0, "DebugInformation") self["key_menu"] = StaticText() self["key_info"] = StaticText(_("INFO")) self["key_yellow"] = StaticText() self["key_blue"] = StaticText() self["debugActions"] = HelpableActionMap(self, ["MenuActions", "InfoActions", "ColorActions", "NavigationActions"], { "menu": (self.showLogMenu, _("Show selection menu for debug log files")), "info": (self.showLogSettings, _("Show the Logs Settings screen")), "yellow": (self.deleteLog, _("Delete the currently displayed log file")), "blue": (self.deleteAllLogs, _("Delete all log files")), "left": (self.previousDebugLog, _("Show previous debug log file")), "right": (self.nextDebugLog, _("Show next debug log file")) }, prio=0, description=_("Debug Log Information Actions")) self["debugActions"].setEnabled(False) self.debugLogs = [] self.debugLogIndex = 0 self.debugLogMax = 0 self.cachedDebugInfo = {} def showLogMenu(self): choices = [(_("Log file: '%s' (%s)") % (debugLog[0], debugLog[1]), index) for index, debugLog in enumerate(self.debugLogs)] self.session.openWithCallback(self.showLogMenuCallBack, MessageBox, text=_("Select a debug log file to view:"), list=choices, default=self.debugLogIndex, windowTitle=self.baseTitle) def showLogMenuCallBack(self, selectedIndex): if isinstance(selectedIndex, int): self.debugLogIndex = selectedIndex self.displayInformation() self.informationTimer.start(25) def showLogSettings(self): self.setTitle(_("Debug Log Information")) self.session.openWithCallback(self.showLogSettingsCallback, Setup, "Logs") def showLogSettingsCallback(self, *retVal): if retVal and retVal[0]: self.close(True) def deleteLog(self): name, sequence, path = self.debugLogs[self.debugLogIndex] self.session.openWithCallback(self.deleteLogCallback, MessageBox, "%s\n\n%s" % (_("Log file: '%s' (%s)") % (name, sequence), _("Do you want to delete this log file?")), default=False) def deleteLogCallback(self, answer): if answer: name, sequence, path = self.debugLogs[self.debugLogIndex] try: remove(path) del self.cachedDebugInfo[path] self.session.open(MessageBox, _("Log file '%s' deleted.") % name, type=MessageBox.TYPE_INFO, timeout=5, close_on_any_key=True, windowTitle=self.baseTitle) self.debugLogs = [] except OSError as err: self.session.open(MessageBox, _("Error %d: Log file '%s' not deleted! (%s)") % (err.errno, name, err.strerror), type=MessageBox.TYPE_ERROR, timeout=5, windowTitle=self.baseTitle) self.informationTimer.start(25) def deleteAllLogs(self): self.session.openWithCallback(self.deleteAllLogsCallback, MessageBox, _("Do you want to delete all the log files?"), default=False) def deleteAllLogsCallback(self, answer): if answer: log = [] type = MessageBox.TYPE_INFO close = True for name, sequence, path in self.debugLogs: try: remove(path) log.append(((_("Log file '%s' deleted.") % name), None)) except OSError as err: type = MessageBox.TYPE_ERROR close = False log.append(((_("Error %d: Log file '%s' not deleted! (%s)") % (err.errno, name, err.strerror)), None)) self.session.open(MessageBox, _("Results of the delete all logs:"), type=type, list=log, timeout=5, close_on_any_key=close, windowTitle=self.baseTitle) self.debugLogs = [] self.cachedDebugInfo = {} self.informationTimer.start(25) def previousDebugLog(self): self.debugLogIndex = (self.debugLogIndex - 1) % self.debugLogMax self.displayInformation() self.informationTimer.start(25) def nextDebugLog(self): self.debugLogIndex = (self.debugLogIndex + 1) % self.debugLogMax self.displayInformation() self.informationTimer.start(25) def fetchInformation(self): self.informationTimer.stop() if not self.debugLogs: self.debugLogs = self.findLogFiles() self.debugLogIndex = 0 self.debugLogMax = len(self.debugLogs) if self.debugLogs: self["key_menu"].setText(_("MENU")) self["key_yellow"].setText(_("Delete log")) self["key_blue"].setText(_("Delete all logs")) self["debugActions"].setEnabled(True) name, sequence, path = self.debugLogs[self.debugLogIndex] if path in self.cachedDebugInfo: info = self.cachedDebugInfo[path] else: try: with open(path) as fd: info = [x.strip() for x in fd.readlines()][-LOG_MAX_LINES:] except OSError as err: info = f"{err.errno},{err.strerror}" self.cachedDebugInfo[path] = info else: self["key_menu"].setText("") self["key_yellow"].setText("") self["key_blue"].setText("") self["debugActions"].setEnabled(False) name = "Unavailable" self.debugLogs = [(name, name, name)] self.cachedDebugInfo[name] = f"0,{_('No log files found so debug logs are unavailable!')}" for callback in self.onInformationUpdated: if callable(callback): callback() def findLogFiles(self): debugLogs = [] installLog = "/home/root/autoinstall.log" if isfile(installLog): debugLogs.append((_("Auto install log"), _("Install 1/1"), installLog)) crashLog = "/tmp/enigma2_crash.log" if isfile(crashLog): debugLogs.append((_("Current crash log"), _("Current 1/1"), crashLog)) paths = [x for x in sorted(glob("/mnt/hdd/*.log"), key=lambda x: isfile(x) and getmtime(x))] if paths: countLogs = len(paths) for index, path in enumerate(reversed(paths)): debugLogs.append((basename(path), _("Log %d/%d") % (index + 1, countLogs), path)) logPath = config.crash.debug_path.value paths = [x for x in sorted(glob(join(logPath, "*-enigma2-crash.log")), key=lambda x: isfile(x) and getmtime(x))] paths += [x for x in sorted(glob(join(logPath, "enigma2_crash*.log")), key=lambda x: isfile(x) and getmtime(x))] if paths: countLogs = len(paths) for index, path in enumerate(reversed(paths)): debugLogs.append((basename(path), _("Crash %d/%d") % (index + 1, countLogs), path)) paths = [x for x in sorted(glob(join(logPath, "*-enigma2-debug.log")), key=lambda x: isfile(x) and getmtime(x))] paths += [x for x in sorted(glob(join(logPath, "Enigma2-debug*.log")), key=lambda x: isfile(x) and getmtime(x))] if paths: countLogs = len(paths) for index, path in enumerate(reversed(paths)): debugLogs.append((basename(path), _("Debug %d/%d") % (index + 1, countLogs), path)) return debugLogs def refreshInformation(self): # Should we limit the number of fetches per minute? self.debugLogs = [] self.debugLogIndex = 0 self.cachedDebugInfo = {} InformationBase.refreshInformation(self) def displayInformation(self): if self.debugLogs: name, sequence, path = self.debugLogs[self.debugLogIndex] self.setTitle(_("Debug Log Information") if sequence == "Unavailable" else f"{self.baseTitle}: '{name}' ({sequence})") if path in self.cachedDebugInfo: info = self.cachedDebugInfo[path] if isinstance(info, str): errno, strerror = info.split(",", 1) info = [] if errno == "0": info.append(strerror) else: info.append(_("Error %s: Unable to retrieve the '%s' file! (%s)") % (errno, path, strerror)) info.append("") info.append(_("The '%s' file can't be retrieved, please try again later.") % path) else: info = [_("Retrieving '%s' log, please wait...") % name] else: info = [_("Finding available log files, please wait...")] self["information"].setText("\n".join(info)) def getSummaryInformation(self): return "Debug Log Information" class DistributionInformation(InformationBase): def __init__(self, session): InformationBase.__init__(self, session) self.displayDistro = BoxInfo.getItem("displaydistro", "Enigma2") self.setTitle(_("%s Information") % self.displayDistro) self.skinName.insert(0, "DistributionInformation") self["key_info"] = StaticText(_("INFO")) self["key_yellow"] = StaticText(_("Commit Logs")) self["key_blue"] = StaticText(_("Translation")) self["receiverActions"] = HelpableActionMap(self, ["InfoActions", "ColorActions"], { "info": (self.showBuild, _("Show build information")), "yellow": (self.showCommitLogs, _("Show commit log information")), "blue": (self.showTranslation, _("Show translation information")) }, prio=0, description=_("%s Information Actions") % self.displayDistro) self.resolutions = { 480: "NTSC", 576: "PAL", 720: "HD", 1080: "FHD", 2160: "4K", 4320: "8K", 8640: "16K" } self.imageMessage = BoxInfo.getItem("InformationDistributionWelcome", "") def showBuild(self): self.session.openWithCallback(self.informationWindowClosed, BuildInformation) def showCommitLogs(self): self.session.openWithCallback(self.informationWindowClosed, CommitInformation) def showTranslation(self): self.session.openWithCallback(self.informationWindowClosed, TranslationInformation) def displayInformation(self): info = [] info.append(formatLine("H", _("Distribution '%s' information for %s %s") % (self.displayDistro, DISPLAY_BRAND, DISPLAY_MODEL))) info.append("") if self.imageMessage: for line in self.imageMessage: info.append(formatLine("M", line)) info.append("") info.append(formatLine("S", _("System information"))) if self.extraSpacing: info.append("") info.append(formatLine("P1", _("Info file checksum"), _("Invalid") if BoxInfo.getItem("checksumerror", False) else _("Valid"))) override = BoxInfo.getItem("overrideactive", False) if override: info.append(formatLine("P1", _("Info file override"), _("Defined / Active"))) info.append(formatLine("P1", _("Distribution version"), BoxInfo.getItem("imgversion"))) info.append(formatLine("P1", _("Distribution revision"), formatDate(BoxInfo.getItem("imgrevision")))) info.append(formatLine("P1", _("Distribution language"), BoxInfo.getItem("imglanguage"))) info.append(formatLine("P1", _("OEM model"), BoxInfo.getItem("platform", _("Unknown")))) slotCode, bootCode = MultiBoot.getCurrentSlotAndBootCodes() if MultiBoot.canMultiBoot(): device = MultiBoot.getBootDevice() if BoxInfo.getItem("HasHiSi") and "sda" in device and slotCode != "F": slotCode = int(slotCode) image = slotCode - 4 if slotCode > 4 else slotCode - 1 device = _("SDcard slot %s%s") % (image, f" - {device}" if device else "") else: if BoxInfo.getItem("HasKexecMultiboot"): device = MultiBoot.bootSlots[slotCode]["device"] elif BoxInfo.getItem("HasChkrootMultiboot"): device = MultiBoot.bootSlots[slotCode]["device"] if "mmcblk" in device: device = _("eMMC slot %s%s") % (slotCode, f" - {device}" if device else "") elif "mtd" in device: device = _("MTD slot %s%s") % (slotCode, f" - {device}" if device else "") elif "ubi" in device: device = _("UBI slot %s%s") % (slotCode, f" - {device}" if device else "") else: device = _("USB slot %s%s") % (slotCode, f" - {device}" if device else "") info.append(formatLine("P1", _("Hardware MultiBoot device"), device)) info.append(formatLine("P1", _("MultiBoot startup file"), MultiBoot.getStartupFile())) if bootCode: info.append(formatLine("P1", _("MultiBoot boot mode"), MultiBoot.getBootCodeDescription(bootCode))) info.append(formatLine("P1", _("Software MultiBoot"), _("Yes") if BoxInfo.getItem("multiboot", False) else _("No"))) if BoxInfo.getItem("HasKexecMultiboot"): info.append(formatLine("P1", _("Vu+ MultiBoot"), _("Yes"))) info.append(formatLine("P1", _("Flash type"), about.getFlashType())) xResolution = getDesktop(0).size().width() yResolution = getDesktop(0).size().height() info.append(formatLine("P1", _("Skin & Resolution"), f"{config.skin.primary_skin.value.split('/')[0]} ({self.resolutions.get(yResolution, 'Unknown')} - {xResolution} x {yResolution})")) info.append("") info.append(formatLine("S", _("Enigma2 information"))) if self.extraSpacing: info.append("") enigmaVersion = str(BoxInfo.getItem("imageversion")) enigmaVersion = enigmaVersion.rsplit("-", enigmaVersion.count("-") - 2) if len(enigmaVersion) == 3: enigmaVersion = f"{enigmaVersion[0]} ({enigmaVersion[2]}-{enigmaVersion[1].capitalize()})" elif len(enigmaVersion) == 1: enigmaVersion = f"{enigmaVersion[0]}" else: enigmaVersion = f"{enigmaVersion[0]} ({enigmaVersion[1].capitalize()})" info.append(formatLine("P1", _("%s version") % "Enigma2", enigmaVersion)) info.append(formatLine("P1", _("Enigma2 revision"), getE2Rev())) compileDate = str(BoxInfo.getItem("compiledate")) info.append(formatLine("P1", _("Last update"), formatDate(f"{compileDate[:4]}{compileDate[4:6]}{compileDate[6:]}"))) info.append(formatLine("P1", _("Last flash"), formatDate(about.getFlashDateString()))) info.append(formatLine("P1", _("Enigma2 (re)starts"), config.misc.startCounter.value)) info.append(formatLine("P1", _("Enigma2 debug level"), eGetEnigmaDebugLvl())) mediaService = BoxInfo.getItem("mediaservice") if mediaService: info.append(formatLine("P1", _("Media service"), mediaService.replace("enigma2-plugin-systemplugins-", ""))) info.append("") info.append(formatLine("S", _("Build information"))) if self.extraSpacing: info.append("") info.append(formatLine("P1", _("Distribution"), BoxInfo.getItem("displaydistro"))) info.append(formatLine("P1", _("Distribution build"), formatDate(BoxInfo.getItem("imagebuild")))) info.append(formatLine("P1", _("Distribution build date"), formatDate(about.getBuildDateString()))) info.append(formatLine("P1", _("Distribution architecture"), BoxInfo.getItem("architecture"))) if BoxInfo.getItem("imagedir"): info.append(formatLine("P1", _("Distribution folder"), BoxInfo.getItem("imagedir"))) if BoxInfo.getItem("imagefs"): info.append(formatLine("P1", _("Distribution file system"), BoxInfo.getItem("imagefs").strip())) upxVersion = BoxInfo.getItem("upx") info.append(formatLine("P1", _("File compression"), f"{_("Enabled")} / {_("%s version") % "UPX"} {upxVersion}" if upxVersion else _("Disabled"))) info.append(formatLine("P1", _("Feed URL"), BoxInfo.getItem("feedsurl"))) info.append(formatLine("P1", _("Compiled by"), BoxInfo.getItem("developername"))) info.append("") info.append(formatLine("S", _("Software information"))) if self.extraSpacing: info.append("") versions = [ ("GCC", about.getGccVersion()), ("Glibc", about.getGlibcVersion()), ("OpenSSL", about.getVersionFromOpkg("openssl")), ("Python", about.getPythonVersionString()), ("Rust", BoxInfo.getItem("rust")), ("Samba", about.getVersionFromOpkg("samba")), ("GStreamer", getGStreamerVersionString().replace("GStreamer ", "")), ("FFmpeg", about.getVersionFromOpkg("ffmpeg")) ] for version in versions: info.append(formatLine("P1", _("%s version") % version[0], version[1])) bootId = fileReadLine("/proc/sys/kernel/random/boot_id", source=MODULE_NAME) if bootId: info.append(formatLine("P1", _("Boot ID"), bootId)) uuId = fileReadLine("/proc/sys/kernel/random/uuid", source=MODULE_NAME) if uuId: info.append(formatLine("P1", _("UUID"), uuId)) info.append("") info.append(formatLine("S", _("Boot information"))) if self.extraSpacing: info.append("") if BoxInfo.getItem("mtdbootfs"): info.append(formatLine("P1", _("MTD boot"), BoxInfo.getItem("mtdbootfs"))) if BoxInfo.getItem("mtdkernel"): info.append(formatLine("P1", _("MTD kernel"), BoxInfo.getItem("mtdkernel"))) if BoxInfo.getItem("mtdrootfs"): info.append(formatLine("P1", _("MTD root"), BoxInfo.getItem("mtdrootfs"))) if BoxInfo.getItem("kernelfile"): info.append(formatLine("P1", _("Kernel file"), BoxInfo.getItem("kernelfile"))) if BoxInfo.getItem("rootfile"): info.append(formatLine("P1", _("Root file"), BoxInfo.getItem("rootfile"))) if BoxInfo.getItem("mkubifs"): info.append(formatLine("P1", _("MKUBIFS"), BoxInfo.getItem("mkubifs"))) if BoxInfo.getItem("ubinize"): info.append(formatLine("P1", _("UBINIZE"), BoxInfo.getItem("ubinize"))) self["information"].setText("\n".join(info)) def getSummaryInformation(self): return f"{self.displayDistro} Information" class GeolocationInformation(InformationBase): def __init__(self, session): InformationBase.__init__(self, session) self.setTitle(_("Geolocation Information")) self.skinName.insert(0, "GeolocationInformation") def displayInformation(self): info = [] info.append(formatLine("H", _("Geolocation information for %s %s") % getBoxDisplayName())) info.append("") geolocationData = geolocation.getGeolocationData(fields="continent,country,regionName,city,lat,lon,timezone,currency,isp,org,mobile,proxy,query", useCache=False) if geolocationData.get("status", None) == "success": info.append(formatLine("S", _("Location information"))) if self.extraSpacing: info.append("") continent = geolocationData.get("continent", None) if continent: info.append(formatLine("P1", _("Continent"), continent)) country = geolocationData.get("country", None) if country: info.append(formatLine("P1", _("Country"), country)) state = geolocationData.get("regionName", None) if state: # TRANSLATORS: "State" is location information and not condition based information. info.append(formatLine("P1", _("State"), state)) city = geolocationData.get("city", None) if city: info.append(formatLine("P1", _("City"), city)) latitude = geolocationData.get("lat", None) if latitude: info.append(formatLine("P1", _("Latitude"), latitude)) longitude = geolocationData.get("lon", None) if longitude: info.append(formatLine("P1", _("Longitude"), longitude)) info.append("") info.append(formatLine("S", _("Local information"))) if self.extraSpacing: info.append("") timezone = geolocationData.get("timezone", None) if timezone: info.append(formatLine("P1", _("Timezone"), timezone)) currency = geolocationData.get("currency", None) if currency: info.append(formatLine("P1", _("Currency"), currency)) info.append("") info.append(formatLine("S", _("Connection information"))) if self.extraSpacing: info.append("") isp = geolocationData.get("isp", None) if isp: ispOrg = geolocationData.get("org", None) if ispOrg: info.append(formatLine("P1", _("ISP"), f"{isp} ({ispOrg})")) else: info.append(formatLine("P1", _("ISP"), isp)) mobile = geolocationData.get("mobile", None) info.append(formatLine("P1", _("Mobile connection"), (_("Yes") if mobile else _("No")))) proxy = geolocationData.get("proxy", False) info.append(formatLine("P1", _("Proxy detected"), (_("Yes") if proxy else _("No")))) publicIp = geolocationData.get("query", None) if publicIp: info.append(formatLine("P1", _("Public IP"), publicIp)) else: info.append(_("Geolocation information cannot be retrieved, please try again later.")) info.append("") info.append(_("Access to geolocation information requires an Internet connection.")) self["information"].setText("\n".join(info)) def getSummaryInformation(self): return "Geolocation Information" class MemoryInformation(InformationBase): def __init__(self, session): InformationBase.__init__(self, session) self.setTitle(_("Memory Information")) self.skinName.insert(0, "MemoryInformation") self["clearActions"] = HelpableActionMap(self, ["ColorActions"], { "yellow": (self.clearMemoryInformation, _("Clear the virtual memory caches")) }, prio=0, description=_("Memory Information Actions")) self["key_yellow"] = StaticText(_("Clear")) def displayInformation(self): def formatNumber(number): number = number.strip() value, units = number.split(maxsplit=1) if " " in number else (number, None) if "." in value: format = "%.3f" value = float(value) else: format = "%d" value = int(value) return f"{format_string(format, value, grouping=True)} {units}" if units else format_string(format, value, grouping=True) info = [] info.append(formatLine("H", _("Memory information for %s %s") % getBoxDisplayName())) info.append("") memInfo = fileReadLines("/proc/meminfo", source=MODULE_NAME) info.append(formatLine("S", _("RAM (Summary)"))) if self.extraSpacing: info.append("") for line in memInfo: key, value = (x for x in line.split(maxsplit=1)) if key == "MemTotal:": info.append(formatLine("P1", _("Total memory"), formatNumber(value))) elif key == "MemFree:": info.append(formatLine("P1", _("Free memory"), formatNumber(value))) elif key == "Buffers:": info.append(formatLine("P1", _("Buffers"), formatNumber(value))) elif key == "Cached:": info.append(formatLine("P1", _("Cached"), formatNumber(value))) elif key == "SwapTotal:": info.append(formatLine("P1", _("Total swap"), formatNumber(value))) elif key == "SwapFree:": info.append(formatLine("P1", _("Free swap"), formatNumber(value))) info.append("") info.append(formatLine("S", _("FLASH"))) if self.extraSpacing: info.append("") stat = statvfs("/") diskSize = stat.f_blocks * stat.f_frsize diskFree = stat.f_bfree * stat.f_frsize diskUsed = diskSize - diskFree info.append(formatLine("P1", _("Total flash"), f"{scaleNumber(diskSize)} ({scaleNumber(diskSize, 'Iec')})")) info.append(formatLine("P1", _("Used flash"), f"{scaleNumber(diskUsed)} ({scaleNumber(diskUsed, 'Iec')})")) info.append(formatLine("P1", _("Free flash"), f"{scaleNumber(diskFree)} ({scaleNumber(diskFree, 'Iec')})")) for line in fileReadLines("/proc/mtd", [], source=MODULE_NAME): if "\"kernel" in line: data = line.split() name = data[3].strip("\"") size = int(data[1], 16) label = _("Kernel partition") if name == "kernel" else _("Kernel%s partition") % name.replace("kernel", "") info.append(formatLine("P1", label, f"{scaleNumber(size)} ({scaleNumber(size, "Iec")})")) info.append("") info.append(formatLine("S", _("RAM (Details)"))) if self.extraSpacing: info.append("") for line in memInfo: key, value = (x for x in line.split(maxsplit=1)) info.append(formatLine("P1", key[:-1], formatNumber(value))) info.append("") info.append(formatLine("M1", _("The detailed information is intended for developers only."))) info.append(formatLine("M1", _("Please don't panic if you see values that look suspicious."))) self["information"].setText("\n".join(info)) def clearMemoryInformation(self): self.console.ePopen(("/bin/sync", "/bin/sync")) fileWriteLine("/proc/sys/vm/drop_caches", "3") self.informationTimer.start(25) for callback in self.onInformationUpdated: if callable(callback): callback() def getSummaryInformation(self): return "Memory Information Data" class MultiBootInformation(InformationBase): def __init__(self, session): InformationBase.__init__(self, session) self.setTitle(_("MultiBoot Information")) self.skinName.insert(0, "MultiBootInformation") self.slotImages = None def fetchInformation(self): def fetchInformationCallback(slotImages): self.slotImages = slotImages for callback in self.onInformationUpdated: if callable(callback): callback() self.informationTimer.stop() MultiBoot.getSlotImageList(fetchInformationCallback) def refreshInformation(self): self.slotImages = None MultiBoot.loadMultiBoot() InformationBase.refreshInformation(self) def displayInformation(self): info = [] info.append(formatLine("H", _("Boot slot information for %s %s") % getBoxDisplayName())) info.append("") if self.slotImages: slotCode, bootCode = MultiBoot.getCurrentSlotAndBootCodes() slotImageList = sorted(self.slotImages.keys(), key=lambda x: (not x.isnumeric(), int(x) if x.isnumeric() else x)) currentMsg = f" - {_('Current')}" imageLists = {} for slot in slotImageList: for boot in self.slotImages[slot]["bootCodes"]: if imageLists.get(boot) is None: imageLists[boot] = [] current = currentMsg if boot == bootCode and slot == slotCode else "" indent = "P0V" if boot == "" else "P1V" if current: indent = indent.replace("P", "F").replace("V", "F") device = self.slotImages[slot]["device"] slotType = "eMMC" if "mmcblk" in device else "MTD" if "mtd" in device else "UBI" if "ubi" in device else "USB" imageLists[boot].append(formatLine(indent, _("Slot '%s' %s") % (slot, slotType), f"{self.slotImages[slot]['imagename']}{current}")) count = 0 for bootCode in sorted(imageLists.keys()): if bootCode == "": continue if count: info.append("") info.append(formatLine("S", MultiBoot.getBootCodeDescription(bootCode), None)) if self.extraSpacing: info.append("") info.extend(imageLists[bootCode]) count += 1 if count: info.append("") if "" in imageLists: info.extend(imageLists[""]) else: info.append(formatLine("P1", _("Retrieving boot slot information..."))) self["information"].setText("\n".join(info)) def getSummaryInformation(self): return "MultiBoot Information Data" class NetworkInformation(InformationBase): def __init__(self, session): InformationBase.__init__(self, session) self.setTitle(_("Network Information")) self.skinName.insert(0, "NetworkInformation") self["key_yellow"] = StaticText(_("WAN Geolocation")) self["geolocationActions"] = HelpableActionMap(self, ["ColorActions"], { "yellow": (self.useGeolocation, _("Use geolocation to get WAN information")), }, prio=0, description=_("Network Information Actions")) self.interfaceData = {} self.geolocationData = [] self.ifconfigAttributes = { "Link encap": "encapsulation", "HWaddr": "mac", "inet addr": "addr", "Bcast": "brdaddr", "Mask": "nmask", "inet6 addr": "addr6", "Scope": "scope", "MTU": "mtu", "Metric": "metric", "RX packets": "rxPackets", "rxerrors": "rxErrors", "rxdropped": "rxDropped", "rxoverruns": "rxOverruns", "rxframe": "rxFrame", "TX packets": "txPackets", "txerrors": "txErrors", "txdropped": "txDropped", "txoverruns": "txOverruns", "collisions": "txCollisions", "txqueuelen": "txQueueLen", "RX bytes": "rxBytes", "TX bytes": "txBytes" } self.iwconfigAttributes = { "interface": "interface", "standard": "standard", "ESSID": "ssid", "Mode": "mode", "Frequency": "frequency", "Access Point": "accessPoint", "Bit Rate": "bitrate", "Tx-Power": "transmitPower", "Retry short limit": "retryLimit", "RTS thr": "rtsThrottle", "Fragment thr": "fragThrottle", "Encryption key": "encryption", "Power Management": "powerManagement", "Link Quality": "signalQuality", "Signal level": "signalStrength", "Rx invalid nwid": "rxInvalidNwid", "Rx invalid crypt": "rxInvalidCrypt", "Rx invalid frag": "rxInvalidFrag", "Tx excessive retries": "txExcessiveReties", "Invalid misc": "invalidMisc", "Missed beacon": "missedBeacon" } self.ethtoolAttributes = { "Speed": "speed", "Duplex": "duplex", "Transceiver": "transceiver", "Auto-negotiation": "autoNegotiation", "Link detected": "link" } def useGeolocation(self): geolocationData = geolocation.getGeolocationData(fields="isp,org,mobile,proxy,query", useCache=False) info = [] if geolocationData.get("status", None) == "success": info.append("") info.append(formatLine("S", _("WAN connection information"))) isp = geolocationData.get("isp", None) if isp: ispOrg = geolocationData.get("org", None) if ispOrg: info.append(formatLine("P1", _("ISP"), f"{isp} ({ispOrg})")) else: info.append(formatLine("P1", _("ISP"), isp)) mobile = geolocationData.get("mobile", None) info.append(formatLine("P1", _("Mobile connection"), (_("Yes") if mobile else _("No")))) proxy = geolocationData.get("proxy", False) info.append(formatLine("P1", _("Proxy detected"), (_("Yes") if proxy else _("No")))) publicIp = geolocationData.get("query", None) if publicIp: info.append(formatLine("P1", _("Public IP"), publicIp)) else: info.append(_("Geolocation information cannot be retrieved, please try again later.")) info.append("") info.append(_("Access to geolocation information requires an Internet connection.")) self.geolocationData = info for callback in self.onInformationUpdated: if callable(callback): callback() def fetchInformation(self): self.informationTimer.stop() for interface in sorted([x for x in listdir("/sys/class/net") if not self.isBlacklisted(x)]): self.interfaceData[interface] = {} self.console.ePopen(("/sbin/ifconfig", "/sbin/ifconfig", interface), self.ifconfigInfoFinished, extra_args=interface) if iNetwork.isWirelessInterface(interface): self.console.ePopen(("/sbin/iwconfig", "/sbin/iwconfig", interface), self.iwconfigInfoFinished, extra_args=interface) else: self.console.ePopen(("/usr/sbin/ethtool", "/usr/sbin/ethtool", interface), self.ethtoolInfoFinished, extra_args=interface) for callback in self.onInformationUpdated: if callable(callback): callback() def isBlacklisted(self, interface): for type in ("lo", "wifi", "wmaster", "sit", "tun", "sys", "p2p", "ip6_vti", "ip_vti", "ip6tn", "wg", "tap"): if interface.startswith(type): return True return False def ifconfigInfoFinished(self, result, retVal, extraArgs): # This temporary code borrowed and adapted from the new but unreleased Network.py! if retVal == 0: capture = False data = "" if isinstance(result, bytes): result = result.decode("UTF-8", "ignore") for line in result.split("\n"): if line.startswith(f"{extraArgs} "): capture = True if "HWaddr " in line: line = line.replace("HWaddr ", "HWaddr:") data += line continue if capture and line.startswith(" "): if " Scope:" in line: line = line.replace(" Scope:", " ") elif "X packets:" in line: pos = line.index("X packets:") direction = line[pos - 1:pos].lower() line = "%s%s" % (line[0:pos + 10], line[pos + 10:].replace(" ", " %sx" % direction)) # line = f"{line[0:pos + 10]}{line[pos + 10:].replace(" ", f" {direction}x")}" # Python 3.12 elif " txqueuelen" in line: line = line.replace(" txqueuelen:", " txqueuelen:") data += line continue if line == "": break data = list(filter(None, [x.strip().replace("=", ":", 1) for x in data.split(" ")])) data[0] = f"interface:{data[0]}" # print("[Network] DEBUG: Raw network data %s." % data) for item in data: if ":" not in item: flags = item.split() self.interfaceData[extraArgs]["up"] = True if "UP" in flags else False self.interfaceData[extraArgs]["status"] = "up" if "UP" in flags else "down" # Legacy status flag. self.interfaceData[extraArgs]["running"] = True if "RUNNING" in flags else False self.interfaceData[extraArgs]["broadcast"] = True if "BROADCAST" in flags else False self.interfaceData[extraArgs]["multicast"] = True if "MULTICAST" in flags else False continue key, value = item.split(":", 1) key = self.ifconfigAttributes.get(key, None) if key: value = value.strip() if value.startswith("\""): value = value[1:-1] if key == "addr6": if key not in self.interfaceData[extraArgs]: self.interfaceData[extraArgs][key] = [] self.interfaceData[extraArgs][key].append(value) else: self.interfaceData[extraArgs][key] = value for callback in self.onInformationUpdated: if callable(callback): callback() def iwconfigInfoFinished(self, result, retVal, extraArgs): # This temporary code borrowed and adapted from the new but unreleased Network.py! if retVal == 0: capture = False data = "" if isinstance(result, bytes): result = result.decode("UTF-8", "ignore") for line in result.split("\n"): if line.startswith(f"{extraArgs} "): capture = True data += line continue if capture and line.startswith(" "): data += line continue if line == "": break data = list(filter(None, [x.strip().replace("=", ":", 1) for x in data.split(" ")])) data[0] = f"interface:{data[0]}" data[1] = f"standard:{data[1]}" for item in data: if ":" not in item: continue key, value = item.split(":", 1) key = self.iwconfigAttributes.get(key, None) if key: value = value.strip() if value.startswith("\""): value = value[1:-1] self.interfaceData[extraArgs][key] = value if "encryption" in self.interfaceData[extraArgs]: self.interfaceData[extraArgs]["encryption"] = _("Disabled or WPA/WPA2") if self.interfaceData[extraArgs]["encryption"] == "off" else _("Enabled") if "standard" in self.interfaceData[extraArgs] and "no wireless extensions" in self.interfaceData[extraArgs]["standard"]: del self.interfaceData[extraArgs]["standard"] self.interfaceData[extraArgs]["wireless"] = False else: self.interfaceData[extraArgs]["wireless"] = True if "ssid" in self.interfaceData[extraArgs]: self.interfaceData[extraArgs]["SSID"] = self.interfaceData[extraArgs]["ssid"] for callback in self.onInformationUpdated: if callable(callback): callback() def ethtoolInfoFinished(self, result, retVal, extraArgs): # This temporary code borrowed and adapted from the new but unreleased Network.py! if retVal == 0: if isinstance(result, bytes): result = result.decode("UTF-8", "ignore") for line in result.split("\n"): if "Speed:" in line: self.interfaceData[extraArgs]["speed"] = line.split(":")[1][:-4].strip() if "Duplex:" in line: self.interfaceData[extraArgs]["duplex"] = _(line.split(":")[1].strip().capitalize()) if "Transceiver:" in line: self.interfaceData[extraArgs]["transeiver"] = _(line.split(":")[1].strip().capitalize()) if "Auto-negotiation:" in line: self.interfaceData[extraArgs]["autoNegotiation"] = line.split(":")[1].strip().lower() == "on" if "Link detected:" in line: self.interfaceData[extraArgs]["link"] = line.split(":")[1].strip().lower() == "yes" for callback in self.onInformationUpdated: if callable(callback): callback() def displayInformation(self): info = [] info.append(formatLine("H", _("Network information for %s %s") % getBoxDisplayName())) info.append("") hostname = fileReadLine("/proc/sys/kernel/hostname", source=MODULE_NAME) info.append(formatLine("S0S", _("Hostname"), hostname)) for interface in sorted(self.interfaceData.keys()): info.append("") info.append(formatLine("S", _("Interface '%s'") % interface, iNetwork.getFriendlyAdapterName(interface))) if "up" in self.interfaceData[interface]: info.append(formatLine("P1", _("Status"), (_("Up / Active") if self.interfaceData[interface]["up"] else _("Down / Inactive")))) if self.interfaceData[interface]["up"]: if "addr" in self.interfaceData[interface]: info.append(formatLine("P1", _("IP address"), self.interfaceData[interface]["addr"])) if "nmask" in self.interfaceData[interface]: info.append(formatLine("P1", _("Netmask"), self.interfaceData[interface]["nmask"])) if "brdaddr" in self.interfaceData[interface]: info.append(formatLine("P1", _("Broadcast address"), self.interfaceData[interface]["brdaddr"])) if "addr6" in self.interfaceData[interface]: for addr6 in self.interfaceData[interface]["addr6"]: addr, scope = addr6.split() info.append(formatLine("P1", _("IPv6 address"), addr)) info.append(formatLine("P3V2", _("Scope"), scope)) info.append(formatLine("P1", _("IPv6 address"), "2003:0000:4021:4700:4270:0000:0000:8250/64")) info.append(formatLine("P3V2", _("Scope"), "Global")) if "mac" in self.interfaceData[interface]: info.append(formatLine("P1", _("MAC address"), self.interfaceData[interface]["mac"])) if "speed" in self.interfaceData[interface]: info.append(formatLine("P1", _("Speed"), f"{self.interfaceData[interface]['speed']} Mbps")) if "duplex" in self.interfaceData[interface]: info.append(formatLine("P1", _("Duplex"), self.interfaceData[interface]["duplex"])) if "mtu" in self.interfaceData[interface]: info.append(formatLine("P1", _("MTU"), self.interfaceData[interface]["mtu"])) if "link" in self.interfaceData[interface]: info.append(formatLine("P1", _("Link detected"), (_("Yes") if self.interfaceData[interface]["link"] else _("No")))) if "ssid" in self.interfaceData[interface]: info.append(formatLine("P1", _("SSID"), self.interfaceData[interface]["ssid"])) if "standard" in self.interfaceData[interface]: info.append(formatLine("P1", _("Standard"), self.interfaceData[interface]["standard"])) if "encryption" in self.interfaceData[interface]: info.append(formatLine("P1", _("Encryption"), self.interfaceData[interface]["encryption"])) if "frequency" in self.interfaceData[interface]: info.append(formatLine("P1", _("Frequency"), self.interfaceData[interface]["frequency"])) if "accessPoint" in self.interfaceData[interface]: info.append(formatLine("P1", _("Access point"), self.interfaceData[interface]["accessPoint"])) if "bitrate" in self.interfaceData[interface]: info.append(formatLine("P1", _("Bitrate"), self.interfaceData[interface]["bitrate"])) if "signalQuality" in self.interfaceData[interface]: info.append(formatLine("P1", _("Signal quality"), self.interfaceData[interface]["signalQuality"])) if "signalStrength" in self.interfaceData[interface]: info.append(formatLine("P1", _("Signal strength"), self.interfaceData[interface]["signalStrength"])) if "rxBytes" in self.interfaceData[interface] or "txBytes" in self.interfaceData[interface]: info.append("") rxBytes = int(self.interfaceData[interface]["rxBytes"].split(" ")[0]) txBytes = int(self.interfaceData[interface]["txBytes"].split(" ")[0]) info.append(formatLine("P1", _("Bytes received"), "%d (%s)" % (rxBytes, scaleNumber(rxBytes, style="Iec", format="%.1f")))) info.append(formatLine("P1", _("Bytes sent"), "%d (%s)" % (txBytes, scaleNumber(txBytes, style="Iec", format="%.1f")))) info += self.geolocationData self["information"].setText("\n".join(info)) class PictureInformation(Screen): skin = """ """ def __init__(self, session): Screen.__init__(self, session, enableHelp=True) self.setTitle(_("Picture Information")) self["name"] = Label() self["picture"] = Pixmap() self["key_red"] = StaticText(_("Close")) self["key_yellow"] = StaticText() self["key_blue"] = StaticText() self["actions"] = HelpableActionMap(self, ["OkCancelActions", "ColorActions"], { "cancel": (self.keyCancel, _("Close the screen")), "close": (self.closeRecursive, _("Close the screen and exit all menus")), "red": (self.keyCancel, _("Close the screen")), }, prio=0, description=_("Picture Information Actions")) self["pictureActions"] = HelpableActionMap(self, ["OkCancelActions", "ColorActions", "NavigationActions"], { "ok": (self.nextPicture, _("Show next picture")), "yellow": (self.prevPicture, _("Show previous picture")), "blue": (self.nextPicture, _("Show next picture")), "up": (self.prevPicture, _("Show previous picture")), "left": (self.prevPicture, _("Show previous picture")), "right": (self.nextPicture, _("Show next picture")), "down": (self.nextPicture, _("Show next picture")) }, prio=0, description=_("Picture Information Actions")) self["pictureActions"].setEnabled(False) self.definedPictures = ( (_("Remote Control"), f"hardware/{BoxInfo.getItem('rcname')}.png"), (_("Front"), f"hardware/{MACHINE_BUILD}_front.png"), (_("Rear"), f"hardware/{MACHINE_BUILD}_rear.png"), (_("Internal"), f"hardware/{MACHINE_BUILD}_internal.png") ) self.pictures = [] for item in self.definedPictures: picture = resolveFilename(SCOPE_SKINS, item[1]) if isfile(picture): picture = LoadPixmap(picture) if picture: self.pictures.append((item[0], picture)) if not self.pictures: self.pictures.append((_("No pictures available"), None)) self.pictureIndex = 0 self.pictureMax = len(self.pictures) self.onLayoutFinish.append(self.layoutFinished) def keyCancel(self): self.close() def closeRecursive(self): self.close(True) def layoutFinished(self): self["name"].setText(f"{DISPLAY_BRAND} {DISPLAY_MODEL} - {self.pictures[self.pictureIndex][0]} View") if self.pictureMax > 1: self["key_yellow"].setText(_("%s View") % self.pictures[(self.pictureIndex - 1) % self.pictureMax][0]) self["key_blue"].setText(_("%s View") % self.pictures[(self.pictureIndex + 1) % self.pictureMax][0]) self["pictureActions"].setEnabled(True) else: self["key_yellow"].setText("") self["key_blue"].setText("") self["pictureActions"].setEnabled(False) picture = self.pictures[self.pictureIndex][1] if picture: self["picture"].instance.setPixmap(self.pictures[self.pictureIndex][1]) def prevPicture(self): self.pictureIndex = (self.pictureIndex - 1) % self.pictureMax self.layoutFinished() def nextPicture(self): self.pictureIndex = (self.pictureIndex + 1) % self.pictureMax self.layoutFinished() class ReceiverInformation(InformationBase): def __init__(self, session): InformationBase.__init__(self, session) self.setTitle(_("Receiver Information")) self.skinName.insert(0, "ReceiverInformation") self["key_info"] = StaticText(_("INFO")) self["key_yellow"] = StaticText(_("System Information")) self["key_blue"] = StaticText(_("Debug Information")) self["receiverActions"] = HelpableActionMap(self, ["InfoActions", "ColorActions"], { "info": (self.showPictureInformation, _("Show picture information")), "yellow": (self.showSystemInformation, _("Show system information")), "blue": (self.showDebugInformation, _("Show debug log information")) }, prio=0, description=_("Receiver Information Actions")) def showPictureInformation(self): self.session.openWithCallback(self.informationWindowClosed, PictureInformation) def showSystemInformation(self): self.session.openWithCallback(self.informationWindowClosed, SystemInformation) def showDebugInformation(self): self.session.openWithCallback(self.informationWindowClosed, DebugInformation) def displayInformation(self): def findPackageRevision(package, packageList): revision = None data = [x for x in packageList if f"-{package}" in x] if data: data = data[0].split("-") if len(data) >= 4: revision = data[3] return revision info = [] info.append(formatLine("H", _("Receiver information for %s %s") % getBoxDisplayName())) info.append("") info.append(formatLine("S", _("Hardware information"))) if self.extraSpacing: info.append("") info.append(formatLine("P1", _("Receiver name"), "%s %s" % getBoxDisplayName())) info.append(formatLine("P1", _("Build Brand"), BoxInfo.getItem("brand"))) platform = BoxInfo.getItem("platform") info.append(formatLine("P1", _("Build Model"), MODEL)) if platform != MODEL: info.append(formatLine("P1", _("Platform"), platform)) procModel = getBoxProc() if procModel != MODEL: info.append(formatLine("P1", _("Proc model"), procModel)) procModelType = getBoxProcTypeName() if procModelType and procModelType != "unknown": info.append(formatLine("P1", _("Hardware type"), procModelType)) hwSerial = getHWSerial() if hwSerial: info.append(formatLine("P1", _("Hardware serial"), (hwSerial if hwSerial != "unknown" else about.getCPUSerial()))) hwRelease = fileReadLine("/proc/stb/info/release", source=MODULE_NAME) if hwRelease: info.append(formatLine("P1", _("Factory release"), hwRelease)) displayType = BoxInfo.getItem("displaytype") if displayType and not displayType.startswith(" "): info.append(formatLine("P1", _("Display type"), displayType)) fpVersion = getFPVersion() if fpVersion and fpVersion != "unknown": info.append(formatLine("P1", _("Front processor version"), fpVersion)) demodVersion = getDemodVersion() if demodVersion and demodVersion != "unknown": info.append(formatLine("P1", _("Demod firmware version"), demodVersion)) transcoding = _("Yes") if BoxInfo.getItem("transcoding") else _("MultiTranscoding") if BoxInfo.getItem("multitranscoding") else _("No") info.append(formatLine("P1", _("Transcoding"), transcoding)) temp = about.getSystemTemperature() if temp: info.append(formatLine("P1", _("System temperature"), temp)) info.append("") info.append(formatLine("S", _("Processor information"))) if self.extraSpacing: info.append("") cpu = about.getCPUInfoString() info.append(formatLine("P1", _("CPU"), cpu[0])) info.append(formatLine("P1", _("CPU speed/cores"), f"{cpu[1]} {cpu[2]}")) if cpu[3]: info.append(formatLine("P1", _("CPU temperature"), cpu[3])) info.append(formatLine("P1", _("CPU brand"), about.getCPUBrand())) socFamily = BoxInfo.getItem("socfamily") if socFamily: info.append(formatLine("P1", _("SoC family"), socFamily)) info.append(formatLine("P1", _("CPU architecture"), about.getCPUArch())) if BoxInfo.getItem("fpu"): info.append(formatLine("P1", _("FPU"), BoxInfo.getItem("fpu"))) if BoxInfo.getItem("architecture") == "aarch64": info.append(formatLine("P1", _("MultiLib"), (_("Yes") if BoxInfo.getItem("multilib") else _("No")))) info.append("") info.append(formatLine("S", _("Remote control information"))) if self.extraSpacing: info.append("") rcIndex = int(config.inputDevices.remotesIndex.value) info.append(formatLine("P1", _("RC identification"), f"{remoteControl.remotes[rcIndex][remoteControl.REMOTE_DISPLAY_NAME]} (Index: {rcIndex})")) rcName = remoteControl.remotes[rcIndex][remoteControl.REMOTE_NAME] info.append(formatLine("P1", _("RC selected name"), rcName)) boxName = BoxInfo.getItem("rcname") if boxName != rcName: info.append(formatLine("P1", _("RC default name"), boxName)) rcType = remoteControl.remotes[rcIndex][remoteControl.REMOTE_RCTYPE] info.append(formatLine("P1", _("RC selected type"), rcType)) boxType = BoxInfo.getItem("rctype") if boxType != rcType: info.append(formatLine("P1", _("RC default type"), boxType)) boxRcType = getBoxRCType() if boxRcType: if boxRcType == "unknown": if isfile("/usr/bin/remotecfg"): boxRcType = _("Amlogic remote") elif isfile("/usr/sbin/lircd"): boxRcType = _("LIRC remote") if boxRcType != rcType and boxRcType != "unknown": info.append(formatLine("P1", _("RC detected type"), boxRcType)) customCode = fileReadLine("/proc/stb/ir/rc/customcode", source=MODULE_NAME) if customCode: info.append(formatLine("P1", _("RC custom code"), customCode)) if BoxInfo.getItem("HasHDMI-CEC") and config.hdmicec.enabled.value: info.append("") address = config.hdmicec.fixed_physical_address.value if config.hdmicec.fixed_physical_address.value != "0.0.0.0" else _("N/A") info.append(formatLine("P1", _("HDMI-CEC address"), address)) info.append("") info.append(formatLine("S", _("Driver and kernel information"))) if self.extraSpacing: info.append("") info.append(formatLine("P1", _("Drivers version"), formatDate(BoxInfo.getItem("driversdate")))) info.append(formatLine("P1", _("Kernel version"), BoxInfo.getItem("kernel"))) deviceId = fileReadLine("/proc/device-tree/amlogic-dt-id", source=MODULE_NAME) if deviceId: info.append(formatLine("P1", _("Device id"), deviceId)) givenId = fileReadLine("/proc/device-tree/le-dt-id", source=MODULE_NAME) if givenId: info.append(formatLine("P1", _("Given device id"), givenId)) if BoxInfo.getItem("HiSilicon"): info.append("") info.append(formatLine("S", _("HiSilicon specific information"))) if self.extraSpacing: info.append("") process = Popen(("/usr/bin/opkg", "list-installed"), stdout=PIPE, stderr=PIPE, universal_newlines=True) stdout, stderr = process.communicate() if process.returncode == 0: missing = True packageList = stdout.split("\n") revision = findPackageRevision("grab", packageList) if revision and revision != "r0": info.append(formatLine("P1", _("Grab"), revision)) missing = False revision = findPackageRevision("hihalt", packageList) if revision: info.append(formatLine("P1", _("Halt"), revision)) missing = False revision = findPackageRevision("libs", packageList) if revision: info.append(formatLine("P1", _("Libs"), revision)) missing = False revision = findPackageRevision("partitions", packageList) if revision: info.append(formatLine("P1", _("Partitions"), revision)) missing = False revision = findPackageRevision("reader", packageList) if revision: info.append(formatLine("P1", _("Reader"), revision)) missing = False revision = findPackageRevision("showiframe", packageList) if revision: info.append(formatLine("P1", _("Showiframe"), revision)) missing = False if missing: info.append(formatLine("P1", _("HiSilicon specific information not found."))) else: info.append(formatLine("P1", _("Package information currently not available!"))) info.append("") info.append(formatLine("S", _("Tuner information"))) if self.extraSpacing: info.append("") for count, nim in enumerate(nimmanager.nimListCompressed()): tuner, type = (x.strip() for x in nim.split(":", 1)) info.append(formatLine("P1", tuner, type)) info.append("") info.append(formatLine("S", _("Storage / Drive information"))) if self.extraSpacing: info.append("") stat = statvfs("/") diskSize = stat.f_blocks * stat.f_frsize info.append(formatLine("P1", _("Internal flash"), f"{scaleNumber(diskSize)} ({scaleNumber(diskSize, 'Iec')})")) # hddList = storageManager.HDDList() hddList = harddiskmanager.HDDList() if hddList: for hdd in hddList: hdd = hdd[1] diskSize = hdd.diskSize() * 1000000 info.append(formatLine("P1", hdd.model(), f"{scaleNumber(diskSize)} ({scaleNumber(diskSize, 'Iec')})")) else: info.append(formatLine("H", _("No hard disks detected."))) info.append("") info.append(formatLine("S", _("Network information"))) if self.extraSpacing: info.append("") for x in about.GetIPsFromNetworkInterfaces(): info.append(formatLine("P1", x[0], x[1])) info.append("") info.append(formatLine("S", _("Uptime"), about.getBoxUptime())) self["information"].setText("\n".join(info)) def getSummaryInformation(self): return "Receiver Information" class ServiceInformation(InformationBase): def __init__(self, session, serviceRef=None): InformationBase.__init__(self, session) self.baseTitle = _("Service Information") self.setTitle(self.baseTitle) self.skinName.insert(0, "ServiceInformation") self.serviceRef = serviceRef self["key_menu"] = StaticText() self["key_yellow"] = StaticText() self["key_blue"] = StaticText() self["serviceActions"] = HelpableActionMap(self, ["MenuActions", "ColorActions", "NavigationActions"], { "menu": (self.showServiceMenu, _("Show selection for service information screen")), "yellow": (self.previousService, _("Show previous service information screen")), "blue": (self.nextService, _("Show next service information screen")), "left": (self.previousService, _("Show previous service information screen")), "right": (self.nextService, _("Show next service information screen")) }, prio=0, description=_("Service Information Actions")) self.serviceCommands = [ (_("Service and PID information"), _("Service & PID"), self.showServiceInformation), (_("Transponder information"), _("Transponder"), self.showTransponderInformation), (_("ECM information"), _("ECM"), self.showECMInformation) ] self.serviceCommandsMax = len(self.serviceCommands) self.info = None if serviceRef: self.serviceCommandsIndex = 1 else: self.eventTracker = ServiceEventTracker(screen=self, eventmap={iPlayableService.evEnd: self.fetchInformationDelayed}) self.serviceCommandsIndex = 0 def getServiceTransponderData(self): self.frontendInfo = None self.serviceInfo = None self.transponderInfo = None self.service = None playServiceRef = self.session.nav.getCurrentlyPlayingServiceReference() if playServiceRef: self.serviceName = ServiceReference(playServiceRef).getServiceName() self.serviceReference = playServiceRef.toString() self.serviceReferenceType = playServiceRef.type else: self.serviceName = _("N/A") self.serviceReference = _("N/A") self.serviceReferenceType = 0 if self.serviceRef: # and not (playServiceRef and playServiceRef == self.serviceRef): self.serviceName = ServiceReference(self.serviceRef).getServiceName() self.transponderInfo = eServiceCenter.getInstance().info(self.serviceRef).getInfoObject(self.serviceRef, iServiceInformation.sTransponderData) # Note that info is a iStaticServiceInformation not a iServiceInformation. self["key_menu"].setText("") self["serviceActions"].setEnabled(False) self.serviceCommandsIndex = 1 else: self.service = self.session.nav.getCurrentService() if self.service: self.serviceInfo = self.service.info() self.frontendInfo = self.service.frontendInfo() if self.frontendInfo and not self.frontendInfo.getAll(True): self.frontendInfo = None serviceRef = playServiceRef self.transponderInfo = serviceRef and eServiceCenter.getInstance().info(serviceRef).getInfoObject(serviceRef, iServiceInformation.sTransponderData) self["key_menu"].setText(_("MENU")) self["serviceActions"].setEnabled(True) def showServiceMenu(self): def showServiceMenuCallBack(selectedIndex): if isinstance(selectedIndex, int): self.serviceCommandsIndex = selectedIndex self.displayInformation() self.informationTimer.start(25) choices = [(serviceCommand[0], index) for index, serviceCommand in enumerate(self.serviceCommands)] self.session.openWithCallback(showServiceMenuCallBack, MessageBox, text=_("Select service information to view:"), list=choices, windowTitle=self.baseTitle) def previousService(self): self.serviceCommandsIndex = (self.serviceCommandsIndex - 1) % self.serviceCommandsMax self.displayInformation() self.informationTimer.start(25) def nextService(self): self.serviceCommandsIndex = (self.serviceCommandsIndex + 1) % self.serviceCommandsMax self.displayInformation() self.informationTimer.start(25) def fetchInformation(self): self.informationTimer.stop() self.getServiceTransponderData() name, label, method = self.serviceCommands[self.serviceCommandsIndex] self.info = method() for callback in self.onInformationUpdated: if callable(callback): callback() def fetchInformationDelayed(self): # This allows the newly selected service to stabilize before updating the service data. self.informationTimer.startLongTimer(3) def refreshInformation(self): self.getServiceTransponderData() InformationBase.refreshInformation(self) def displayInformation(self): name, label, method = self.serviceCommands[self.serviceCommandsIndex] self.setTitle(f"{self.baseTitle}: {label}") if self["key_menu"].getText(): self["key_yellow"].setText(self.serviceCommands[(self.serviceCommandsIndex - 1) % self.serviceCommandsMax][1]) self["key_blue"].setText(self.serviceCommands[(self.serviceCommandsIndex + 1) % self.serviceCommandsMax][1]) else: self["key_yellow"].setText("") self["key_blue"].setText("") info = [_("Retrieving '%s' information, please wait...") % name] if self.info is None else self.info if info == [""]: info = [_("There is no information to show for '%s'.") % name] self["information"].setText("\n".join(info)) self.frontendInfo = None self.serviceInfo = None self.transponderInfo = None self.service = None def showServiceInformation(self): def formatHex(value): return f"0x{value:04X} ({value})" if value and isinstance(value, int) else "" def getServiceInfoValue(item): if self.serviceInfo: value = self.serviceInfo.getInfo(item) if value == -2: value = self.serviceInfo.getInfoString(item) elif value == -1: value = _("N/A") else: value = "" return value def getNamespace(value): if isinstance(value, str): namespace = f"{_('N/A')} - {_('N/A')}" else: namespace = f"{value & 0xFFFFFFFF:08X}" if namespace.startswith("EEEE"): namespace = f"{namespace} - DVB-T" elif namespace.startswith("FFFF"): namespace = f"{namespace} - DVB-C" else: position = int(namespace[:4], 16) if position > 1800: position = 3600 - position alignment = _("W") else: alignment = _("E") namespace = f"{namespace} - {float(position) / 10.0}\u00B0{alignment}" return namespace def getSubtitleList(): # IanSav: If we know the current subtitle then we should flag it as "(Current)". subtitleTypes = { # This should be in SystemInfo maybe as a BoxInfo variable. 0: _("Unknown"), 1: _("Embedded"), 2: _("SSA file"), 3: _("ASS file"), 4: _("SRT file"), 5: _("VOB file"), 6: _("PGS file"), 7: "WebVTT", } subtitleSelected = self.service and self.service.subtitle().getCachedSubtitle() if subtitleSelected: subtitleSelected = subtitleSelected[:3] subtitle = self.service and self.service.subtitle() subList = subtitle and subtitle.getSubtitleList() or [] for subtitle in subList: print(subtitle) indent = "P1F0" if subtitle[:3] == subtitleSelected else "P1" subtitleLang = subtitle[4] if subtitle[0] == 0: # DVB PID. info.append(formatLine(indent, _("DVB Subtitles PID & Language"), f"{formatHex(subtitle[1])} - {subtitleLang}")) elif subtitle[0] == 1: # Teletext. info.append(formatLine(indent, _("TXT Subtitles page & Language"), f"0x0{subtitle[3] or 8:X}{subtitle[2]:02X} - {subtitleLang}")) elif subtitle[0] == 2: # File. subtitleDesc = subtitleTypes.get(subtitle[2], f"{_('Unknown')}: {subtitle[2]}") info.append(formatLine(indent, _("Other Subtitles & Language"), f"{subtitle[1] + 1} - {subtitleDesc} - {subtitleLang}")) info = [] info.append(formatLine("H", _("Service and PID information for '%s'") % self.serviceName)) info.append("") if self.serviceInfo: from Components.Converter.PliExtraInfo import codec_data # This should be in SystemInfo maybe as a BoxInfo variable. videoData = [] videoData.append(codec_data.get(self.serviceInfo.getInfo(iServiceInformation.sVideoType), _("N/A"))) width = self.serviceInfo.getInfo(iServiceInformation.sVideoWidth) height = self.serviceInfo.getInfo(iServiceInformation.sVideoHeight) if width > 0 and height > 0: videoData.append(f"{width}x{height}") videoData.append(f"{(self.serviceInfo.getInfo(iServiceInformation.sFrameRate) + 500) // 1000}{('i', 'p', '')[self.serviceInfo.getInfo(iServiceInformation.sProgressive)]}") videoData.append(f"[{'4:3' if getServiceInfoValue(iServiceInformation.sAspect) in (1, 2, 5, 6, 9, 0xA, 0xD, 0xE) else '16:9'}]") # This should be in SystemInfo maybe as a BoxInfo variable. gamma = ("SDR", "HDR", "HDR10", "HLG", "")[self.serviceInfo.getInfo(iServiceInformation.sGamma)] # This should be in SystemInfo maybe as a BoxInfo variable. if gamma: videoData.append(gamma) videoData = " - ".join(videoData) else: videoData = _("Unknown") if "%3a//" in self.serviceReference and self.serviceReferenceType not in (1, 257, 4098, 4114): # IPTV 4097 5001, no PIDs shown. info.append(formatLine("P1", _("Video Codec, Size & Format"), videoData)) info.append(formatLine("P1", _("Service reference"), ":".join(self.serviceReference.split(":")[:9]))) info.append(formatLine("P1", _("URL"), self.serviceReference.split(":")[10].replace("%3a", ":"))) getSubtitleList() # IanSav: This wasn't activated to be used! else: if ":/" in self.serviceReference: # mp4 videos, DVB-S-T recording. info.append(formatLine("P1", _("Video Codec, Size & Format"), videoData)) info.append(formatLine("P1", _("Service reference"), ":".join(self.serviceReference.split(":")[:9]))) info.append(formatLine("P1", _("Filename"), self.serviceReference.split(":")[10])) else: # fallback, movistartv, live DVB-S-T. info.append(formatLine("P1", _("Provider"), getServiceInfoValue(iServiceInformation.sProvider))) info.append(formatLine("P1", _("Video Codec, Size & Format"), videoData)) if "%3a//" in self.serviceReference: # fallback, movistartv. info.append(formatLine("P1", _("Service reference"), ":".join(self.serviceReference.split(":")[:9]))) info.append(formatLine("P1", _("URL"), self.serviceReference.split(":")[10].replace("%3a", ":"))) else: # Live DVB-S-T info.append(formatLine("P1", _("Service reference"), self.serviceReference)) info.append(formatLine("P1", _("Namespace & Orbital position"), getNamespace(getServiceInfoValue(iServiceInformation.sNamespace)))) info.append(formatLine("P1", _("Service ID (SID)"), formatHex(getServiceInfoValue(iServiceInformation.sSID)))) info.append(formatLine("P1", _("Transport Stream ID (TSID)"), formatHex(getServiceInfoValue(iServiceInformation.sTSID)))) info.append(formatLine("P1", _("Original Network ID (ONID)"), formatHex(getServiceInfoValue(iServiceInformation.sONID)))) info.append(formatLine("P1", _("Video PID"), formatHex(getServiceInfoValue(iServiceInformation.sVideoPID)))) audio = self.service and self.service.audioTracks() numberOfTracks = audio and audio.getNumberOfTracks() if numberOfTracks: for index in range(numberOfTracks): audioPID = audio.getTrackInfo(index).getPID() audioDesc = audio.getTrackInfo(index).getDescription() audioLang = audio.getTrackInfo(index).getLanguage() or _("Undefined") audioPIDValue = _("N/A") if getServiceInfoValue(iServiceInformation.sAudioPID) == "N/A" else formatHex(audioPID) indent = "P1F0" if numberOfTracks > 1 and audio.getCurrentTrack() == index else "P1" info.append(formatLine(indent, _("Audio PID%s, Codec & Language") % (f" {index + 1}" if numberOfTracks > 1 else ""), f"{audioPIDValue} - {audioDesc} - {audioLang}")) else: info.append(formatLine("P1", _("Audio PID"), _("N/A"))) info.append(formatLine("P1", _("PCR PID"), formatHex(getServiceInfoValue(iServiceInformation.sPCRPID)))) info.append(formatLine("P1", _("PMT PID"), formatHex(getServiceInfoValue(iServiceInformation.sPMTPID)))) info.append(formatLine("P1", _("TXT PID"), formatHex(getServiceInfoValue(iServiceInformation.sTXTPID)))) getSubtitleList() return info def showTransponderInformation(self): def getValue(key, default): valueLive = frontendLive.get(key, default) valueConfig = frontendConfig.get(key, default) return valueLive if valueLive == valueConfig else f"{valueLive} ({valueConfig})" def getDVBCFrequencyValue(): valueLive = frontendLive.get("frequency", 0) / 1000.0 valueConfig = frontendConfig.get("frequency", 0) / 1000.0 return f"{valueLive:.3f} {mhz}" if valueLive == valueConfig else f"{valueLive:.3f} {mhz} ({valueConfig:.3f} {mhz})" def getSymbolRateValue(): valueLive = frontendLive.get("symbol_rate", 0) // 1000 valueConfig = frontendConfig.get("symbol_rate", 0) // 1000 return f"{valueLive} {_('KSymb/s')}" if valueLive == valueConfig else f"{valueLive} {_('KSymb/s')} ({valueConfig} {_('KSymb/s')})" def getDVBSFrequencyValue(): valueLive = frontendLive.get("frequency", 0) // 1000 valueConfig = frontendConfig.get("frequency", 0) // 1000 return f"{valueLive} {mhz}" if valueLive == valueConfig else f"{valueLive} {mhz} ({valueConfig} {mhz})" def getInputStreamID(): valueLive = frontendLive.get("is_id", -1) if valueLive == -1: valueLive = na valueConfig = frontendConfig.get("is_id", -1) if valueConfig == -1: valueConfig = na return valueLive if valueLive == valueConfig else f"{valueLive} ({valueConfig})" def getFrequencyValue(): valueLive = frontendLive.get("frequency", 0) / 1000000.0 valueConfig = frontendConfig.get("frequency", 0) / 1000000.0 return f"{valueLive:.3f} {mhz}" if valueLive == valueConfig else f"{valueLive:.3f} {mhz} ({valueConfig:.3f} {mhz})" info = [] info.append(formatLine("H", _("Transponder information for '%s'") % self.serviceName)) info.append("") if self.frontendInfo: frontendLive = self.frontendInfo and self.frontendInfo.getAll(False) frontendConfig = self.frontendInfo and self.frontendInfo.getAll(True) else: frontendLive = self.transponderInfo frontendConfig = self.transponderInfo if frontendLive and len(frontendLive) and frontendConfig and len(frontendConfig): tunerType = frontendLive["tuner_type"] frontendLive = ConvertToHumanReadable(frontendLive) frontendConfig = ConvertToHumanReadable(frontendConfig) na = _("N/A") mhz = _("MHz") if not self.transponderInfo: info.append(formatLine("P1", _("NIM"), f"{chr(ord('A') + frontendLive.get('tuner_number', 0))}")) info.append(formatLine("P1", _("Type"), f"{frontendLive.get('tuner_type', na)} [{tunerType}]")) if tunerType == "DVB-C": info.append(formatLine("P1", _("Modulation"), getValue("modulation", na))) info.append(formatLine("P1", _("Frequency"), getDVBCFrequencyValue())) info.append(formatLine("P1", _("Symbol rate"), getSymbolRateValue())) info.append(formatLine("P1", _("Forward Error Correction (FEC)"), getValue("fec_inner", na))) info.append(formatLine("P1", _("Inversion"), getValue("inversion", na))) elif tunerType == "DVB-S": info.append(formatLine("P1", _("System"), getValue("system", na))) info.append(formatLine("P1", _("Modulation"), getValue("modulation", na))) info.append(formatLine("P1", _("Orbital position"), getValue("orbital_position", na))) info.append(formatLine("P1", _("Frequency"), getDVBSFrequencyValue())) info.append(formatLine("P1", _("Polarization"), getValue("polarization", na))) info.append(formatLine("P1", _("Symbol rate"), getSymbolRateValue())) info.append(formatLine("P1", _("Forward Error Correction (FEC)"), getValue("fec_inner", na))) info.append(formatLine("P1", _("Inversion"), getValue("inversion", na))) info.append(formatLine("P1", _("Pilot"), getValue("pilot", na))) info.append(formatLine("P1", _("Roll-off"), getValue("rolloff", na))) info.append(formatLine("P1", _("Input Stream ID"), getInputStreamID())) info.append(formatLine("P1", _("PLS Mode"), getValue("pls_mode", na))) info.append(formatLine("P1", _("PLS Code"), getValue("pls_code", 0))) valueLive = frontendLive.get("t2mi_plp_id", -1) valueConfig = frontendConfig.get("t2mi_plp_id", -1) if valueLive != -1 or valueConfig != -1: info.append(formatLine("P1", _("T2MI PLP ID"), f"{valueLive}" if valueLive == valueConfig else f"{valueLive} ({valueConfig})")) valueLive = None if frontendLive.get("t2mi_plp_id", -1) == -1 else frontendLive.get("t2mi_pid", eDVBFrontendParametersSatellite.T2MI_Default_Pid) valueConfig = None if frontendConfig.get("t2mi_plp_id", -1) == -1 else frontendConfig.get("t2mi_pid", eDVBFrontendParametersSatellite.T2MI_Default_Pid) if valueLive or valueConfig: info.append(formatLine("P1", _("T2MI PID"), f"{valueLive or 'None'}" if valueLive == valueConfig else f"{valueLive or 'None'} ({valueConfig or 'None'})")) elif tunerType == "DVB-T": info.append(formatLine("P1", _("Frequency"), getFrequencyValue())) info.append(formatLine("P1", _("Channel"), getValue("channel", na))) info.append(formatLine("P1", _("Inversion"), getValue("inversion", na))) info.append(formatLine("P1", _("Bandwidth"), getValue("bandwidth", na))) info.append(formatLine("P1", _("Code rate LP"), getValue("code_rate_lp", na))) info.append(formatLine("P1", _("Code rate HP"), getValue("code_rate_hp", na))) info.append(formatLine("P1", _("Guard Interval"), getValue("guard_interval", na))) info.append(formatLine("P1", _("Constellation"), getValue("constellation", na))) info.append(formatLine("P1", _("Transmission mode"), getValue("transmission_mode", na))) info.append(formatLine("P1", _("Hierarchy info"), getValue("hierarchy_information", na))) elif tunerType == "ATSC": info.append(formatLine("P1", _("System"), getValue("system", na))) info.append(formatLine("P1", _("Modulation"), getValue("modulation", na))) info.append(formatLine("P1", _("Frequency"), getFrequencyValue())) info.append(formatLine("P1", _("Inversion"), getValue("inversion", na))) else: info.append(formatLine("M0", _("Tuner data is not available!"))) return info def showECMInformation(self): info = [] info.append(formatLine("H", _("ECM information for '%s'") % self.serviceName)) info.append("") if self.serviceInfo: from Tools.GetEcmInfo import getCaidData, GetEcmInfo ecmData = GetEcmInfo().getEcmData() for caID in sorted(set(self.serviceInfo.getInfoObject(iServiceInformation.sCAIDPIDs)), key=lambda x: (x[0], x[1])): description = _("Undefined") extraInfo = "" provid = "" for caidEntry in getCaidData(): if int(caidEntry[0], 16) <= caID[0] <= int(caidEntry[1], 16): description = caidEntry[2] break if caID[2]: if description == "Seca": provid = ",".join([caID[2][y:y + 4] for y in range(len(caID[2]), 30)]) elif description == "Nagra": provid = caID[2][-4:] elif description == "Via": provid = caID[2][-6:] if provid: extraInfo = f" provid={provid}" else: extraInfo = f" extra={caID[2]}" active = f" ({_('Active')})" if caID[0] == int(ecmData[1], 16) and (caID[1] == int(ecmData[3], 16) or str(int(ecmData[2], 16)) in provid) else "" info.append(formatLine("P1", f"ECMPid {caID[1]:04X} ({caID[1]})", f"{caID[0]:04X}-{description}{extraInfo}{active}")) if len(info) == 2: info.append(formatLine("P1", _("No ECM PIDs available"), _("Free to Air (FTA) Service"))) return info def getSummaryInformation(self): return "Service Information" class StorageInformation(InformationBase): def __init__(self, session): InformationBase.__init__(self, session) self.setTitle(_("Storage / Disk Information")) self.skinName.insert(0, "StorageDiskInformation") self["information"].setText(_("Retrieving network server information, please wait...")) self.mountInfo = [] def fetchInformation(self): self.informationTimer.stop() self.console.ePopen("df -mh | grep -v '^Filesystem'", self.fetchComplete) for callback in self.onInformationUpdated: if callable(callback): callback() def fetchComplete(self, result, retVal, extraArgs=None): self.mountInfo = [] previousLine = None for line in [x.strip() for x in result.split("\n")]: if "%" in line: if previousLine: line = f"{previousLine} {line}" previousLine = None if line.startswith("//"): line = line[::-1] mount, other = line.split(" %") percent, free, used, total, device = other.split(None, 4) self.mountInfo.append([device[::-1], total[::-1], used[::-1], free[::-1], f"{percent[::-1]}%", mount[::-1]]) else: previousLine = line if isdir("/media/autofs"): for entry in sorted(listdir("/media/autofs")): path = join("/media/autofs", entry) keep = True for data in self.mountInfo: if data[5] == path: keep = False break if keep: self.mountInfo.append(["", 0, 0, 0, "N/A", path]) for callback in self.onInformationUpdated: if callable(callback): callback() def displayInformation(self): info = [] info.append(formatLine("H", _("Storage / Disk information for %s %s") % getBoxDisplayName())) info.append("") partitions = sorted(harddiskmanager.getMountedPartitions(), key=lambda partitions: partitions.device or "") for partition in partitions: if partition.mountpoint == "/": info.append(formatLine("S1", "/dev/root", partition.description)) stat = statvfs("/") diskSize = stat.f_blocks * stat.f_frsize diskFree = stat.f_bfree * stat.f_frsize diskUsed = diskSize - diskFree info.append(formatLine("P2", _("Mount point"), partition.mountpoint)) info.append(formatLine("P2", _("Size"), f"{scaleNumber(diskSize)} ({scaleNumber(diskSize, 'Iec')})")) info.append(formatLine("P2", _("Used"), f"{scaleNumber(diskUsed)} ({scaleNumber(diskUsed, 'Iec')})")) info.append(formatLine("P2", _("Free"), f"{scaleNumber(diskFree)} ({scaleNumber(diskFree, 'Iec')})")) break # hddList = storageManager.HDDList() hddList = harddiskmanager.HDDList() if hddList: for hdd in hddList: hdd = hdd[1] info.append("") info.append(formatLine("S1", hdd.getDeviceName(), hdd.bus())) info.append(formatLine("P2", _("Model"), hdd.model())) diskSize = hdd.diskSize() * 1000000 info.append(formatLine("P2", _("Size"), f"{scaleNumber(diskSize)} ({scaleNumber(diskSize, 'Iec')})")) info.append(formatLine("P2", _("Sleeping"), (_("Yes") if hdd.isSleeping() else _("No")))) for partition in partitions: if partition.device and join("/dev", partition.device).startswith(hdd.getDeviceName()): info.append(formatLine("P2", _("Partition"), partition.device)) stat = statvfs(partition.mountpoint) diskSize = stat.f_blocks * stat.f_frsize diskFree = stat.f_bfree * stat.f_frsize diskUsed = diskSize - diskFree info.append(formatLine("P3", _("Mount point"), partition.mountpoint)) info.append(formatLine("P3", _("Size"), f"{scaleNumber(diskSize)} ({scaleNumber(diskSize, 'Iec')})")) info.append(formatLine("P3", _("Used"), f"{scaleNumber(diskUsed)} ({scaleNumber(diskUsed, 'Iec')})")) info.append(formatLine("P3", _("Free"), f"{scaleNumber(diskFree)} ({scaleNumber(diskFree, 'Iec')})")) else: info.append("") info.append(formatLine("S1", _("No storage or hard disks detected."))) info.append("") info.append(formatLine("H", f"{_('Network storage on')} {DISPLAY_BRAND} {DISPLAY_MODEL}")) info.append("") if self.mountInfo: count = 0 for data in self.mountInfo: if count: info.append("") info.append(formatLine("S1", data[5])) if data[0]: info.append(formatLine("P2", _("Network address"), data[0])) info.append(formatLine("P2", _("Size"), data[1])) info.append(formatLine("P2", _("Used"), f"{data[2]} ({data[4]})")) info.append(formatLine("P2", _("Free"), data[3])) else: info.append(formatLine("P2", _("Not currently mounted."))) count += 1 else: info.append(formatLine("S1", _("No network storage detected."))) self["information"].setText("\n".join(info)) class StreamingInformation(InformationBase): def __init__(self, session): InformationBase.__init__(self, session) self.setTitle(_("Streaming Tuner Information")) self.skinName.insert(0, "StreamingInformation") self["key_yellow"] = StaticText(_("Stop Auto Refresh")) self["key_blue"] = StaticText() self["refreshActions"] = HelpableActionMap(self, ["ColorActions"], { "yellow": (self.toggleAutoRefresh, _("Toggle auto refresh On/Off")) }, prio=0, description=_("Streaming Information Actions")) self["streamActions"] = HelpableActionMap(self, ["ColorActions"], { "blue": (self.stopStreams, _("Stop streams")) }, prio=0, description=_("Streaming Information Actions")) self["streamActions"].setEnabled(False) self.autoRefresh = True def toggleAutoRefresh(self): self.autoRefresh = not self.autoRefresh self["key_yellow"].setText(_("Stop Auto Refresh") if self.autoRefresh else _("Start Auto Refresh")) def stopStreams(self): if eStreamServer.getInstance().getConnectedClients(): eStreamServer.getInstance().stopStream() if eRTSPStreamServer.getInstance().getConnectedClients(): eRTSPStreamServer.getInstance().stopStream() def displayInformation(self): info = [] info.append(formatLine("H", _("Streaming tuner information for %s %s") % getBoxDisplayName())) info.append("") clientList = eStreamServer.getInstance().getConnectedClients() + eRTSPStreamServer.getInstance().getConnectedClients() if clientList: self["key_blue"].setText(_("Stop Streams")) self["streamActions"].setEnabled(True) for count, client in enumerate(clientList): # print("[Information] DEBUG: Client data '%s'." % str(client)) if count: info.append("") info.append(formatLine("S", f"{_('Client')} - {count + 1}")) info.append(formatLine("P1", _("Service reference"), client[1])) info.append(formatLine("P1", _("Service name"), ServiceReference(client[1]).getServiceName() or _("Unknown service!"))) info.append(formatLine("P1", _("IP address"), client[0][7:] if client[0].startswith("::ffff:") else client[0])) info.append(formatLine("P1", _("Transcoding"), _("Yes") if client[2] else _("No"))) else: self["key_blue"].setText("") self["streamActions"].setEnabled(False) info.append(formatLine("P1", _("No tuners are currently streaming."))) self["information"].setText("\n".join(info)) if self.autoRefresh: self.informationTimer.start(AUTO_REFRESH_TIME) def getSummaryInformation(self): return "Streaming Tuner Information" class SystemInformation(InformationBase): def __init__(self, session): InformationBase.__init__(self, session) self.baseTitle = _("System Information") self.setTitle(self.baseTitle) self.skinName.insert(0, "SystemInformation") self["key_menu"] = StaticText(_("MENU")) self["key_yellow"] = StaticText() self["key_blue"] = StaticText() self["systemActions"] = HelpableActionMap(self, ["MenuActions", "ColorActions", "NavigationActions"], { "menu": (self.showSystemMenu, _("Show selection for system information screen")), "yellow": (self.previousSystem, _("Show previous system information screen")), "blue": (self.nextSystem, _("Show next system information screen")), "left": (self.previousSystem, _("Show previous system information screen")), "right": (self.nextSystem, _("Show next system information screen")) }, prio=0, description=_("System Information Actions")) self.systemCommands = [ ("CPU", None, "/proc/cpuinfo"), ("Top Processes", ("/usr/bin/top", "/usr/bin/top", "-b", "-n", "1"), None), ("Current Processes", ("/bin/ps", "/bin/ps", "-l"), None), ("Kernel Modules", None, "/proc/modules"), ("Kernel Messages", ("/bin/dmesg", "/bin/dmesg"), None), ("System Messages", None, "/var/volatile/log/messages"), ("Network Interfaces", ("/sbin/ifconfig", "/sbin/ifconfig"), None), ("Disk Usage", ("/bin/df", "/bin/df", "-h"), None), ("Mounted Volumes", ("/bin/mount", "/bin/mount"), None), ("Partition Table", None, "/proc/partitions") ] edidPath = eAVControl.getInstance().getEDIDPath() if edidPath: self.systemCommands.append(("EDID", ("/usr/bin/edid-decode", "/usr/bin/edid-decode", edidPath), None)) self.systemCommandsIndex = 0 self.systemCommandsMax = len(self.systemCommands) self.info = None def showSystemMenu(self): def showSystemMenuCallBack(selectedIndex): if isinstance(selectedIndex, int): self.systemCommandsIndex = selectedIndex self.displayInformation() self.informationTimer.start(25) choices = [(systemCommand[0], index) for index, systemCommand in enumerate(self.systemCommands)] self.session.openWithCallback(showSystemMenuCallBack, MessageBox, text=_("Select system information to view:"), list=choices, windowTitle=self.baseTitle) def previousSystem(self): self.systemCommandsIndex = (self.systemCommandsIndex - 1) % self.systemCommandsMax self.displayInformation() self.informationTimer.start(25) def nextSystem(self): self.systemCommandsIndex = (self.systemCommandsIndex + 1) % self.systemCommandsMax self.displayInformation() self.informationTimer.start(25) def fetchInformation(self): def fetchInformationCallback(result, retVal, extraArgs): self.info = [x.rstrip() for x in result.split("\n")] for callback in self.onInformationUpdated: if callable(callback): callback() self.informationTimer.stop() name, command, path = self.systemCommands[self.systemCommandsIndex] self.info = None if command: self.console.ePopen(command, fetchInformationCallback) elif path: try: with open(path) as fd: self.info = [x.strip() for x in fd.readlines()] except OSError as err: self.info = [_("Error %d: System information file '%s' can't be read! (%s)") % (err.errno, path, err.strerror)] for callback in self.onInformationUpdated: if callable(callback): callback() def displayInformation(self): name, command, path = self.systemCommands[self.systemCommandsIndex] self.setTitle(f"{self.baseTitle}: {name}") self["key_yellow"].setText(self.systemCommands[(self.systemCommandsIndex - 1) % self.systemCommandsMax][0]) self["key_blue"].setText(self.systemCommands[(self.systemCommandsIndex + 1) % self.systemCommandsMax][0]) info = [_("Retrieving '%s' information, please wait...") % name] if self.info is None else self.info if info == [""]: info = [_("There is no information to show for '%s'.") % name] self["information"].setText("\n".join(info)) def getSummaryInformation(self): return "System Information" class TranslationInformation(InformationBase): def __init__(self, session): InformationBase.__init__(self, session) self.setTitle(_("Translation Information")) self.skinName.insert(0, "TranslationInformation") def displayInformation(self): info = [] info.append(formatLine("H", _("Translation information for %s %s") % getBoxDisplayName())) info.append("") translateInfo = _("TRANSLATOR_INFO") if translateInfo != "TRANSLATOR_INFO": info.append(formatLine("H", _("Translation information"))) info.append("") translateInfo = translateInfo.split("\n") for translate in translateInfo: info.append(formatLine("P1", translate)) info.append("") translateInfo = _("").split("\n") # This is deliberate to dump the translation information. for translate in translateInfo: if not translate: continue translate = [x.strip() for x in translate.split(":", 1)] if len(translate) == 1: translate.append("") info.append(formatLine("P1", translate[0], translate[1])) self["information"].setText("\n".join(info)) def getSummaryInformation(self): return "Translation Information" class TunerInformation(InformationBase): def __init__(self, session): InformationBase.__init__(self, session) self.setTitle(_("Tuner Information")) self.skinName.insert(0, "TunerInformation") self.frontEndFields = { "DVB API version": "api", "Name": "name", "Frequency": "frequency", "Symbolrate": "symbolrate", "Capabilities": "capabilities", "Delivery Systems": "delivery" } self.broadcasts = ("DVB-S", "DVB-S2", "DVB-C", "DVB-T", "DVB-T2", "ATSC", "ISDB-T", "DTMB") self.tunerList = [] def fetchInformation(self): self.informationTimer.stop() self.tunerList = [] curIndex = -1 for count, nim in enumerate(nimmanager.nimList()): tunerData = {} tuner, model = (x.strip() for x in nim.split(":", 1)) tuner = tuner.strip("Tuner").strip() if self.tunerList and self.tunerList[curIndex]["model"] == model: self.tunerList[curIndex]["end"] = tuner continue curIndex += 1 tunerData["start"] = tuner tunerData["end"] = tuner tunerData["model"] = model for key, value in [(x.strip(), y.strip()) for x, y in [x.split(":", 1) for x in eDVBResourceManager.getInstance().getFrontendCapabilities(count).splitlines()]]: if key in self.frontEndFields: tunerData[self.frontEndFields[key]] = value else: print(f"[Information] Note: Unexpected field '{key}' in front-end with data '{value}'!") if tunerData.get("delivery"): broadcasts = [] for broadcast in self.broadcasts: if broadcast.replace("-", "") in tunerData["delivery"]: broadcasts.append(broadcast) if broadcasts: tunerData["broadcast"] = ", ".join(broadcasts) self.tunerList.append(tunerData) for callback in self.onInformationUpdated: if callable(callback): callback() def displayInformation(self): def parseValues(data): values = {} for item in data.split(","): key, value = item.split("=", 1) values[key] = formatNumber(value) return values def formatNumber(number): number = number.strip() value, units = number.split(maxsplit=1) if " " in number else (number, None) if "." in value: format = "%.3f" value = float(value) else: format = "%d" value = int(value) return f"{format_string(format, value, grouping=True)} {units}" if units else format_string(format, value, grouping=True) def extractModes(data, mode): values = [] if data: mode = f"{mode} " length = len(mode) for item in data.split(","): if item.startswith(mode): values.append(item[length:].capitalize()) return sorted(values) def sortQAM(values): if "Auto" in values: values.remove("Auto") addAuto = True else: addAuto = False values = [str(x) for x in sorted([int(x) for x in values])] if addAuto: values.append("Auto") return values info = [] info.append(formatLine("H", _("Tuner information for %s %s") % getBoxDisplayName())) info.append("") for count, tunerData in enumerate(self.tunerList): if count: info.append("") tuner = tunerData["start"] if tunerData["start"] == tunerData["end"] else f"{tunerData['start']} - {tunerData['end']}" info.append(formatLine("S", f"Tuner {tuner}")) if self.extraSpacing: info.append("") name = tunerData.get("name") if name: info.append(formatLine("P1", _("Name"), name)) model = tunerData.get("model") if model: info.append(formatLine("P1", _("Type / Model"), model)) broadcast = tunerData.get("broadcast") if broadcast: info.append(formatLine("P1", _("Broadcast systems"), broadcast)) capabilities = tunerData.get("capabilities") if capabilities: info.append(formatLine("P1", _("Multistream"), (_("Yes") if "MULTISTREAM" in capabilities else _("No")))) frequency = tunerData.get("frequency") if frequency: data = parseValues(frequency) info.append(formatLine("P1", _("Frequency range"), f"{data['min']} - {data['max']} (Step {data['stepsize']})")) symbolrate = tunerData.get("symbolrate") if symbolrate: data = parseValues(symbolrate) info.append(formatLine("P1", _("Symbol rate"), f"{data['min']} - {data['max']}")) FEC = extractModes(capabilities, "FEC") if FEC: info.append(formatLine("P1", _("FEC modes"), ", ".join(FEC))) QAM = sortQAM(extractModes(capabilities, "QAM")) if QAM: info.append(formatLine("P1", _("Modulation modes"), ", ".join(QAM))) api = tunerData.get("api") if api: info.append(formatLine("P1", _("%s version") % "DVB API", api)) if info: info.append("") info.append(formatLine("S", _("Transcoding"), (_("Yes") if BoxInfo.getItem("transcoding") else _("No")))) info.append(formatLine("S", _("MultiTranscoding"), (_("Yes") if BoxInfo.getItem("multitranscoding") else _("No")))) self["information"].setText("\n".join(info)) def getSummaryInformation(self): return "Tuner Information" class TestingInformation(InformationBase): def __init__(self, session): InformationBase.__init__(self, session) self.setTitle(_("Testing Information")) self.skinName.insert(0, "TestingInformation") self.slotImages = None def displayInformation(self): html = remoteControl.getOpenWebifHTML() if html is None: html = "OpenWebif HTML file isn't available." self["information"].setText(html) # info = [] # for index in range(1, 24): # info.append("This is test line %d." % index) # self["information"].setText("\n".join(info)) def getSummaryInformation(self): return "Testing Information Data" class InformationSummary(ScreenSummary): def __init__(self, session, parent): ScreenSummary.__init__(self, session, parent=parent) self.parent = parent self["information"] = StaticText() parent.onInformationUpdated.append(self.updateSummary) # self.updateSummary() def updateSummary(self): self["information"].setText(self.parent.getSummaryInformation())