diff options
author | Timmy Keller <tjk@tjkeller.xyz> | 2024-08-10 23:22:29 -0500 |
---|---|---|
committer | Timmy Keller <tjk@tjkeller.xyz> | 2024-08-10 23:22:29 -0500 |
commit | 8e929bd44420ed9a3bc0b6a030cebbdfcafebf4d (patch) | |
tree | 62c41cbd55a958135ede2baebb3fddd7bc5eb6a7 /lowbat.c | |
download | lowbat-8e929bd44420ed9a3bc0b6a030cebbdfcafebf4d.tar.xz lowbat-8e929bd44420ed9a3bc0b6a030cebbdfcafebf4d.zip |
initial commit
Diffstat (limited to 'lowbat.c')
-rw-r--r-- | lowbat.c | 155 |
1 files changed, 155 insertions, 0 deletions
diff --git a/lowbat.c b/lowbat.c new file mode 100644 index 0000000..14f10a2 --- /dev/null +++ b/lowbat.c @@ -0,0 +1,155 @@ +/* LINUX DOCS: https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-class-power + * (C) 2024 Tim Keller <tjkeller.xyz> + * MIT License + */ + +#include <libnotify/notify.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <dirent.h> + +#define PERIOD 3 // number of seconds before each check +#define PSUPATH "/sys/class/power_supply" // number of seconds before each check +#define MAX_BATTERIES 8 +#define MAX_PSU_NAME_LEN 8 +#define MAX_PROP_PATH_LEN 64 + +// variables +static char batteries[MAX_BATTERIES][MAX_PSU_NAME_LEN]; +static unsigned int num_batteries; +static FILE *fp; +static char prop_path[MAX_PROP_PATH_LEN]; + +// functions +void load_installed_batteries() { + // search through "power supplies" for "status" property (only available on batteries) + DIR *dir, *subdir; + struct dirent *entry, *subentry; + + if ((dir = opendir(PSUPATH)) == NULL) { + fprintf(stderr, "failed to see power supplies"); // TODO + exit(EXIT_FAILURE); + } + + // power supplies TODO warn user if max batteries exceeded + num_batteries = 0; + while ((entry = readdir(dir)) != NULL && num_batteries < MAX_BATTERIES) { + if (entry->d_name[0] == '.') + continue; // skip . and .. + if (strlen(entry->d_name) > MAX_PSU_NAME_LEN) { + fprintf(stderr, "the power supply name was too long: %s", entry->d_name); + exit(EXIT_FAILURE); + } + + // power supplies will always be a directory, no need to check beforehand :) + snprintf(prop_path, MAX_PROP_PATH_LEN, PSUPATH "/%s", entry->d_name); + + if ((subdir = opendir(prop_path)) == NULL) { + fprintf(stderr, "failed to see power supplies sub\n"); // TODO + exit(EXIT_FAILURE); + } + + // power supply properties + while ((subentry = readdir(subdir)) != NULL) { + // add to list of batteries if it has the status property + if (strcmp(subentry->d_name, "status") == 0) { + strncpy(batteries[num_batteries], entry->d_name, MAX_PSU_NAME_LEN); + num_batteries++; + break; + } + } + closedir(subdir); + } + closedir(dir); +} + +void notifysend(char *title, char *content) { + NotifyNotification *notif; + notify_init("lowbat"); + notif = notify_notification_new(title, content, NULL); + + notify_notification_show(notif, NULL); +} + +void read_prop(char *psu, char *prop, char *fmt, void *out) { + /* this function is only designed to read one var at a time because all bat + * properties will only be one var */ + snprintf(prop_path, MAX_PROP_PATH_LEN, PSUPATH "/%s/%s", psu, prop); + + if ((fp = fopen(prop_path, "r")) != NULL) { + fscanf(fp, fmt, out); + fclose(fp); + } + else { + printf("failed to read fmt '%s' from property %s\n", fmt, prop_path); // TODO fprintf me plz + exit(EXIT_FAILURE); + } +} + +int get_total_capacity() { + unsigned long int energy_full = 0, energy_now = 0, x; + int i; + + for (i = 0; i < num_batteries; i++) { + read_prop(batteries[i], "energy_full", "%ld", &x); + energy_full += x; + read_prop(batteries[i], "energy_now", "%ld", &x); + energy_now += x; + } + + return energy_now * 100 / energy_full; +} + +int is_discharging() { + char status[12]; // valid values: "Unknown", "Charging", "Discharging", "Not charging", "Full" + int i; + + for (i = 0; i < num_batteries; i++) { + read_prop(batteries[i], "status", "%s", &status); + if (strcmp(status, "Discharging") != 0) + return 0; + } + + return 1; +} + +int main() { + int discharging, capacity; + int w_lowbat = 0, w_lowbat_crit = 0; // 1 if warned already + char msg[32]; + + load_installed_batteries(); + if (!num_batteries) { + fprintf(stderr, "no batteries installed in system. exiting...\n"); // TODO + exit(EXIT_FAILURE); + } + + while (1) { + load_installed_batteries(); + discharging = is_discharging(); + capacity = get_total_capacity(); + + if (discharging) { + if (capacity <= 5 && !w_lowbat_crit) { + sprintf(msg, "%d%% remains", capacity); // TODO snprintf me plz + notifysend("Critical Low Battery Warning", msg); + + w_lowbat_crit = w_lowbat = 1; + } else if (capacity <= 20 && !w_lowbat) { + sprintf(msg, "%d%% remains", capacity); + notifysend("Low Battery Warning", msg); + + w_lowbat = 1; + } + } else { + w_lowbat_crit = w_lowbat = 0; // clear conditions + } + + printf("\r%d%%", capacity); + fflush(stdout); + sleep(PERIOD); + } + + return 0; +} |