tis-audit-battery icon

Audit Battery

Silent install package for Audit Battery

1.2-5
System and network
System and network

#tis-audit-battery

Once the tis-audit-battery package is installed on a machine, it allows you to check the status of computer batteries. It provides detailed information about battery properties (ID, health, location, etc.). The results are recorded in the audit-battery section of the Audit Data tab of the machine.


##Example of Returned Data Format:

{
	"value": {
		"BATTERY_1": {
			"Id": "45N1111",
			"Name": "45N1111",
			"Health": "73 %",
			"Location": "Front",
			"LongTerm": "1",
			"Chemistry": "LiP",
			"CycleCount": "0",
			"Manufacturer": "SONY",
			"SBDS_Version": "03.01",
			"SerialNumber": "17531",
			"Maximum_Error": "Unknown",
			"DesignCapacity": "23200",
			"Design_Voltage": "11100 mV",
			"SBDS_Chemistry": "LiP",
			"Design_Capacity": "23200 mWh",
			"ManufactureDate": null,
			"RelativeCapacity": "0",
			"FullChargeCapacity": "16950",
			"SBDS_Serial_Number": "447B",
			"SBDS_Manufacture_Date": "2016-11-12",
			"OEM-specific_Information": "0x00000000"
		},
		"BATTERY_2": {
			"Id": "45N1127",
			"Name": "45N1127",
			"Health": "76 %",
			"Location": "Rear",
			"LongTerm": "1",
			"Chemistry": "LION",
			"CycleCount": "0",
			"Manufacturer": "LGC",
			"SBDS_Version": "03.01",
			"SerialNumber": " 6931",
			"Maximum_Error": "Unknown",
			"DesignCapacity": "23480",
			"Design_Voltage": "11400 mV",
			"SBDS_Chemistry": "LION",
			"Design_Capacity": "23480 mWh",
			"ManufactureDate": null,
			"RelativeCapacity": "0",
			"FullChargeCapacity": "18040",
			"SBDS_Serial_Number": "1B13",
			"SBDS_Manufacture_Date": "2016-12-23",
			"OEM-specific_Information": "0x00000000"
		},
		"battery_count": "2"
    }
}

  • package: tis-audit-battery
  • name: Audit Battery
  • version: 1.2-5
  • categories: System and network
  • maintainer: WAPT Team,Tranquil IT,Jimmy PELÉ
  • licence: wapt_public
  • target_os: windows
  • architecture: all
  • signature_date:
  • size: 9.69 Ko

package           : tis-audit-battery
version           : 1.2-5
architecture      : all
section           : base
priority          : optional
name              : Audit Battery
categories        : System and network
maintainer        : WAPT Team,Tranquil IT,Jimmy PELÉ
description       : Audit batteries capabilities with "powercfg /batteryreport" and dmi_info(). You will be able to pin the health of your batteries in WAPT Console
depends           : 
conflicts         : 
maturity          : PROD
locale            : 
target_os         : windows
min_wapt_version  : 2.3
sources           : https://support.microsoft.com/windows/caring-for-your-battery-in-windows-2db3e37f-5e7d-488e-9086-ed15320519e4
installed_size    : 
impacted_process  : 
description_fr    : Vérifiez les capacités des batteries avec "powercfg /batteryreport" et dmi_info(). Vous serez en mesure d'épingler l'état de santé de vos batteries dans la console WAPT
description_pl    : Audyt możliwości baterii za pomocą "powercfg /batteryreport" i dmi_info(). Będziesz mógł sprawdzić stan baterii w konsoli WAPT
description_de    : Überprüfen Sie die Fähigkeiten der Batterien mit "powercfg /batteryreport" und dmi_info(). Sie können den Zustand Ihrer Batterien in der WAPT-Konsole anzeigen lassen
description_es    : Audite las capacidades de las baterías con "powercfg /batteryreport" y dmi_info(). Usted será capaz de fijar la Salud de sus baterías en WAPT Consola
description_pt    : Auditar as capacidades das baterias com "powercfg /batteryreport" e dmi_info(). Poderá identificar o estado das suas baterias na consola WAPT
description_it    : Verificare le capacità delle batterie con "powercfg /batteryreport" e dmi_info(). Sarà possibile visualizzare lo stato di salute delle batterie nella console WAPT
description_nl    : Controleer de mogelijkheden van de batterijen met "powercfg /batteryreport" en dmi_info(). U kunt de gezondheid van uw accu's vastleggen in WAPT Console
description_ru    : Проведите аудит состояния батарей с помощью функций "powercfg /batteryreport" и dmi_info(). Вы сможете вывести информацию о состоянии батарей в WAPT-консоль
audit_schedule    : 7d
editor            : 
keywords          : 
licence           : wapt_public
homepage          : 
package_uuid      : 9cba4fea-30e4-4e28-89bc-a9b43ca69734
valid_from        : 
valid_until       : 
forced_install_on : 
changelog         : 
min_os_version    : 
max_os_version    : 
icon_sha256sum    : 0c223120ac1a6e4cd0d0abe04cd831c7d4a4c2661947e758c0f703b656933d9a
signer            : Tranquil IT
signer_fingerprint: 8c5127a75392be9cc9afd0dbae1222a673072c308c14d88ab246e23832e8c6bb
signature_date    : 2026-04-14T12:33:38.000000
signed_attributes : package,version,architecture,section,priority,name,categories,maintainer,description,depends,conflicts,maturity,locale,target_os,min_wapt_version,sources,installed_size,impacted_process,description_fr,description_pl,description_de,description_es,description_pt,description_it,description_nl,description_ru,audit_schedule,editor,keywords,licence,homepage,package_uuid,valid_from,valid_until,forced_install_on,changelog,min_os_version,max_os_version,icon_sha256sum,signer,signer_fingerprint,signature_date,signed_attributes
signature         : CKHTbFViurC09Bm7H3JFE7mUHOL19nWSVmgmc2wemNqYq0t08Ec6ozrWXspolxVgj6jEgyYkk7tsDpYkn7o56jsMx40sLZHjuNgFft5bhmd2HPR5A0iNmevcvtukshg81O93mFzMQP/4TcsEFndgKTxqYJXOYerywN2DTpyABup2IK8CYsgccnn9z7VrJLqk/s01VZuMmhJQ1DbnhifmdydAyvLi9IIOTcj8ciWpzGlYqanvqYkZ31xB7ywm8Z+zalE81vl+iszmTtOSLoHXjy1nyJPAM1kbLR2IWENnn6W2JNRPJII2+PqDwBex1iypUbUrFUEu2rCdaVC2NqZhDQ==

