tis-audit-battery icon

Audit Battery

Paquet d'installation silencieuse pour Audit Battery

1.2-5
System and network
System and network

tis-audit-battery

Une fois le paquet tis-audit-battery installé sur les postes. Il permet de vérifier l'état des batteries des ordinateurs. Il fournit des informations détaillées sur les propriétés des batteries (ID, santé, localisation, etc.). Les résultats sont consignés dans la section audit-battery de l'onglet Données d'audit de la machine.


Exemple de Format des données retournées :

{
	"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