diff --git a/scripts/power b/scripts/power index 8bf162d..4835b3d 100755 --- a/scripts/power +++ b/scripts/power @@ -47,7 +47,10 @@ def get_battery_profiler(): elif item == 'sppower_ups_installed': batteryprofiler['UPSInstalled'] = obj[item] elif item == 'sppower_battery_health_info': - batteryprofiler['condition'] = obj['sppower_battery_health_info']['sppower_battery_health'] + try: + batteryprofiler['condition'] = obj['sppower_battery_health_info']['sppower_battery_health'] + except: + pass elif item == 'sppower_battery_installed' and obj['sppower_battery_installed'] == "FALSE": batteryprofiler['condition'] = "No Battery" elif item == 'sppower_battery_charge_info': @@ -120,17 +123,19 @@ def get_battery_stats(): plist = plistlib.loads(output) batteryxml = plist[0] + cpu_arch = os.uname()[3].lower() + batteryinfo = get_battery_profiler() for item in batteryxml: if item == 'DesignCapacity': batteryinfo['DesignCapacity'] = batteryxml[item] - elif item == 'MaxCapacity' and platform.processor() == 'i386': + elif item == 'MaxCapacity' and (cpu_arch == 'i386' or cpu_arch == 'x86_64'): batteryinfo['MaxCapacity'] = batteryxml[item] - elif item == 'AppleRawMaxCapacity' and platform.processor() == 'arm': + elif item == 'AppleRawMaxCapacity' and (cpu_arch == 'arm' or cpu_arch == 'arm64'): batteryinfo['MaxCapacity'] = batteryxml[item] - elif item == 'CurrentCapacity' and platform.processor() == 'i386': + elif item == 'CurrentCapacity' and (cpu_arch == 'i386' or cpu_arch == 'x86_64'): batteryinfo['CurrentCapacity'] = batteryxml[item] - elif item == 'AppleRawCurrentCapacity' and platform.processor() == 'arm': + elif item == 'AppleRawCurrentCapacity' and (cpu_arch == 'arm' or cpu_arch == 'arm64'): batteryinfo['CurrentCapacity'] = batteryxml[item] elif item == 'Temperature': batteryinfo['Temperature'] = batteryxml[item] @@ -154,6 +159,7 @@ def get_battery_stats(): batteryinfo['condition'] = "No Battery" elif item == 'Serial': # macOS 11+ battery serial is here batteryinfo['BatterySerialNumber'] = batteryxml[item] + batteryinfo['ManufactureDate'] = process_manufacture_date(batteryxml[item]) elif item == 'BatteryData': if 'CellVoltage' in batteryxml[item]: # macOS 11+ stores cell voltage here cells = [] @@ -162,61 +168,24 @@ def get_battery_stats(): cells.append(str(float(cell)/1000)+"v") batteryinfo['CellVoltage'] = (', '.join(map(str,(cells)))) - # BatteryData values exist prior to Big Sur, but doesn't include the serial number in the dict. - # Only run this routine if Big Sur (Darwin 20) or later. - if getDarwinVersion() > 19: - # The format for battery manufacturing date changed in Big Sur. - # Apple suggested parsing the SerialNumber that contains the manufacture date. - # A SN of XXX1234ZZZZZZZZZZ has the manufacturing date as the values of 1234. - # The first digit is the last digit of the year. 9 would be 2019. - # Note: they reset every decade. So you need to figure out if 9 is 2019 or 2029 - battery_serial_number = batteryxml[item]['Serial'] - battery_year_sn_digit = int(battery_serial_number[3]) - # The next two digits are the week number of the year - battery_week_sn_digit = int(battery_serial_number[4:6]) - # The last digit is the day offset: 1 is Monday 7 is Sunday. - battery_day_sn_digit = int(battery_serial_number[6]) - - # Determine the decade for the battery year - current_decade = datetime.date.today().year - datetime.date.today().year % 10 - battery_year = current_decade + battery_year_sn_digit - if (battery_year, battery_week_sn_digit) > datetime.date.today().isocalendar()[:2]: - battery_year -= 10 - - # Set the variable of the date to year-Wweek-day: - # For example: 2018-10-20 = 2018-W42-5 - battery_date_data = str(battery_year) + '-W' + str(battery_week_sn_digit) + '-' + str(battery_day_sn_digit) - - # Python's evaluation of the week number does not respect the ISO week numbers. - # Reference: https://community.dataiku.com/t5/Using-Dataiku-DSS/Converting-week-and-year-to-datetime-stamp-using-Python-function/m-p/11310 - # This is addressed in Python 3.6+ with using the datetime strptime format of '%G %V %w' - # To be backward compatible with 2.7 the additional work will need to exist. - def iso_year_start(iso_year): - "The gregorian calendar date of the first day of the given ISO year" - fourth_jan = datetime.date(iso_year, 1, 4) - delta = datetime.timedelta(fourth_jan.isoweekday()-1) - return fourth_jan - delta - - def iso_to_gregorian(iso_year, iso_week, iso_day): - "Gregorian calendar date for the given ISO year, week and day" - year_start = iso_year_start(iso_year) - return year_start + datetime.timedelta(days=iso_day-1, weeks=iso_week-1) - - dt = iso_to_gregorian(battery_year, battery_week_sn_digit, battery_day_sn_digit) - battery_date = dt.strftime("%Y-%m-%d") - # Calculate the date based off the year-week-day with a datetime value in the format YYYY-MM-DD - #battery_date = datetime.datetime.strptime(battery_date_data, "%Y-W%W-%w") - - # Convert the date values to binary format - binary_date_day = '{0:05b}'.format(int(battery_date.split("-")[2])) - binary_date_month = '{0:04b}'.format(int(battery_date.split("-")[1])) - binary_date_year = '{0:07b}'.format(int(battery_year) - 1980) - # Combine the binary values into one long string of YYYYYYYMMMMDDDDD - # to match the legacy battery ManufactureDate format - binary_full_date = binary_date_year + binary_date_month + binary_date_day - packed_date = int(binary_full_date, 2) - - batteryinfo['ManufactureDate'] = packed_date + if 'Serial' in batteryxml[item]: # Battery serial is sometimes here + batteryinfo['BatterySerialNumber'] = batteryxml[item] + batteryinfo['ManufactureDate'] = process_manufacture_date(batteryxml[item]) + + if 'LifetimeData' in batteryxml[item]: + if "MaximumChargeCurrent" in batteryxml[item]['LifetimeData']: + batteryinfo['max_charge_current'] = batteryxml[item]['LifetimeData']['MaximumChargeCurrent'] + if "MaximumDischargeCurrent" in batteryxml[item]['LifetimeData']: + batteryinfo['max_discharge_current'] = batteryxml[item]['LifetimeData']['MaximumDischargeCurrent'] + if "MaximumPackVoltage" in batteryxml[item]['LifetimeData']: + batteryinfo['max_pack_voltage'] = batteryxml[item]['LifetimeData']['MaximumPackVoltage'] + if "MinimumPackVoltage" in batteryxml[item]['LifetimeData']: + batteryinfo['min_pack_voltage'] = batteryxml[item]['LifetimeData']['MinimumPackVoltage'] + if "MaximumTemperature" in batteryxml[item]['LifetimeData']: + batteryinfo['max_temperature'] = batteryxml[item]['LifetimeData']['MaximumTemperature'] + if "MinimumTemperature" in batteryxml[item]['LifetimeData']: + batteryinfo['min_temperature'] = batteryxml[item]['LifetimeData']['MinimumTemperature'] + elif item == 'ManufactureDate': # 10.15- has Manufacture Date here batteryinfo['ManufactureDate'] = batteryxml[item] elif item == 'Voltage': @@ -232,11 +201,15 @@ def get_battery_stats(): batteryinfo['adapter_current'] = format((float(batteryxml[item]['Current'])/1000),'.3f') if 'Voltage' in batteryxml[item]: batteryinfo['adapter_voltage'] = format((float(batteryxml[item]['Voltage'])/1000),'.3f') + if 'Description' in batteryxml[item]: + batteryinfo['adapter_description'] = batteryxml[item]['Description'] elif item == 'ChargerData': if 'ChargingCurrent' in batteryxml[item]: batteryinfo['charging_current'] = format((float(batteryxml[item]['ChargingCurrent'])/1000),'.3f') if 'ChargingVoltage' in batteryxml[item]: batteryinfo['charging_voltage'] = format((float(batteryxml[item]['ChargingVoltage'])/1000),'.3f') + if 'Description' in batteryxml[item]: + batteryinfo['adapter_description'] = batteryxml[item]['Description'] return batteryinfo except: return get_battery_profiler() @@ -321,11 +294,11 @@ def get_pmset_therm(): output = output.decode().replace("CPU_Scheduler_Limit", "\nCPU_Scheduler_Limit") for item in output.split("\n"): - if "CPU_Scheduler_Limit = " in item: + if "CPU_Scheduler_Limit" in item: therminfo['CPUSchedulerLimit'] = re.sub('[^0-9]','', item.strip()) - elif " CPU_Available_CPUs = " in item: + elif "CPU_Available_CPUs" in item: therminfo['CPUAvailableCPUs'] = re.sub('[^0-9]','', item.strip()) - elif " CPU_Speed_Limit = " in item: + elif "CPU_Speed_Limit" in item: therminfo['CPUSpeedLimit'] = re.sub('[^0-9]','', item.strip()) return therminfo @@ -362,11 +335,11 @@ def get_pmset_ups(): upsinfo = get_pmset_sched() for item in output.decode().split("\n"): - if " haltlevel " in item: + if "haltlevel" in item: upsinfo['haltlevel'] = re.sub('[^0-9]','', item.strip()) - elif " haltafter " in item: + elif "haltafter" in item: upsinfo['haltafter'] = re.sub('[^0-9]','', item.strip()) - elif " haltremain " in item: + elif "haltremain" in item: upsinfo['haltremain'] = re.sub('[^0-9]','', item.strip()) return upsinfo @@ -427,9 +400,76 @@ def get_pmset_general(): return powerinfo +def process_manufacture_date(serial): + + try: + try: + # The format for battery manufacturing date changed in Big Sur. + # Apple suggested parsing the SerialNumber that contains the manufacture date. + # A SN of XXX1234ZZZZZZZZZZ has the manufacturing date as the values of 1234. + # The first digit is the last digit of the year. 9 would be 2019. + # Note: they reset every decade. So you need to figure out if 9 is 2019 or 2029 + battery_serial_number = serial + battery_year_sn_digit = int(battery_serial_number[3]) + # The next two digits are the week number of the year + battery_week_sn_digit = int(battery_serial_number[4:6]) + # The last digit is the day offset: 1 is Monday 7 is Sunday. + battery_day_sn_digit = int(battery_serial_number[6]) + except: + # The serial may also be shifted + battery_serial_number = serial + battery_year_sn_digit = int(battery_serial_number[2]) + # The next two digits are the week number of the year + battery_week_sn_digit = int(battery_serial_number[3:5]) + # The last digit is the day offset: 1 is Monday 7 is Sunday. + battery_day_sn_digit = int(battery_serial_number[5]) + + # Determine the decade for the battery year + current_decade = datetime.date.today().year - datetime.date.today().year % 10 + battery_year = current_decade + battery_year_sn_digit + if (battery_year, battery_week_sn_digit) > datetime.date.today().isocalendar()[:2]: + battery_year -= 10 + + # Set the variable of the date to year-Wweek-day: + # For example: 2018-10-20 = 2018-W42-5 + battery_date_data = str(battery_year) + '-W' + str(battery_week_sn_digit) + '-' + str(battery_day_sn_digit) + + # Python's evaluation of the week number does not respect the ISO week numbers. + # Reference: https://community.dataiku.com/t5/Using-Dataiku-DSS/Converting-week-and-year-to-datetime-stamp-using-Python-function/m-p/11310 + # This is addressed in Python 3.6+ with using the datetime strptime format of '%G %V %w' + # To be backward compatible with 2.7 the additional work will need to exist. + def iso_year_start(iso_year): + "The gregorian calendar date of the first day of the given ISO year" + fourth_jan = datetime.date(iso_year, 1, 4) + delta = datetime.timedelta(fourth_jan.isoweekday()-1) + return fourth_jan - delta + + def iso_to_gregorian(iso_year, iso_week, iso_day): + "Gregorian calendar date for the given ISO year, week and day" + year_start = iso_year_start(iso_year) + return year_start + datetime.timedelta(days=iso_day-1, weeks=iso_week-1) + + dt = iso_to_gregorian(battery_year, battery_week_sn_digit, battery_day_sn_digit) + battery_date = dt.strftime("%Y-%m-%d") + # Calculate the date based off the year-week-day with a datetime value in the format YYYY-MM-DD + #battery_date = datetime.datetime.strptime(battery_date_data, "%Y-W%W-%w") + + # Convert the date values to binary format + binary_date_day = '{0:05b}'.format(int(battery_date.split("-")[2])) + binary_date_month = '{0:04b}'.format(int(battery_date.split("-")[1])) + binary_date_year = '{0:07b}'.format(int(battery_year) - 1980) + # Combine the binary values into one long string of YYYYYYYMMMMDDDDD + # to match the legacy battery ManufactureDate format + binary_full_date = binary_date_year + binary_date_month + binary_date_day + packed_date = int(binary_full_date, 2) + + return packed_date + except: + return "" + def remove_all(substr, str): return str.replace(substr, "") - + def getDarwinVersion(): """Returns the Darwin version.""" # Catalina -> 10.15.7 -> 19.6.0 -> 19 @@ -437,7 +477,7 @@ def getDarwinVersion(): # return int(os_version_tuple[1]) darwin_version_tuple = platform.release().split('.') return int(darwin_version_tuple[0]) - + def main(): """Main""" @@ -457,6 +497,5 @@ def main(): with open(output_plist, 'wb') as fp: plistlib.dump(info, fp, fmt=plistlib.FMT_XML) - if __name__ == "__main__": main()