1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
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;
}
|