#include "battery.h"
#include "common/io/io.h"
#include "util/stringUtils.h"
#include <dirent.h>
#include <unistd.h>
#include <fcntl.h>
static bool checkAc(const char* id, FFstrbuf* tmpBuffer)
{
if (ffStrStartsWith(id, "BAT"))
ffStrbufSetS(tmpBuffer, "/sys/class/power_supply/ADP1/online");
else if (ffStrStartsWith(id, "macsmc-battery"))
ffStrbufSetS(tmpBuffer, "/sys/class/power_supply/macsmc-ac/online");
else
ffStrbufClear(tmpBuffer);
char online = '\0';
return ffReadFileData(tmpBuffer->chars, 1, &online) == 1 && online == '1';
}
static void parseBattery(int dfd, const char* id, FFBatteryOptions* options, FFlist* results)
{
FF_STRBUF_AUTO_DESTROY tmpBuffer = ffStrbufCreate();
if (!ffReadFileBufferRelative(dfd, "type", &tmpBuffer))
return;
ffStrbufTrimRightSpace(&tmpBuffer);
if(!ffStrbufIgnCaseEqualS(&tmpBuffer, "Battery"))
return;
if (ffReadFileBufferRelative(dfd, "scope", &tmpBuffer))
ffStrbufTrimRightSpace(&tmpBuffer);
if(ffStrbufIgnCaseEqualS(&tmpBuffer, "Device"))
return;
if (!ffReadFileBufferRelative(dfd, "capacity", &tmpBuffer))
return;
FFBatteryResult* result = ffListAdd(results);
result->capacity = ffStrbufToDouble(&tmpBuffer);
ffStrbufInit(&result->manufacturer);
if (ffReadFileBufferRelative(dfd, "manufacturer", &result->manufacturer))
ffStrbufTrimRightSpace(&result->manufacturer);
else if (ffStrEquals(id, "macsmc-battery")) ffStrbufSetStatic(&result->manufacturer, "Apple Inc.");
ffStrbufInit(&result->modelName);
if (ffReadFileBufferRelative(dfd, "model_name", &result->modelName))
ffStrbufTrimRightSpace(&result->modelName);
ffStrbufInit(&result->technology);
if (ffReadFileBufferRelative(dfd, "technology", &result->technology))
ffStrbufTrimRightSpace(&result->technology);
ffStrbufInit(&result->status);
if (ffReadFileBufferRelative(dfd, "status", &result->status))
ffStrbufTrimRightSpace(&result->status);
result->timeRemaining = -1;
if (ffStrbufEqualS(&result->status, "Discharging"))
{
if (ffReadFileBufferRelative(dfd, "time_to_empty_now", &tmpBuffer))
result->timeRemaining = (int32_t) ffStrbufToSInt(&tmpBuffer, 0);
else
{
if (ffReadFileBufferRelative(dfd, "charge_now", &tmpBuffer))
{
int64_t chargeNow = ffStrbufToSInt(&tmpBuffer, 0);
if (chargeNow > 0)
{
if (ffReadFileBufferRelative(dfd, "current_now", &tmpBuffer))
{
int64_t currentNow = ffStrbufToSInt(&tmpBuffer, INT64_MIN);
if (currentNow < 0) currentNow = -currentNow;
if (currentNow > 0)
result->timeRemaining = (int32_t) ((chargeNow * 3600) / currentNow);
}
}
}
}
if (checkAc(id, &tmpBuffer))
ffStrbufAppendS(&result->status, ", AC Connected");
}
else if (ffStrbufEqualS(&result->status, "Not charging") || ffStrbufEqualS(&result->status, "Full"))
ffStrbufSetStatic(&result->status, "AC Connected");
else if (ffStrbufEqualS(&result->status, "Charging"))
ffStrbufAppendS(&result->status, ", AC Connected");
else if (ffStrbufEqualS(&result->status, "Unknown"))
{
ffStrbufClear(&result->status);
if (checkAc(id, &tmpBuffer))
ffStrbufAppendS(&result->status, "AC Connected");
}
if (ffReadFileBufferRelative(dfd, "capacity_level", &tmpBuffer))
{
ffStrbufTrimRightSpace(&tmpBuffer);
if (ffStrbufEqualS(&tmpBuffer, "Critical"))
{
if (result->status.length)
ffStrbufAppendS(&result->status, ", Critical");
else
ffStrbufSetStatic(&result->status, "Critical");
}
}
ffStrbufInit(&result->serial);
if (ffReadFileBufferRelative(dfd, "serial_number", &result->serial))
ffStrbufTrimRightSpace(&result->serial);
if (ffReadFileBufferRelative(dfd, "cycle_count", &tmpBuffer))
{
int64_t cycleCount = ffStrbufToSInt(&tmpBuffer, 0);
result->cycleCount = cycleCount < 0 || cycleCount > UINT32_MAX ? 0 : (uint32_t) cycleCount;
}
ffStrbufInit(&result->manufactureDate);
if (ffReadFileBufferRelative(dfd, "manufacture_year", &tmpBuffer))
{
int year = (int) ffStrbufToSInt(&tmpBuffer, 0);
if (year > 0)
{
if (ffReadFileBufferRelative(dfd, "manufacture_month", &tmpBuffer))
{
int month = (int) ffStrbufToSInt(&tmpBuffer, 0);
if (month > 0)
{
if (ffReadFileBufferRelative(dfd, "manufacture_day", &tmpBuffer))
{
int day = (int) ffStrbufToSInt(&tmpBuffer, 0);
if (day > 0)
ffStrbufSetF(&result->manufactureDate, "%.4d-%.2d-%.2d", year, month, day);
}
}
}
}
}
result->temperature = FF_BATTERY_TEMP_UNSET;
if (options->temp)
{
if (ffReadFileBufferRelative(dfd, "temp", &tmpBuffer))
result->temperature = ffStrbufToDouble(&tmpBuffer) / 10;
}
}
const char* ffDetectBattery(FFBatteryOptions* options, FFlist* results)
{
FF_AUTO_CLOSE_DIR DIR* dirp = opendir("/sys/class/power_supply/");
if(dirp == NULL)
return "opendir(\"/sys/class/power_supply/\") == NULL";
struct dirent* entry;
while((entry = readdir(dirp)) != NULL)
{
if(entry->d_name[0] == '.')
continue;
FF_AUTO_CLOSE_FD int dfd = openat(dirfd(dirp), entry->d_name, O_RDONLY | O_CLOEXEC | O_PATH | O_DIRECTORY);
if (dfd > 0) parseBattery(dfd, entry->d_name, options, results);
}
return NULL;
}