diff options
Diffstat (limited to '')
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | TODO | 9 | ||||
| -rw-r--r-- | src/plugins/alsa.py | 12 | ||||
| -rw-r--r-- | src/plugins/lid.py | 41 | ||||
| -rw-r--r-- | src/plugins/powersupply.py | 266 | 
5 files changed, 321 insertions, 9 deletions
@@ -38,7 +38,7 @@ PLUGINS = chase clock cpuinfo cpuonline cpu df discstats ipaddress  \            pacman snmp snmp6 softirqs solar uname uptime users       \            vmstat weather xdisplay xkb alsa dentrystate inodestate   \            files hdparm tzclock ropty ping inotify random swaps      \ -          locks image application menu ii +          locks image application menu ii powersupply lid  PLUGIN_EXAMPLES = chase clock cpu cpuinfo cpuonline df discstats   \                    ipaddress kmsg loadavg lunar mem moc network     \ @@ -5,8 +5,6 @@ List of plugins to implement:       portage       iostat [-x] [-d] [-N] -zk [-p | <devices>...] <interval>       SMART monitoring -     Battery -     /proc/acpi (mute)       System tray       Eyes       Taskbar (as in rarity) @@ -35,6 +33,11 @@ List of plugins to implement:       /proc/net/udp       /proc/net/unix       /proc/sysvipc +     /sys/class/rtc +     /sys/class/rfkill +     /sys/class/leds +     /sys/class/ieee80211 +     /sys/class/bluetooth  Demo plugins: @@ -43,6 +46,8 @@ Demo plugins:       weather       hdparm       ii +     powersupply +     lid  Terminal mode diff --git a/src/plugins/alsa.py b/src/plugins/alsa.py index 68d47bf..f624672 100644 --- a/src/plugins/alsa.py +++ b/src/plugins/alsa.py @@ -44,7 +44,7 @@ class ALSA:      ''' -    def __init__(self, card = -1, mixername = 'Master', *, **kwargs): +    def __init__(self, card = -1, mixername = 'Master', *, cardindex = None):          '''          Constructor @@ -52,8 +52,8 @@ class ALSA:                                 `ALSA.DEFAULT_CARD` (-1) or 'default' for the default card          @param  mixername:str  The name of the mixer          ''' -        if card == -1 and 'cardindex' in kwargs: # For backwards compatibility -            card = kwargs['cardindex'] +        if card == -1 and cardindex is not None: # For backwards compatibility +            card = cardindex          if isinstance(card, str):              if card == 'default':                  card = -1 @@ -131,7 +131,7 @@ class ALSA:      @staticmethod -    def get_mixers(card = -1, *, **kwargs): +    def get_mixers(card = -1, *, cardindex = None):          '''          Get the names of all available mixers for an audio card @@ -139,8 +139,8 @@ class ALSA:                                  `ALSA.DEFAULT_CARD` (-1) or 'default' for the default card          @return  :list<str>     The names of all available mixers for an audio card          ''' -        if card == -1 and 'cardindex' in kwargs: # For backwards compatibility -            card = kwargs['cardindex'] +        if card == -1 and cardindex is not None: # For backwards compatibility +            card = cardindex          if isinstance(card, str):              if card == 'default':                  card = -1 diff --git a/src/plugins/lid.py b/src/plugins/lid.py new file mode 100644 index 0000000..c307abb --- /dev/null +++ b/src/plugins/lid.py @@ -0,0 +1,41 @@ +# -*- python -*- +''' +xpybar – xmobar replacement written in python +Copyright © 2014, 2015, 2016, 2017  Mattias Andrée (maandree@member.fsf.org) + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program.  If not, see <http://www.gnu.org/licenses/>. +''' + +import os + + +class Lid: +    ''' +    Laptop lid monitor +    ''' +     +    @staticmethod +    def is_open(): +        ''' +        Check whether the lid is open +         +        @param  :bool?  `True` if the lid is open, +                        `False` if the lid is closed, +                        `None` if there is no lid, or if the +                        computer does not report the lid's state +        ''' +        if not os.path.exists('/proc/acpi/button/lid/LID/state'): +            return None +        with open('/proc/acpi/button/lid/LID/state') as file: +            return 'open' in file.read.decode('utf-8', 'strict') diff --git a/src/plugins/powersupply.py b/src/plugins/powersupply.py new file mode 100644 index 0000000..5408088 --- /dev/null +++ b/src/plugins/powersupply.py @@ -0,0 +1,266 @@ +# -*- python -*- +''' +xpybar – xmobar replacement written in python +Copyright © 2014, 2015, 2016, 2017  Mattias Andrée (maandree@member.fsf.org) + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program.  If not, see <http://www.gnu.org/licenses/>. +''' + +import os + + +class PowerSupply: +    ''' +    Power supply monitor +     +    To calculate the time (in hours) until discharged: +    ``` +    power_supply.get_charge() / power_supply.get_current() +    ``` +     +    To calculate the time (in hours) until charged: +    ``` +    (power_supply.get_charge_full() - power_supply.get_charge()) / power_supply.get_current() +    ``` +     +    @variable  name:str                 The name of the power supply +    @variable  path:str                 The path to the power supply in /sys +    @variable  type:str?                The power supply type, or `None` if not found +                                        (this is most likely not the case), known possible values +                                        'Mains' and 'Battery' +     +    The following will probably be `None` for non-Battery power supplies +     +    @variable  manufacturer:str?        The manufacturer of the power supply, `None` if unknown +    @variable  model_name:str?          The model name of the power supply, `None` if unknown +    @variable  serial_number:str?       The serial number of the power supply, `None` if unknown +    @variable  technology:str?          The technology the power supply uses, `None` if unknown +    @variable  charge_full_design:int?  The charge (nAh) of the power supply at full capacity +                                        when the power supply supply is 100 % healthy, +                                        `None` if unknown of if not applicable +    @variable  voltage_min_design:int?  The minimum voltage (nV) when the power supply supply is +                                        100 % healthy, `None` if unknown of if not applicable +    ''' +     +    def __init__(self, name): +        ''' +        Constructor +         +        @param  name:str  The name of the power supply, you can find +                          the with `PowerSupplu.supplies` +        ''' +        self.name = name +        self.path = '/sys/class/power_supply/' + name +         +        self.type = None +        if os.path.exists(self.path + '/type'): +            with open(self.path + '/type', 'rb') as file: +                self.type = file.read().decode('utf-8', 'strict')[:-1] +         +        self.manufacturer = None +        if os.path.exists(self.path + '/manufacturer'): +            with open(self.path + '/manufacturer', 'rb') as file: +                self.manufacturer = file.read().decode('utf-8', 'strict')[:-1] +         +        self.model_name = None +        if os.path.exists(self.path + '/model_name'): +            with open(self.path + '/model_name', 'rb') as file: +                self.model_name = file.read().decode('utf-8', 'strict')[:-1] +         +        self.serial_number = None +        if os.path.exists(self.path + '/serial_number'): +            with open(self.path + '/serial_number', 'rb') as file: +                self.serial_number = file.read().decode('utf-8', 'strict')[:-1] +         +        self.technology = None +        if os.path.exists(self.path + '/technology'): +            with open(self.path + '/technology', 'rb') as file: +                self.technology = file.read().decode('utf-8', 'strict')[:-1] +         +        self.charge_full_design = None +        if os.path.exists(self.path + '/charge_full_design'): +            with open(self.path + '/charge_full_design', 'rb') as file: +                self.charge_full_design = int(file.read().decode('utf-8', 'strict')[:-1]) +        elif os.path.exists(self.path + '/energy_full_design'): +            with open(self.path + '/energy_full_design', 'rb') as file: +                self.charge_full_design = int(file.read().decode('utf-8', 'strict')[:-1]) +         +        self.voltage_min_design = None +        if os.path.exists(self.path + '/voltage_min_design'): +            with open(self.path + '/voltage_min_design', 'rb') as file: +                self.voltage_min_design = int(file.read().decode('utf-8', 'strict')[:-1]) +         +    def get_alarm(): +        ''' +        Get the alarm level +         +        @return  :int?  The alarm level, `None` if unknown or if unapplicable +        ''' +        if os.path.exists(self.path + '/alarm'): +            with open(self.path + '/alarm', 'rb') as file: +                return int(file.read().decode('utf-8', 'strict')[:-1]) +        return None +     +    def get_capacity(): +        ''' +        Get the current capacity +         +        This is a rounded down version of `.get_charge() / .get_charge_full()` +         +        @param  :int?  The current capacity, +                       `None` if unknown or if unapplicable +        ''' +        if os.path.exists(self.path + '/capacity'): +            with open(self.path + '/capacity', 'rb') as file: +                return int(file.read().decode('utf-8', 'strict')[:-1]) +        return None +     +    def get_capacity_level(): +        ''' +        TODO some information about what this is would be nice +        ''' +        if os.path.exists(self.path + '/capacity_level'): +            with open(self.path + '/capacity_level', 'rb') as file: +                return file.read().decode('utf-8', 'strict')[:-1] +        return None +     +    def get_charge_full(): +        ''' +        Get the charge when the power supply is fully charged +        according to the last known charge as fully charged state +         +        @return  :int?  The charge in nAh when the power supply is fully charged, +                        `None` if unknown or if unapplicable +        ''' +        if os.path.exists(self.path + '/charge_full'): +            with open(self.path + '/charge_full', 'rb') as file: +                return int(file.read().decode('utf-8', 'strict')[:-1]) +        if os.path.exists(self.path + '/energy_full'): +            with open(self.path + '/energy_full', 'rb') as file: +                return int(file.read().decode('utf-8', 'strict')[:-1]) +        return None +     +    def get_charge(): +        ''' +        Get the current charge +         +        @return  :int?  The current charge in nAh, +                        `None` if unknown or if unapplicable +        ''' +        if os.path.exists(self.path + '/charge_now'): +            with open(self.path + '/charge_now', 'rb') as file: +                return int(file.read().decode('utf-8', 'strict')[:-1]) +        if os.path.exists(self.path + '/energy_now'): +            with open(self.path + '/energy_now', 'rb') as file: +                return int(file.read().decode('utf-8', 'strict')[:-1]) +        return None +     +    def get_current(): +        ''' +        Get the current current +         +        @return  :int?  The current current in nA, `None` if unknown +        ''' +        if os.path.exists(self.path + '/current_now'): +            with open(self.path + '/current_now', 'rb') as file: +                return int(file.read().decode('utf-8', 'strict')[:-1]) +        elif os.path.exists(self.path + '/power_now') and os.path.exists(self.path + '/voltage_now'): +            with open(self.path + '/power_now', 'rb') as file: +                power = int(file.read().decode('utf-8', 'strict')[:-1]) +            with open(self.path + '/voltage_now', 'rb') as file: +                voltage = int(file.read().decode('utf-8', 'strict')[:-1]) +            return 1000000 * power / voltage +        return None +     +    def get_power(): +        ''' +        Get the current power +         +        @return  :int?  The current power in nW, `None` if unknown +        ''' +        if os.path.exists(self.path + '/power_now'): +            with open(self.path + '/power_now', 'rb') as file: +                return int(file.read().decode('utf-8', 'strict')[:-1]) +        elif os.path.exists(self.path + '/current_now') and os.path.exists(self.path + '/voltage_now'): +            with open(self.path + '/current_now', 'rb') as file: +                current = int(file.read().decode('utf-8', 'strict')[:-1]) +            with open(self.path + '/voltage_now', 'rb') as file: +                voltage = int(file.read().decode('utf-8', 'strict')[:-1]) +            return voltage * current / 1000000 +        return None +     +    def get_cycle_count(): +        ''' +        Get the battery's cycle count, that is, the full charge +        energy divided by the total used energy +         +        @param  :int?  The number of cycle, is often appear as zero, +                       `None` if unknown or if unapplicable +        ''' +        if os.path.exists(self.path + '/cycle_count'): +            with open(self.path + '/cycle_count', 'rb') as file: +                return int(file.read().decode('utf-8', 'strict')[:-1]) +        return None +     +    def get_status(): +        ''' +        Get the current status +         +        @return  :str?  The current status, known values are +                        'Charging', 'Discharging' and 'Unknown', +                        `None` if unknown or if unapplicable +        ''' +        if os.path.exists(self.path + '/status'): +            with open(self.path + '/status', 'rb') as file: +                return file.read().decode('utf-8', 'strict')[:-1] +        return None +     +    def get_voltage(): +        ''' +        Get the current voltage +         +        @return  :int?  The current voltage in nV, `None` if unknown +        ''' +        if os.path.exists(self.path + '/voltage_now'): +            with open(self.path + '/voltage_now', 'rb') as file: +                return int(file.read().decode('utf-8', 'strict')[:-1]) +        return None +     +    def is_online(self): +        ''' +        Check whether the power supply is online +         +        @return  :bool?  Whether the power supply is online, `None` if unknown +        ''' +        if os.path.exists(self.path + '/online'): +            with open(self.path + '/online', 'rb') as file: +                return file.read().decode('utf-8', 'strict') == '1\n' +        if os.path.exists(self.path + '/present'): +            with open(self.path + '/present', 'rb') as file: +                return file.read().decode('utf-8', 'strict') == '1\n' +        return False +     +    @staticmethod +    def supplies(): +        ''' +        Lists all available power supplies +         +        If the returned list is empty, you are probably running on a +        desktop (or more powerful machine such as a server) that only +        runs on mains (AC). +         +        @return  :list<str>  The name of all available power supplies +        ''' +        return os.listdir('/sys/class/power_supply') +  | 