# -*- coding: utf-8 -*-
from setuphelpers import *
import os
import xml.etree.ElementTree as ET

def install():
    # Audit-only package
    pass


"""
powercfg /batteryreport /?

POWERCFG /BATTERYREPORT [/OUTPUT <FILENAME>] [/XML] [/TRANSFORMXML <FILENAME.XML>]


Description:
  Generates a report of battery usage characteristics over the lifetime of the
  system. The BATTERYREPORT command will generate an HTML report file in the
  current path.

Parameter List:
  /OUTPUT <FILENAME>     Specify the path and filename to store the battery
                         report HTML or XML file.

  /XML                   Format the report file as XML.

  /DURATION <DAYS>       Specify the number of days to analyze for the report.

  /TRANSFORMXML <FILENAME.XML>   Reformat an XML report file as HTML.

Examples:
  POWERCFG /BATTERYREPORT
  POWERCFG /BATTERYREPORT /OUTPUT "batteryreport.html"
  POWERCFG /BATTERYREPORT /OUTPUT "batteryreport.xml" /XML
  POWERCFG /BATTERYREPORT /TRANSFORMXML "batteryreport.xml"
  POWERCFG /BATTERYREPORT /TRANSFORMXML "batteryreport.xml" /OUTPUT "batteryreport.html"

"""
# ------------------------------------------------------------
# Persistent path helper
# ------------------------------------------------------------
def get_private_persistent_package_file(file_name):
    file_name = os.path.basename(file_name)
    try:
        if control.package_uuid:
            installed = WAPT.is_installed(control.package)
            if installed and control.package_uuid == installed.get("package_uuid"):
                return makepath(WAPT.persistent_root_dir, control.package_uuid, file_name)
    except Exception:
        pass

    return makepath(os.getcwd(), "WAPT", "persistent", file_name)

# ------------------------------------------------------------
# Battery report generation
# ------------------------------------------------------------
def get_batteryreport(output_file):
    """
    Generates a battery report using powercfg.
    If output_file ends with .html -> HTML, otherwise XML.
    """
    ensure_dir(os.path.dirname(output_file))

    if output_file.lower().endswith(".html"):
        run(f'POWERCFG /BATTERYREPORT /OUTPUT "{output_file}"')
    else:
        run(f'POWERCFG /BATTERYREPORT /OUTPUT "{output_file}" /XML')

# ------------------------------------------------------------
# XML parsing (robust namespace handling)
# ------------------------------------------------------------
def get_batteries_from_batteryreport_xml(xml_file):
    """
    Returns a list of dicts (one per battery) from powercfg batteryreport XML.
    Handles Microsoft batteryreport namespace: http://schemas.microsoft.com/battery/2012
    """
    NS = {"b": "http://schemas.microsoft.com/battery/2012"}

    def strip_ns(tag):
        return tag.split("}", 1)[-1] if "}" in tag else tag

    def element_to_obj(el):
        children = list(el)
        if not children:
            return (el.text or "").strip()

        obj = {}
        for ch in children:
            key = strip_ns(ch.tag)
            val = element_to_obj(ch)

            # Repeated tags -> list
            if key in obj:
                if not isinstance(obj[key], list):
                    obj[key] = [obj[key]]
                obj[key].append(val)
            else:
                obj[key] = val
        return obj

    tree = ET.parse(xml_file)
    root = tree.getroot()

    batteries = []
    for b in root.findall("b:Batteries/b:Battery", NS):
        batteries.append(element_to_obj(b))

    return batteries


