From 81958559d95a169b9a5748ddefbe1148dd559089 Mon Sep 17 00:00:00 2001 From: khayber Date: Thu, 26 Jan 2006 15:21:57 +0000 Subject: [PATCH] Oops, some previously added files not added to svn (I hate it when that happens) Fix from Thomas Leonard for acpi.py reporting >100% charge --- Messages/dist | 8 + Messages/make_tips | 49 +++++ Messages/update-po | 16 ++ acpi.py | 569 +++++++++++++++++++++++++++++++++++++++++++++++++++++ pmu.py | 157 +++++++++++++++ 5 files changed, 799 insertions(+) create mode 100755 Messages/dist create mode 100755 Messages/make_tips create mode 100755 Messages/update-po create mode 100644 acpi.py create mode 100644 pmu.py diff --git a/Messages/dist b/Messages/dist new file mode 100755 index 0000000..a637e09 --- /dev/null +++ b/Messages/dist @@ -0,0 +1,8 @@ +#!/bin/sh + +echo Making all .mo files... +echo + +for FILE in *.po; do + msgfmt $FILE -o ${FILE%.po}.gmo && echo Created file $OUT OK +done diff --git a/Messages/make_tips b/Messages/make_tips new file mode 100755 index 0000000..6d5a13c --- /dev/null +++ b/Messages/make_tips @@ -0,0 +1,49 @@ +#!/usr/bin/env python + +from xmllib import * +import string, os + +print "Extracting translatable bits from Options.xml..." + +class Parser(XMLParser): + data = "" + + def unknown_starttag(self, tag, attrs): + for x in ['title', 'label', 'end', 'unit']: + if attrs.has_key(x): + self.trans(attrs[x]) + self.data = "" + + def handle_data(self, data): + self.data = self.data + data + + def unknown_endtag(self, tag): + data = string.strip(self.data) + if data: + self.trans(data) + + def trans(self, data): + data = string.join(string.split(data, '\n'), '\\n') + if data: + out.write('_("%s")\n' % data) + +try: + os.chdir("po") +except OSError: + pass + +file = open('../Options.xml', 'rb') +out = open('../tips', 'wb') +parser = Parser() +parser.feed(file.read()) +file.close() +parser.close() +out.close() + +# file = open('../Mixer.xml', 'rb') +# out = open('../tips', 'wb') +# parser = Parser() +# parser.feed(file.read()) +# file.close() +# parser.close() +# out.close() diff --git a/Messages/update-po b/Messages/update-po new file mode 100755 index 0000000..300a331 --- /dev/null +++ b/Messages/update-po @@ -0,0 +1,16 @@ +#!/bin/sh + +echo Extracting messages from source files... +echo + +(cd ..; pygettext *.py AppRun) + +echo +echo Updating all .po files... +echo + +for FILE in *.po; do + echo -n "Updating '$FILE' translation" + mv $FILE $FILE.old + msgmerge $FILE.old ../messages.pot > $FILE +done diff --git a/acpi.py b/acpi.py new file mode 100644 index 0000000..383caf5 --- /dev/null +++ b/acpi.py @@ -0,0 +1,569 @@ +############################################################################## +## +## $Id: acpi.py,v 1.23 2003/12/15 20:04:09 riemer Exp $ +## +## Copyright (C) 2002-2003 Tilo Riemer , +## Luc Sorgue and +## Rds +## All rights reserved. +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## +## 1. Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## 2. Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## 3. The name of the author may not be used to endorse or promote products +## derived from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +## IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +## OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +## IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +## INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +## NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +## THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +############################################################################### + +import os,stat,sys + + +# genenal constants ########################################################### + +#enums + +VERSION = "0.3.0" + +OFFLINE = 0 +ONLINE = 1 +CHARGING = 2 #implies ONLINE +FAN_OFF = 0 +FAN_ON = 1 + + + +# exceptions ################################################################## + +#exception enums +ERR_GENERIC = -1 +ERR_NO_DEVICE = -2 +ERR_NOT_IMPLEMENTED = -3 +ERR_NO_LOW_LEVEL = -4 +ERR_CONFIGURATION_CHANGED = -5 #reload module? or function reconfigure? +ERR_NOT_ALLOWED = -6 #access for some resource not allowed --> better idea? + +class AcpiError(Exception): + """ACPI exceptions""" + + def __init__(self, errno): + self.errno = errno + + def __str__(self): + if self.errno == ERR_GENERIC: + return "Any ACPI error occured." + elif self.errno == ERR_NO_DEVICE: + return "ACPI is not configured on this host." + elif self.errno == ERR_NOT_IMPLEMENTED: + return "No implementation for this operating system." + elif self.errno == ERR_NO_LOW_LEVEL: + return "Acpi_lowlevel module not found." + elif self.errno == ERR_CONFIGURATION_CHANGED: + return "ACPI configuartion has been changed." + else: + return "Unknown error occured." + + + +# interface ################################################################### + +class Acpi: + """Interface class for ACPI""" + + def __init__(self): + res = sys.platform + if res.find("freebsd4") > -1: + self.acpi = None #throw exception + raise NotImplemented + + elif res.find("netbsd1") > -1: + self.acpi = None #throw exception + raise NotImplemented + + elif res.find("linux2") > -1: + self.acpi = AcpiLinux() + + elif res.find("linux") > -1: + #some systems return linux instead of linux2. We should + #show a warning or check by ourselves for Linux2 + self.acpi = AcpiLinux() + + else: + self.acpi = None #throw exception (os unknown) + raise NotImplemented + + + def identity(self): + """Returns the identity of this module""" + return "acpi.py" + + def version(self): + """Returns the version of this module""" + return VERSION + + def update(self): + """Updates the ACPI state""" + self.acpi.update() + + def percent(self): + """Returns percentage capacity of all batteries""" + return self.acpi.percent() + + def capacity(self): + """Returns capacity of all batteries (in mWh)""" + return self.acpi.capacity() + + def nb_of_batteries(self): + """Returns the number of batteries""" + return self.acpi.nb_of_batteries() + + def charging_state(self): + """Returns ac state (off-/online/charging)""" + return self.acpi.charging_state() + + def estimated_lifetime(self): + """Returns Estimated Lifetime as real number""" + return self.acpi.estimated_lifetime() + + def temperature(self, idx): + """Returns Processor Temperature""" + return self.acpi.temperature(idx) + + def fan_state(self, idx): + """Returns fan states""" + return self.acpi.fan_state(idx) + + +#unfinished: don't use it or better send us improvements ;-) + def frequency(self, idx): + """ Return the frequency of the processor""" + return self.acpi.frequency(idx) + + def performance_states(self, idx): + """ Return a list of available frequencies for the proc """ + return self.acpi.performance_states(idx) + + def set_frequency(self, f): + """ Set the processor frequency - Warning ! Needs root privileges to work """ + return self.acpi.set_frequency(f) + + + +# implementation for Linux #################################################### + +class AcpiLinux: + def __init__(self): + """init ACPI class and check for any ACPI features in /proc/acpi/""" + + #we read all acpi stuff from here + self.proc_acpi_dir = "/proc/acpi" + + if not os.access(self.proc_acpi_dir, os.F_OK): + raise EnvironmentError + + self.init_batteries() + self.init_fans() + #self.init_processors() + self.init_temperatures() + + self.update() + + + def update(self): + """Read current states of supported acpi components""" + + self.update_batteries() + self.update_fans() + #self.update_processors() + self.update_temperatures() + + # battery related functions + def init_batteries(self): + """Checks for and initializes the batteries""" + + self.proc_battery_dir = self.proc_acpi_dir + "/battery" + + # empty lists implies no battery, no capacity etc. + self.design_capacity = {} + self.life_capacity = {} + self.present_rate = {} + + # empty list of battery sub directories; implies no batteries available + self.battery_dir_entries = [] + + try: + battery_dir_entries = os.listdir(self.proc_battery_dir) + except OSError: + self.ac_line_state = ONLINE # no batteries: we assume that a cable is plugged in ;-) + return #nothing more to do + + + try: + for i in battery_dir_entries: + mode = os.stat(self.proc_battery_dir + "/" + i)[stat.ST_MODE] + if stat.S_ISDIR(mode): + self.battery_dir_entries.append(i) + except OSError: + # the battery module is not correctly loaded, or is broken. + # currently self.battery_dir_entries has no batteries or only + # the batteries which we could stat + # because the appended dirs should be okay we do not return here + pass + + self.ac_line_state = OFFLINE + +#later: the newer acpi versions seems to generate always two BAT dirs... +#check info for present: no + try: + for i in self.battery_dir_entries: + #print self.proc_battery_dir + "/" + i + "/info" + info_file = open(self.proc_battery_dir + "/" + i + "/info") + line = info_file.readline() + + while len(line) != 0: + if line.find("design capacity:") == 0: + cap = line.split(":")[1].strip() + try: + self.design_capacity[i] = int(cap.split("m")[0].strip()) + except ValueError: + #no value --> conversion to int failed + self.design_capacity[i] = 0 + + line = info_file.readline() + info_file.close() + except IOError: + #print "No batt info found." + # the battery module is not correctly loaded... the file info should exist. + # wipe out all lists --> no battery infos + self.battery_dir_entries = [] + self.design_capacity = {} + self.life_capacity = {} + self.present_rate = {} + + + def update_batteries(self): + """Read current state of batteries""" + + try: + for i in self.battery_dir_entries: + state_file = open(self.proc_battery_dir + "/" + i + "/state") + line = state_file.readline() + + while len(line) != 0: + if line.find("remaining capacity") == 0: + cap = line.split(":")[1].strip() + try: + self.life_capacity[i] = int(cap.split("m")[0].strip()) + except ValueError: + self.life_capacity[i] = 0 + +# it's possible that in battery/*/info the charging state is unknown +# --> then we must check ac_state... +# iterating over all batteries this way is not smart. better implementation needed +# better are funcs for capacity, acstate and prrate + + # a little bit tricky... if loading of ac driver fails, we cant use info + # from /proc/ac_*/... + # if information in /proc/acpi/battery/*/state is wrong we had to + # track the capacity history. + # I assume that all battery state files get the same state. + if line.find("charging state") == 0: + state = line.split(":")[1].strip() + if state == "discharging": + self.ac_line_state = OFFLINE + elif state == "charging": + self.ac_line_state = CHARGING + else: + self.ac_line_state = ONLINE + + # Read the present energy consumption to + # estimate life time + + if line.find("present rate:") == 0: + try: + pr_rate = float(line.split(":")[1].strip().split("m")[0].strip()) + except ValueError: + pr_rate = 0 + + self.present_rate[i] = pr_rate + + line = state_file.readline() + state_file.close() + except IOError: + raise AcpiError, ERR_CONFIGURATION_CHANGED + + # maybe we should restart init_batteries instead of generating an error ? + # the user may have unplugged the battery. + #init_batteries() + # I prefer raising an exception because we would run into a recursion of + # member funcs what is not a good idea. + # the case that this error occurs should be very rare + + + def init_temperatures(self): + """Initializes temperature stuff""" + + self.proc_thermal_dir = self.proc_acpi_dir + "/thermal_zone" + + # empty list implies no thermal feature supported + self.temperatures = {} + + # empty list of thermal sub directories; implies no thermal infos available + self.thermal_dir_entries = [] + + try: + thermal_dir_entries = os.listdir(self.proc_thermal_dir) + except OSError: + return #nothing more to do + + try: + for i in thermal_dir_entries: + mode = os.stat(self.proc_thermal_dir + "/" + i)[stat.ST_MODE] + if stat.S_ISDIR(mode): + self.thermal_dir_entries.append(i) + except OSError: + # the thermal module is not correctly loaded, or is broken. + # because the appended dirs should be okay we do not return here + pass + + + def update_temperatures(self): + """Read current temperatures""" + + try: + for i in self.thermal_dir_entries: + file = open(self.proc_thermal_dir + "/" + i + "/temperature") + line = file.readline() + while len(line) != 0: + if line.find("temperature") == 0: + self.temperatures[i] = line.split(":")[1].strip() + line = file.readline() + file.close() + except IOError: + raise AcpiError,ERR_CONFIGURATION_CHANGED + + + def init_fans(self): + """Initialize fans""" + + self.proc_fan_dir = self.proc_acpi_dir + "/fan" + + self.fans = {} + + # empty list of fan sub directories; implies no fan infos available + self.fan_dir_entries = [] + + try: + fan_dir_entries = os.listdir(self.proc_fan_dir) + except OSError: + return #nothing more to do + + try: + for i in fan_dir_entries: + mode = os.stat(self.proc_fan_dir + "/" + i)[stat.ST_MODE] + if stat.S_ISDIR(mode): + self.fan_dir_entries.append(i) + except OSError: + # the fan module is not correctly loaded, or is broken. + # because the appended dirs should be okay we do not return here + pass + + + def update_fans(self): + """Read current state of fans""" + + try: + for i in self.fan_dir_entries: + file = open(self.proc_fan_dir + "/" + i + "/state") + line = file.readline() + while len(line) != 0: + if line.find("status") == 0: + if line.split(":")[1].strip() == 'on': + self.fans[i] = FAN_ON + else: + self.fans[i] = FAN_OFF + line = file.readline() + file.close() + except IOError: + raise AcpiError,ERR_CONFIGURATION_CHANGED + + + def init_processors(self): + """Initialize processors""" + + self.proc_processor_dir = self.proc_acpi_dir + "/processor" + +# TODO: adapt it for multiple CPUs --> we need a matrix instead of a vector!!! + self.perf_states = {} #empty list implies no processor support + + # empty list of processor sub directories; implies no processor infos available + self.processor_dir_entries = [] + + try: + processor_dir_entries = os.listdir(self.proc_processor_dir) + except OSError: + return #nothing more to do + + try: + for i in processor_dir_entries: + mode = os.stat(self.proc_processor_dir + "/" + i)[stat.ST_MODE] + if stat.S_ISDIR(mode): + self.processor_dir_entries.append(i) + except OSError: + # the processor module is not correctly loaded, or is broken. + # because the appended dirs should be okay we do not return here + pass + + try: + for i in self.processor_dir_entries: + file = open(self.proc_processor_dir + "/" + i + "/performance") + line = file.readline() + while(len(line)!=0): + if line.find("MHz") > -1: + state = line.split(":")[0].strip().split("P")[-1] + freq = line.split(":")[1].split(",")[0].strip() + self.perf_states[freq] = state + line = file.readline() + file.close() + except IOError: + self.processor_dir_entries = [] + self.perf_states = {} #reset list --> should we throw an exception? No! + return + + + def update_processors(self): + """Read current state of processors""" + + try: + for i in self.processor_dir_entries: + file = open(self.proc_processor_dir + "/" + i + "/performance") + line = file.readline() + + while(len(line)!=0): + if line.find("*") > -1: + self.freq = line.split(":")[1].strip().split(",")[0] + line = f.readline() + file.close() + except IOError: + raise AcpiError,ERR_CONFIGURATION_CHANGED + + + def percent(self): + """Returns percentage capacity of all batteries""" + + life_capacity = 0 + design_capacity = 0 + for i,c in self.life_capacity.items(): + life_capacity = life_capacity + c + design_capacity = design_capacity + self.design_capacity[i] + + if design_capacity == 0: + return 0 + + # should we use try catch instead of the check above? + return (life_capacity * 100) / design_capacity + + + def capacity(self): + """Returns capacity of all batteries""" + capacity = 0 + for i,c in self.life_capacity.items(): + capacity = capacity + c + return capacity + + + def nb_of_batteries(self): + #returns the number of batteries + #if it returns 0, maybe ACPI is not available or + #battery driver is not loaded + return len(self.battery_dir_entries) + + + def charging_state(self): + return self.ac_line_state + + + def estimated_lifetime(self): + # what should we return if state==charging? + # it's not clean to return a time in one case and any + # English string in another case. + # The user can check for ac-state before call this func + + # fixed bug - if a battery is full and not discharging, it + # would not be counted in the estimated lifetime + # so, this new code totals the remaining capacity and rate + # from all the batteries, and divides + # however, if two or more batteries are discharging at + # different non-zero rates, I don't know how accurate this + # will be + # - QS Computing (postmaster@qscomputing.net) + + time = 0 + totalCapacity = 0 + totalRate = 0 + + for batt, capacity in self.life_capacity.items(): + totalCapacity += capacity + totalRate += self.present_rate[batt] + time = totalCapacity / totalRate + + return time + + + # we need funcs like max_temperature and average_temperature + def temperature(self, idx): + #print self.temperatures + #print self.thermal_dir_entries[idx] + return self.temperatures(self.thermal_dir_entries(idx)) + + + def fan_state(self, idx): + #print self.fans + return self.fans[self.fan_dir_entries[idx]] + + + def performance_states(self, idx): + return self.perf_states[idx].keys() + + + def frequency(self, idx): + #print self.freq + return self.freq[idx] + + +# TODO: adapt it for multiple CPUs + def set_frequency(self, f): + #I think we should throw exceptions if someone goes wrong here + + if self.perf_states.has_key(f): + state = self.perf_states[f] + try: + pr = os.listdir("/proc/acpi/processor")[0] + except OSError: + raise AcpiError, ERR_NOT_ALLOWED + + try: + f = open("/proc/acpi/processor/"+pr+"/performance","w") + except IOError: + raise AcpiError, ERR_NOT_ALLOWED + + f.write(state) + f.close() + else: + raise AcpiError, ERR_NOT_ALLOWED diff --git a/pmu.py b/pmu.py new file mode 100644 index 0000000..898891c --- /dev/null +++ b/pmu.py @@ -0,0 +1,157 @@ +############################################################################## +## +## $Id: pmu.py,v 1.2 2003/04/10 22:13:55 riemer Exp $ +## +## Copyright (C) 2002-2003 Tilo Riemer +## All rights reserved. +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## +## 1. Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## 2. Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## 3. The name of the author may not be used to endorse or promote products +## derived from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +## IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +## OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +## IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +## INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +## NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +## THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +## +############################################################################### + + + +import sys + + +#enums +OFFLINE = 0 +ONLINE = 1 +CHARGING = 2 #implies ONLINE +NOBATTERY = 3 + +UNKNOWN = -1 + +class Pmu: + """Interface class for PMU""" + + def __init__(self): + res = sys.platform + if res.find("linux2") > -1: + self.pmu = PmuLinux() + + else: + self.pmu = None #throw exception (os unknown) + raise NotImplemented + + + def update(self): + """Updates the PMU state""" + self.pmu.update() + + + def percent(self): + """Returns percentage capacity of all batteries""" + return self.pmu.percent() + + + def time(self): + """Returns time of all batteries (in minutes)""" + return self.pmu.time() + + + def charging_state(self): + """Returns ac state (off-/online/charging)""" + return self.pmu.charging_state() + + + + + +#base class for os dependent pmu classes + +class PmuBase: + def __init__(self): + self.ac_line_state = OFFLINE + + self.life_percent = 0 + self.life_time = 0 #0 seconds + + #initial reading of pmu info + self.update() + + def percent(self): + #returns percentage capacity of all batteries + return self.life_percent + + + def time(self): + #returns time of all batteries (in minutes) + return self.life_time + + + def charging_state(self): + return self.ac_line_state + + + + +#implementation for Linux +#if we want support more than battery info we need a low level module implemented using C + +class PmuLinux(PmuBase): + def update(self): + #read /proc/pmu and extract needed infos + + try: + pmu_proc = open("/proc/pmu/info") + except IOError: + raise EnvironmentError + + data = {} + lines = pmu_proc.readlines() + for line in lines: + token = line.split(':') + data[token[0].strip()] = token[1].strip() + + try: + pmu_battery = open("/proc/pmu/battery_0") + except: + self.ac_line_state = ONLINE + return + + lines = pmu_battery.readlines() + for line in lines: + try: + token = line.split(':') + data[token[0].strip()] = token[1].strip() + except: + pass + + if data['Battery count'] != "0" and data['AC Power'] == "1" and \ + (int(data['charge']) < int(data['max_charge'])): + self.ac_line_state = CHARGING + elif data['Battery count'] == "0" or data['AC Power'] == "1": + self.ac_line_state = ONLINE + else: + self.ac_line_state = OFFLINE + + self.life_percent = int(100 * int(data['charge']) / int(data['max_charge'])) + + self.life_time = int(int(data['time rem.']) / 60) + + + pmu_proc.close() + pmu_battery.close() + \ No newline at end of file -- 2.11.4.GIT