#include "fastfetch.h"
#include "battery.h"
#include "util/stringUtils.h"
#include "common/processing.h"
#include "common/properties.h"
#define FF_TERMUX_API_PATH FASTFETCH_TARGET_DIR_ROOT "/libexec/termux-api"
#define FF_TERMUX_API_PARAM "BatteryStatus"
static inline void wrapYyjsonFree(yyjson_doc** doc)
{
assert(doc);
if (*doc)
yyjson_doc_free(*doc);
}
static const char* parseTermuxApi(FFBatteryOptions* options, FFlist* results)
{
FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
if(ffProcessAppendStdOut(&buffer, (char* const[]){
FF_TERMUX_API_PATH,
FF_TERMUX_API_PARAM,
NULL
}))
return "Starting `" FF_TERMUX_API_PATH " " FF_TERMUX_API_PARAM "` failed";
yyjson_doc* __attribute__((__cleanup__(wrapYyjsonFree))) doc = yyjson_read_opts(buffer.chars, buffer.length, 0, NULL, NULL);
if (!doc)
return "Failed to parse battery info";
yyjson_val* root = yyjson_doc_get_root(doc);
if (!yyjson_is_obj(root))
return "Battery info result is not a JSON object";
FFBatteryResult* battery = ffListAdd(results);
battery->temperature = FF_BATTERY_TEMP_UNSET;
battery->cycleCount = 0;
battery->timeRemaining = -1;
ffStrbufInit(&battery->manufacturer);
ffStrbufInit(&battery->modelName);
ffStrbufInit(&battery->status);
ffStrbufInit(&battery->technology);
ffStrbufInit(&battery->serial);
ffStrbufInit(&battery->manufactureDate);
battery->capacity = yyjson_get_num(yyjson_obj_get(root, "percentage"));
const char* acStatus = yyjson_get_str(yyjson_obj_get(root, "plugged"));
if (acStatus)
{
if (ffStrEquals(acStatus, "PLUGGED_AC"))
ffStrbufAppendS(&battery->status, "AC Connected, ");
else if (ffStrEquals(acStatus, "PLUGGED_USB"))
ffStrbufAppendS(&battery->status, "USB Connected, ");
else if (ffStrEquals(acStatus, "PLUGGED_WIRELESS"))
ffStrbufAppendS(&battery->status, "Wireless Connected, ");
}
const char* status = yyjson_get_str(yyjson_obj_get(root, "status"));
if (status)
{
if (ffStrEquals(status, "CHARGING"))
ffStrbufAppendS(&battery->status, "Charging");
else if (ffStrEquals(status, "DISCHARGING"))
ffStrbufAppendS(&battery->status, "Discharging");
}
ffStrbufTrimRight(&battery->status, ' ');
ffStrbufTrimRight(&battery->status, ',');
if(options->temp)
battery->temperature = yyjson_get_num(yyjson_obj_get(root, "temperature"));
return NULL;
}
static const char* parseDumpsys(FFBatteryOptions* options, FFlist* results)
{
FF_STRBUF_AUTO_DESTROY buf = ffStrbufCreate();
if (ffProcessAppendStdOut(&buf, (char* []) {
"/system/bin/dumpsys",
"battery",
NULL,
}) != NULL || buf.length == 0)
return "Executing `/system/bin/dumpsys battery` failed";
if (!ffStrbufStartsWithS(&buf, "Current Battery Service state:\n"))
return "Invalid `/system/bin/dumpsys battery` result";
const char* start = buf.chars + strlen("Current Battery Service state:\n");
FF_STRBUF_AUTO_DESTROY temp = ffStrbufCreate();
if (!ffParsePropLines(start, "present: ", &temp) || !ffStrbufEqualS(&temp, "true"))
return NULL;
ffStrbufClear(&temp);
FFBatteryResult* battery = ffListAdd(results);
battery->temperature = FF_BATTERY_TEMP_UNSET;
battery->cycleCount = 0;
battery->timeRemaining = -1;
ffStrbufInit(&battery->manufacturer);
ffStrbufInit(&battery->modelName);
ffStrbufInit(&battery->status);
ffStrbufInit(&battery->technology);
ffStrbufInit(&battery->serial);
ffStrbufInit(&battery->manufactureDate);
if (ffParsePropLines(start, "AC powered: ", &temp) && ffStrbufEqualS(&temp, "true"))
ffStrbufAppendS(&battery->status, "AC powered");
ffStrbufClear(&temp);
if (ffParsePropLines(start, "USB powered: ", &temp) && ffStrbufEqualS(&temp, "true"))
{
if (battery->status.length) ffStrbufAppendS(&battery->status, ", ");
ffStrbufAppendS(&battery->status, "USB powered");
}
ffStrbufClear(&temp);
if (ffParsePropLines(start, "Wireless powered: ", &temp) && ffStrbufEqualS(&temp, "true"))
{
if (battery->status.length) ffStrbufAppendS(&battery->status, ", ");
ffStrbufAppendS(&battery->status, "Wireless powered");
}
ffStrbufClear(&temp);
{
double level = 0, scale = 0;
if (ffParsePropLines(start, "level: ", &temp))
level = ffStrbufToDouble(&temp);
ffStrbufClear(&temp);
if (ffParsePropLines(start, "scale: ", &temp))
scale = ffStrbufToDouble(&temp);
ffStrbufClear(&temp);
if (level > 0 && scale > 0)
battery->capacity = level * 100 / scale;
}
if(options->temp)
{
if (ffParsePropLines(start, "temperature: ", &temp))
battery->temperature = ffStrbufToDouble(&temp);
ffStrbufClear(&temp);
}
ffParsePropLines(start, "technology: ", &battery->technology);
return NULL;
}
const char* ffDetectBattery(FFBatteryOptions* options, FFlist* results)
{
const char* error = parseTermuxApi(options, results);
if (error && parseDumpsys(options, results) == NULL)
return NULL;
return error;
}