# ------------------------------------------------------------
# Health computation / status
# ------------------------------------------------------------
def compute_health_percent(full_charge_capacity, design_capacity):
    try:
        fcc = int(full_charge_capacity)
        dc = int(design_capacity)
        if dc <= 0:
            return None
        return int(fcc * 100 / dc)
    except Exception:
        return None


def status_from_health(health):
    """
    Same logic as you had:
      - WARNING if <= 50
      - ERROR if <= 19 or >= 101
      - OK otherwise
    """
    if health is None:
        return "ERROR"
    if health <= 19 or health >= 101:
        return "ERROR"
    if health <= 50:
        return "WARNING"
    return "OK"


def merge_dmi_for_index(dmi_portable_list, index):
    """
    Returns DMI dict for given battery index if available, else {}.
    """
    if isinstance(dmi_portable_list, dict):
        dmi_portable_list = [dmi_portable_list]
    if not isinstance(dmi_portable_list, list):
        return {}
    if index < len(dmi_portable_list) and isinstance(dmi_portable_list[index], dict):
        return dmi_portable_list[index]
    return {}


# ------------------------------------------------------------
# WAPT audit entry point
# ------------------------------------------------------------
def audit():
    """
    Correct behavior:
      - Always relies on batteryreport XML to detect batteries.
      - DMI Portable_Battery is OPTIONAL (enrichment only).
      - Writes audit dict with battery_count + BATTERY_1..N dicts.
    """
    audit_status = "OK"

    # 1) Always generate and parse XML
    battery_report_file = get_private_persistent_package_file("batteryreport.xml")
    get_batteryreport(battery_report_file)

    batteries_xml = get_batteries_from_batteryreport_xml(battery_report_file)
    battery_count = len(batteries_xml)

    # 2) If XML shows 0 -> no battery
    if battery_count == 0:
        WAPT.write_audit_data_if_changed("audit-battery", "audit-battery", {"battery_count": "0"})
        print("No battery detected (batteryreport shows 0).")
        return audit_status

    # 3) DMI is optional
    dmi_portable = dmi_info().get("Portable_Battery", None)
    if isinstance(dmi_portable, dict):
        dmi_portable = [dmi_portable]
    if not isinstance(dmi_portable, list):
        dmi_portable = []

    # 4) Build audit payload
    audit_dict = {"battery_count": str(battery_count)}

    for i in range(battery_count):
        battery_key = f"BATTERY_{i+1}"
        b = batteries_xml[i] if i < len(batteries_xml) else {}

        # capacities
        design_capacity = b.get("DesignCapacity")
        full_charge_capacity = b.get("FullChargeCapacity")

        # compute health
        health = compute_health_percent(full_charge_capacity, design_capacity)
        if health is None:
            b["Health"] = "N/A"
        else:
            b["Health"] = f"{health} %"

        # merge DMI (optional)
        b.update(merge_dmi_for_index(dmi_portable, i))

        # global status
        this_status = status_from_health(health)
        if this_status == "ERROR":
            audit_status = "ERROR"
        elif this_status == "WARNING" and audit_status != "ERROR":
            audit_status = "WARNING"

        # friendly console output
        location = b.get("Location", "Unknown")
        dv = b.get("Design_Voltage", "Unknown")
        dc = b.get("DesignCapacity", "?")
        print(
            f"{battery_key} ({location}) Health is {b.get('Health','N/A')} "
            f"for the DesignCapacity {dc} mWh with Design_Voltage {dv}."
        )

        audit_dict[battery_key] = b

    WAPT.write_audit_data_if_changed("audit-battery", "audit-battery", audit_dict)
    return audit_status

fed039c6e0098b32364070a3917ba84500a4f3b57f127b4c19f94dbd43765573 : WAPT/README.md
b0b1a985b2c4c2afeb22c7d09a56da0c5e32e2046bfe65bf479897689dace1e9 : WAPT/README_fr.md
38d056ab130f7bf7c481c12636a4e9959de36561d3dfcbe54c6e3571bc0c1dc3 : WAPT/certificate.crt
54527be7fc7d47961b4eb4f436593919989050f2f7f1598f2031239402e8fb10 : WAPT/control
0c223120ac1a6e4cd0d0abe04cd831c7d4a4c2661947e758c0f703b656933d9a : WAPT/icon.png
083a5560da1aba1515928fbdfffe04b401465471d1acd56626ff2b44d2a3aa5f : luti.json
7a05cb71531cbb24401b68c5476fc96eb74ef4dd9c9002966f7c83bd3da260cd : setup.py