/
githubmirror
/
libsmbios
Обзор
Документация
Войти
/
githubmirror
/
libsmbios
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/bin/smbios-battery-ctl
350 строк
11 KB
Marcin Mielniczuk
Mention me as a contributor to smbios-battery-ctl (see #73)
02 июн 2019, 22:20
02 июн 2019, 22:20
b029460
Код
Авторство
О чём код?
#!/usr/bin/python3 # vim:expandtab:autoindent:tabstop=4:shiftwidth=4:filetype=python:tw=0 ############################################################################# # # Copyright (c) 2016 Dell Computer Corporation # by Srinivas Gowda <srinivas_g_gowda@dell.com> # Dual Licenced under GNU GPL and OSL # Contributor: (c) 2019 Marcin Mielniczuk <marmistrz dot dev at zoho dot eu> # ############################################################################# """smbios-battery-ctl""" # import arranged alphabetically import gettext import locale import os import sys from enum import Enum from typing import Tuple # the following vars are all substituted on install # this bin isnt byte-compiled, so this is ok pythondir=os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])), "..", "python") clidir=os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])), "..", "py-cli") # end vars # import all local modules after this. sys.path.insert(0,pythondir) sys.path.insert(0,clidir) import cli from libsmbios_c import smbios_token, smbios, smi, system_info as sysinfo, localedir, GETTEXT_PACKAGE from libsmbios_c.trace_decorator import traceLog, getLog __VERSION__=sysinfo.get_library_version_string() locale.setlocale(locale.LC_ALL, '') gettext.install(GETTEXT_PACKAGE, localedir) moduleLog = getLog() verboseLog = getLog(prefix="verbose.") class RunTimeBatteryErr(Exception): pass def command_parse(): parser = cli.OptionParser(usage=__doc__, version=__VERSION__) cli.addStdOptions(parser, passwordOpts=True, securityKeyOpt=True) parser.add_option( "--battery-charge", "-c", action="store_true", default=False, help=_("This will get the Current Battery Charging State") ) parser.add_option( "--get-charging-cfg", action="store_true", default=False, help=_("Get the current Primary Battery Charge Configuration") ) choices = [str(mode) for mode in ChargingMode] parser.add_option( "--set-charging-mode", metavar='<MODE>', choices=choices, help=_(f"Set the current Primary Battery Charge Configuration. Valid choices are: {choices}") ) parser.add_option( "--set-custom-charge-interval", nargs=2, metavar='<START> <END>', type='int', help=_( "Set the percentage bounds for custom charge. Both must be integers. " "START must lie in the range [50, 95], " "END must lie in the range [55, 100], " "END must be at least (START + 5)") ) if len(sys.argv) == 1: parser.print_help() sys.exit() return parser.parse_args() def is_set(num, pos): mask = 1 << pos return True if (num & mask) else False # Return a the byte specified by byte_pos from a double word num def get_byte(num, byte_pos): try : if byte_pos < 0 or byte_pos > 3: raise RunTimeBatteryErr( "Internal Error: Byte position out of bound - ", byte_pos ) except RunTimeBatteryErr as e: print("\t%s" % e) return ((num >> (byte_pos*8)) & 0xFF) def print_express_charge_status(n): if n == 0: print('\t Battery is not present') elif n == 1: print('\t Standard') elif n == 2: print('\t Express') elif n == 3: print('\t One-time express') elif n == 0xFF: print('\t Battery is present, and it does not support express charge') else : print('\t Unknown') class ChargingMode(Enum): PRIMARILY_AC = 0x0341 ADAPTIVE = 0x0342 CUSTOM = 0x0343 # 0x0348 is deprecated, cf. token_list.csv STANDARD = 0x0346 EXPRESS = 0x0347 def __str__(self) -> str: return self.name.lower() @staticmethod def from_string(s: str) -> 'ChargingMode': try: return ChargingMode[s.upper()] except KeyError: raise ValueError() CUSTOM_CHARGE_START = 0x0349 CUSTOM_CHARGE_END = 0x034A def get_charging_mode() -> ChargingMode: table = smbios_token.TokenTable() active_mode = None for mode in ChargingMode: active = table[mode.value].isActive() if active: if active_mode is not None: raise RunTimeBatteryErr( f"Multiple charging modes enabled: {active_mode} and " f"{mode} enabled at the same time" ) active_mode = mode if active_mode is None: raise RunTimeBatteryErr("No charging mode enabled") return active_mode def set_charging_mode(mode: ChargingMode) -> None: table = smbios_token.TokenTable() table[mode.value].activate() def get_custom_charge_interval() -> Tuple[int, int]: table = smbios_token.TokenTable() low = table[CUSTOM_CHARGE_START].getString() high = table[CUSTOM_CHARGE_END].getString() # The intervals are stored as 2-byte little endian integers low_num = int.from_bytes(low, "little") high_num = int.from_bytes(high, "little") return (low_num, high_num) def set_custom_charge_interval(start: int, end: int) -> None: table = smbios_token.TokenTable() # Primary Battery Custom Charge Start must be in the range [50, 95] with a step value of 1 # and at least 5% less than Primary Custom Charge End # Primary Battery Custom Charge End must be in the range [55,100] with a step value of 1 # and at least 5% greater than Primary Custom Charge Start if end - start < 5: raise ValueError("END must be at least (START + 5)") if start < 50 or start > 95: raise ValueError("START must lie in the range [50, 95]") if end < 55 or end > 100: raise ValueError("END must lie in the range [55, 100]") start_bytes = start.to_bytes(2, "little") end_bytes = end.to_bytes(2, "little") table[CUSTOM_CHARGE_START].setString(start_bytes) table[CUSTOM_CHARGE_END].setString(end_bytes) def print_primary_battery_cfg() -> None: mode = get_charging_mode() print(f"Charging mode: {mode}") if mode == ChargingMode.CUSTOM: interval = get_custom_charge_interval() print(f"Charging interval: {interval}") def PrintBatteryChargingState(): try: res = smi.simple_ci_smi( 4, 12, 0 ) verboseLog.info( _(" Get Charging State : res[smi.cbRES1]=0x%X,res[smi.cbRES2]=0x%X, res[smi.cbRES3]=0x%X") % (res[smi.cbRES1],res[smi.cbRES2],res[smi.cbRES3]) ) if res[smi.cbRES1] != 0: raise RunTimeBatteryErr( _(" Info: Unable to read Current Battery Charging State on this system\n") ) # cbRES2 byte 0 gives supported Features print(_(" \n Supported battery charging features: ")) if is_set (res[smi.cbRES2], 0): print("\t Express Charging") if is_set (res[smi.cbRES2], 1): print("\t Charge Disable") if (get_byte(res[smi.cbRES2], 0) == 0 ): print("\t NIL") print(_(" \n Battery charging Status: ")) # cbRES2 byte 1 gives the status of supported Features if is_set ((get_byte(res[smi.cbRES2], 1)), 0): print("\t One or more batteries are present that support Express charge") if is_set ((get_byte(res[smi.cbRES2], 1)), 1): print("\t Charging is disabled") if (get_byte(res[smi.cbRES2], 1) == 0 ): print("\t NIL") # cbRes3, bytes 0-3 contain the status of batteries 0-3 respectively for i in range(4): expr_status = get_byte(res[smi.cbRES3], i) if(expr_status): print(_("\n Battery %d Express Charge State:") % (i)) print_express_charge_status(expr_status) except RunTimeBatteryErr as e: print("\t%s" % e) def main(): exit_code = 0 (options, args) = command_parse() cli.setup_std_options(options) try: if options.battery_charge: print(_("Libsmbios version : %s") % sysinfo.get_library_version_string()) print(_("smbios-battery-ctl version : %s") % __VERSION__) verboseLog.info( _(" You can use smbios-battery-ctl utility to view/modify battery settings")) PrintBatteryChargingState() if options.get_charging_cfg: print_primary_battery_cfg() if options.set_charging_mode is not None: mode = ChargingMode.from_string(options.set_charging_mode) set_charging_mode(mode) print(f"Charging mode has been set to: {mode}") if options.set_custom_charge_interval is not None: (low, high) = options.set_custom_charge_interval set_custom_charge_interval(low, high) print(f"Custom charge interval has been set to ({low}, {high})") except (smi.SMIExecutionError, ) as e: exit_code=3 moduleLog.info( _("ERROR: Could not execute SMI.") ) verboseLog.info( _("The smi library returned this error:") ) verboseLog.info( str(e) ) moduleLog.info( cli.standardFailMessage ) except (smbios.TableParseError, smbios_token.TokenTableParseError) as e: exit_code=3 moduleLog.info( _("ERROR: Could not parse system SMBIOS table.") ) verboseLog.info( _("The smbios library returned this error:") ) verboseLog.info( str(e) ) moduleLog.info( cli.standardFailMessage ) except (smbios_token.TokenManipulationFailure,) as e: exit_code=4 moduleLog.info( _("ERROR: Could not manipulate system token.") ) verboseLog.info( _("The token library returned this error:") ) verboseLog.info( str(e) ) moduleLog.info( cli.standardFailMessage ) return exit_code if __name__ == "__main__": sys.exit( main() ) # cbClass 4 # cbSelect 12 # Battery Charging # # cbArg1 determines the function to be performed # On return: # cbRES1 Standard return codes (0, -1, -2) # # cbArg1 0x0 = Get Current State # cbRes2, byte 0 Supported feature bits # bit 0 Express charge is supported (by the BIOS) # bit 1 Charge disable is supported # bits 2-7 Reserved for future use # cbRes2, byte 1 Status bits # bit 0 One or more batteries are present that support Express charge # bit 1 Charging is disabled # bits 2-7 Reserved for future use # If the system supports Express Charge, cbRes3, bytes 0-3 contain the status of batteries 0-3 respectively (numbered as with the Class 8 function). # cbRes3, byte n Battery n express charge status: # 0 Battery is not present # 1 Standard # 2 Express # 3 One-time express # 0xFF Battery is present, and it does not support express charge # # cbArg2, byte 0 Set charge disable state: # 0 Off (enable charging) # 1 On (disable charging) # # cbArg2, bytes 0-3 contain the new state of batteries 0-3 respectively # Battery Status: # 00h High # 01h Low # 02h Critical # 03h Charging # # cbArg2, byte n Battery n state: # 0 No change (state remains as it was previously) # 1 Standard . Express charge not supported by the battery # 2 Express # 3 One-time express # # # cbClass 8 # cbSelect 16 # Set Battery Desktop Mode # # cbArg1 determines the function to be performed # On return: # cbRES1 Standard return codes (0, -1, -2) # # cbArg1, byte 0 # Mode Enablement # 0 Disable desktop mode # 1 Fully enable desktop mode # 2 - 255 Undefined # # On return: # cbRES1 Standard return codes (0, -1, -2) # # # cbClass 8 # cbSelect 17 # Get Battery Desktop Mode # # On return: # cbRES1 Standard return codes (0, -1, -2) # # cbRes2 Status # Bit 0 1 = Supported, 0 = Not Supported # Bit 1 1 = Desktop Mode, 0 = Laptop Mode # Bit 2 1 = Battery is ready to enter desktop mode, 0 = Not ready # Bit 3 to 31 Unused