aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--Makefile26
-rw-r--r--lowbat.c155
-rwxr-xr-xshlowbat26
4 files changed, 209 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..f46a5a0
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+lowbat
+lowbat.o
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..180a311
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,26 @@
+# Define variables
+CC = gcc
+CFLAGS = -std=c99 -Wall $(shell pkg-config --cflags libnotify)
+LDFLAGS = $(shell pkg-config --libs libnotify)
+LIBS = -lnotify
+TARGET = lowbat
+SRCS = lowbat.c
+OBJS = $(SRCS:.c=.o)
+
+# Default target
+all: $(TARGET)
+
+# Build target
+$(TARGET): $(OBJS)
+ $(CC) $(CFLAGS) -o $@ $(OBJS) $(LDFLAGS) $(LIBS)
+
+# Compile source files
+%.o: %.c
+ $(CC) $(CFLAGS) -c $< -o $@
+
+# Clean up build files
+clean:
+ rm -f $(TARGET) $(OBJS)
+
+# Phony targets
+.PHONY: all clean
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;
+}
diff --git a/shlowbat b/shlowbat
new file mode 100755
index 0000000..a02ce56
--- /dev/null
+++ b/shlowbat
@@ -0,0 +1,26 @@
+#!/bin/sh
+psupath=/sys/class/power_supply
+warning_level=0 # 0 = not warned, 1 = warned, 2 = warned critical
+
+sum_props() {
+ for prop in $(cat $psupath/*/$1); do sum=$(( $sum + $prop )); done
+ echo $sum
+}
+is_discharging() { cat $psupath/*/status | grep "Discharging" > /dev/null ; }
+
+while true; do
+ capacity=$(( $(sum_props energy_now) * 100 / $(sum_props energy_full) ))
+
+ if is_discharging; then
+ if [ $capacity -le 5 ] && [ $warning_level -lt 2 ]; then
+ notify_send "Critical Low Battery Warning" "${capacity}% remains"
+ elif [ $capacity -le 20 ] && [ $warning_level -lt 1 ]; then
+ notify_send "Low Battery Warning" "${capacity}% remains"
+ fi
+ else
+ warning_level=0
+ fi
+
+ printf "\r%d%%" $capacity
+ sleep 3
+done