]> mj.ucw.cz Git - osdd.git/blob - osd-batt.c
Add python library
[osdd.git] / osd-batt.c
1 /*
2  *      A Simple Battery Status Display via OSD
3  *
4  *      (c) 2007--2015 Martin Mares <mj@ucw.cz>
5  */
6
7 #undef DEBUG
8
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <unistd.h>
13 #include <dirent.h>
14 #include <getopt.h>
15 #include <fcntl.h>
16
17 #include "util.h"
18 #include "osd.h"
19
20 static int check_mode;
21 static int check_every;
22 static int warn_threshold = 600;
23
24 static int total_now, total_full, discharge_rate;
25 static int charge_time, discharge_time;
26 static int ac_online;
27 static unsigned int present_mask, charge_mask, discharge_mask;
28
29 static unsigned int last_charge_mask, last_discharge_mask;
30 static int last_ac_online = -1;
31
32 #define MAX_BATTS 4
33 #define BATT_NAME_LEN 32
34 static char batt_names[MAX_BATTS][BATT_NAME_LEN];
35
36 static char sys_dir[256];
37 #define BUFSIZE 256
38
39 static int sys_read(char *buf, char *attribute)
40 {
41   char name[256];
42   snprintf(name, sizeof(name), "%s/%s", sys_dir, attribute);
43
44   int fd = open(name, O_RDONLY);
45   if (fd < 0)
46     return 0;
47
48   int n = read(fd, buf, BUFSIZE);
49   close(fd);
50   if (n < 0)
51     return 0;
52
53   buf[BUFSIZE-1] = 0;
54   char *nl = strchr(buf, '\n');
55   if (nl)
56     *nl = 0;
57   DBG("\t%s=%s\n", attribute, buf);
58   return 1;
59 }
60
61 static int sys_read_int(char *attribute, int default_value)
62 {
63   char buf[BUFSIZE];
64   if (!sys_read(buf, attribute) || !buf[0])
65     return default_value;
66   else
67     return atoi(buf);
68 }
69
70 static int sys_read_int_variant(char *attr1, char *attr2, int default_value)
71 {
72   char buf[BUFSIZE];
73   if (sys_read(buf, attr1) && buf[0] ||
74       sys_read(buf, attr2) && buf[0])
75     return atoi(buf);
76   else
77     return default_value;
78 }
79
80 static void parse_ac(void)
81 {
82   ac_online = sys_read_int("online", 0);
83 }
84
85 static int get_batt_id(char *batt_name)
86 {
87   for (int i=0; i<MAX_BATTS; i++)
88     {
89       if (!strcmp(batt_names[i], batt_name))
90         return i;
91       if (!batt_names[i][0])
92         {
93           snprintf(batt_names[i], BATT_NAME_LEN, "%s", batt_name);
94           return i;
95         }
96     }
97   return MAX_BATTS;
98 }
99
100 static void parse_batt(char *batt_name)
101 {
102   int batt_id = get_batt_id(batt_name);
103   DBG("\t-> id %d\n", batt_id);
104
105   if (!sys_read_int("present", 1))
106     return;
107
108   char status[BUFSIZE];
109   int charging = sys_read(status, "status") && !strcmp(status, "Charging");
110   int charge_full = sys_read_int_variant("energy_full", "charge_full", 0);
111   int charge_now = sys_read_int_variant("energy_now", "charge_now", 0);
112   int current_now = sys_read_int_variant("power_now", "current_now", 0);
113
114   present_mask |= 1 << batt_id;
115   total_now += charge_now;
116   total_full += charge_full;
117   if (charging && current_now > 0)
118     {
119       charge_mask |= 1 << batt_id;
120       int ch = (long long)(charge_full - charge_now)*3600 / current_now;
121       if (ch > charge_time)
122         charge_time = ch;
123     }
124   else if (current_now > 0)
125     {
126       discharge_mask |= 1 << batt_id;
127       discharge_rate += current_now;
128     }
129 }
130
131 static void scan(void)
132 {
133   ac_online = 0;
134   charge_time = discharge_time = 0;
135   total_now = total_full = 0;
136   discharge_rate = 0;
137   present_mask = charge_mask = discharge_mask = 0;
138
139   const char dir[] = "/sys/class/power_supply";
140   DIR *d = opendir(dir);
141   if (!d)
142     return;
143
144   struct dirent *e;
145   while (e = readdir(d))
146     {
147       if (e->d_name[0] == '.')
148         continue;
149       snprintf(sys_dir, sizeof(sys_dir), "%s/%s", dir, e->d_name);
150       DBG("%s\n", sys_dir);
151
152       char type[BUFSIZE];
153       if (!sys_read(type, "type"))
154         continue;
155
156       if (!strcmp(type, "Mains"))
157         parse_ac();
158       else if (!strcmp(type, "Battery"))
159         parse_batt(e->d_name);
160     }
161   if (discharge_rate)
162     discharge_time = (long long) total_now*3600 / discharge_rate;
163   else
164     discharge_time = 1000000;
165
166   closedir(d);
167   DBG("=> Capacity: now=%d full=%d\n", total_now, total_full);
168   DBG("=> Charge: mask=%d time=%d\n", charge_mask, charge_time);
169   DBG("=> Discharge: mask=%d rate=%d time=%d\n", discharge_mask, discharge_rate, discharge_time);
170 }
171
172 static char *batt_mask(char *p, unsigned int mask)
173 {
174   if (present_mask & (present_mask-1))
175     {
176       char *p0 = p;
177       for (int i=0; mask; i++)
178         if (mask & (1 << i))
179           {
180             *p = (p == p0) ? ' ' : '+';
181             p++;
182             p += sprintf(p, "B%d", i);
183             mask &= ~(1 << i);
184           }
185     }
186   return p;
187 }
188
189 static void show(void)
190 {
191   char status[256];
192   char *p = status;
193   if (total_full)
194     p += sprintf(p, "%d%%", (int)((long long)100*total_now/total_full));
195   else
196     p += sprintf(p, "??%%");
197   if (discharge_mask && discharge_time < 1000000)
198     {
199       p += sprintf(p, "  %d:%02d  remains", discharge_time/3600, (discharge_time/60)%60);
200       batt_mask(p, discharge_mask);
201     }
202   else if (charge_mask)
203     {
204       p += sprintf(p, "  %d:%02d  charging", charge_time/3600, (charge_time/60)%60);
205       batt_mask(p, charge_mask);
206     }
207   else if (ac_online)
208     p += sprintf(p, " AC");
209   else
210     p += sprintf(p, " BATT");
211
212   struct osd_msg *msg = osd_new_msg();
213   osd_add_line(msg, NULL, status);
214   osd_send(msg);
215 }
216
217 static void show_if_warn(void)
218 {
219   if (discharge_mask && discharge_time < warn_threshold ||
220       last_ac_online >= 0 && (
221         charge_mask != last_charge_mask ||
222         discharge_mask != last_discharge_mask ||
223         ac_online != last_ac_online))
224     show();
225
226   last_charge_mask = charge_mask;
227   last_discharge_mask = discharge_mask;
228   last_ac_online = ac_online;
229 }
230
231 static void NONRET
232 usage(void)
233 {
234   fprintf(stderr, "\
235 Usage: osd-batt <options>\n\
236 \n\
237 Options:\n\
238 -c, --check\t\tDisplay status only if battery is low\n\
239 -e, --check-every=<sec>\tRun on background and check every <sec> seconds\n\
240 -w, --warn=<sec>\tBattery is low if less than <sec> seconds remain (default: 600)\n\
241 ");
242   exit(1);
243 }
244
245 static const char short_opts[] = "ce:w:";
246
247 static const struct option long_opts[] = {
248   { "check",            no_argument,            NULL,   'c' },
249   { "check-every",      required_argument,      NULL,   'e' },
250   { "warn",             required_argument,      NULL,   'w' },
251   { NULL,               0,                      NULL,   0   },
252 };
253
254 int main(int argc, char **argv)
255 {
256   int opt;
257   while ((opt = getopt_long(argc, argv, short_opts, long_opts, NULL)) >= 0)
258     switch (opt)
259       {
260       case 'c':
261         check_mode++;
262         break;
263       case 'e':
264         check_every = atoi(optarg);
265         break;
266       case 'w':
267         warn_threshold = atoi(optarg);
268         break;
269       default:
270         usage();
271       }
272   if (optind < argc)
273     usage();
274
275   if (check_every)
276     {
277       osd_fork();
278       for (;;)
279         {
280           scan();
281           show_if_warn();
282           osd_wait(check_every);
283         }
284     }
285
286   scan();
287   if (check_mode)
288     show_if_warn();
289   else
290     show();
291
292   return 0;
293 }