Skip to content

Instantly share code, notes, and snippets.

@tomtzook
Last active May 5, 2024 06:29
Show Gist options
  • Save tomtzook/d9ab3327fe5e78d24569cc1bba284b68 to your computer and use it in GitHub Desktop.
Save tomtzook/d9ab3327fe5e78d24569cc1bba284b68 to your computer and use it in GitHub Desktop.
Get battery status on a Linux laptop from a C program
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <dirent.h>
#include <linux/limits.h>
#include <regex.h>
#include <stdbool.h>
#include <stdlib.h>
#define DATADIR "/sys/class/power_supply"
#define CHARGE_NOW "energy_now"
#define CHARGE_FULL "energy_full"
#define CHARGE_STATUS "status"
#define CHARGE_STATUS_CHARGING 0
#define CHARGE_STATUS_DISCHARGING 1
#define CHARGE_STATUS_FULL 2
#define CHARGE_STATUS_CHARGING_STR "Charging"
#define CHARGE_STATUS_DISCHARGING_STR "Discharging"
#define CHARGE_STATUS_FULL_STR "Full"
#define ERROR_FOLDER_NOT_FOUND (-1)
#define ERROR_FILE_NOT_FOUND (-2)
#define ERROR_REGEX_COMPILE (-3)
#define ERROR_REGEX_FIND (-4)
#define ERROR_FILE_READING (-5)
#define ERROR_CHARGE_STATUS_UNKNOWN (-6)
static int read_charge_level(const char* data_dir,
const char* sub_dir,
char* buffer,
float* charge_level) {
FILE* file_current = NULL;
FILE* file_full = NULL;
long current;
long full;
int result = 0;
snprintf(buffer, PATH_MAX, "%s/%s/%s", data_dir, sub_dir, CHARGE_NOW);
file_current = fopen(buffer, "r");
snprintf(buffer, PATH_MAX, "%s/%s/%s", data_dir, sub_dir, CHARGE_FULL);
file_full = fopen(buffer, "r");
if (file_current == NULL || file_full == NULL) {
result = ERROR_FILE_NOT_FOUND;
goto clean;
}
if (fscanf(file_current, "%ld", &current) != 1 ||
fscanf(file_full, "%ld", &full) != 1) {
result = ERROR_FILE_READING;
goto clean;
}
*charge_level = (current / (float)full);
clean:
if (file_current != NULL) fclose(file_current);
if (file_full != NULL) fclose(file_full);
return result;
}
static int read_charge_status(const char* data_dir,
const char* sub_dir,
char* buffer,
int* charge_status) {
FILE* file = NULL;
int result = 0;
snprintf(buffer, PATH_MAX, "%s/%s/%s", data_dir, sub_dir, CHARGE_STATUS);
file = fopen(buffer, "r");
if (file == NULL) {
result = ERROR_FILE_NOT_FOUND;
goto clean;
}
if (fscanf(file, "%s", buffer) != 1) {
result = ERROR_FILE_READING;
goto clean;
}
printf("%s\n", buffer);
if (strcmp(buffer, CHARGE_STATUS_CHARGING_STR) == 0) {
*charge_status = CHARGE_STATUS_CHARGING;
} else if (strcmp(buffer, CHARGE_STATUS_DISCHARGING_STR) == 0) {
*charge_status = CHARGE_STATUS_DISCHARGING;
} else if (strcmp(buffer, CHARGE_STATUS_FULL_STR) == 0) {
*charge_status = CHARGE_STATUS_FULL;
} else {
result = ERROR_CHARGE_STATUS_UNKNOWN;
goto clean;
}
clean:
if (file != NULL) fclose(file);
return result;
}
int get_battery_status(float *level, int *charge_status) {
DIR* directory;
struct dirent* dirent;
char buffer[PATH_MAX];
int result = 0;
regex_t regex;
bool data_found = false;
if ((directory = opendir(DATADIR)) == NULL) {
return ERROR_FOLDER_NOT_FOUND;
}
while ((dirent = readdir(directory)) != NULL) {
snprintf(buffer, PATH_MAX, "%s/%s", DATADIR, dirent->d_name);
if (regcomp(&regex, "BAT[0-9]+", REG_EXTENDED) != 0) {
result = ERROR_REGEX_COMPILE;
goto clean;
}
if (regexec(&regex, dirent->d_name, 0, NULL, 0) != 0) {
regfree(&regex);
continue;
}
regfree(&regex);
result = read_charge_level(DATADIR, dirent->d_name,
buffer, level);
if (result != 0) {
continue;
}
result = read_charge_status(DATADIR, dirent->d_name,
buffer, charge_status);
if (result == 0) {
data_found = true;
break;
}
}
if (!data_found) {
result = ERROR_REGEX_FIND;
goto clean;
}
clean:
if (directory != NULL) closedir(directory);
return result;
}
int main(int argc, char** argv) {
float level;
int charge_status;
int result = get_battery_status(&level, &charge_status);
if (result == 0) {
char charge_status_str[20] = {0};
switch (charge_status) {
case CHARGE_STATUS_CHARGING:
strcpy(charge_status_str, CHARGE_STATUS_CHARGING_STR);
break;
case CHARGE_STATUS_DISCHARGING:
strcpy(charge_status_str, CHARGE_STATUS_DISCHARGING_STR);
break;
case CHARGE_STATUS_FULL:
strcpy(charge_status_str, CHARGE_STATUS_FULL_STR);
break;
}
printf("Level: %.2f, Charge: %s\n", level, charge_status_str);
}
return result;
}
@tomtzook
Copy link
Author

tomtzook commented May 9, 2020

Not sure about consistency of the CHARGE_NOW and CHARGE_FULL files across versions. So might need adaptation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment