2 * A Simple Battery Status Display via OSD
4 * (c) 2007--2015 Martin Mares <mj@ucw.cz>
20 static int check_mode;
21 static int check_every;
22 static int warn_threshold = 600;
24 static int total_now, total_full, discharge_rate;
25 static int charge_time, discharge_time;
27 static unsigned int present_mask, charge_mask, discharge_mask;
29 static unsigned int last_charge_mask, last_discharge_mask;
30 static int last_ac_online = -1;
33 #define BATT_NAME_LEN 32
34 static char batt_names[MAX_BATTS][BATT_NAME_LEN];
36 static char sys_dir[256];
39 static int sys_read(char *buf, char *attribute)
42 snprintf(name, sizeof(name), "%s/%s", sys_dir, attribute);
44 int fd = open(name, O_RDONLY);
48 int n = read(fd, buf, BUFSIZE);
54 char *nl = strchr(buf, '\n');
57 DBG("\t%s=%s\n", attribute, buf);
61 static int sys_read_int(char *attribute, int default_value)
64 if (!sys_read(buf, attribute) || !buf[0])
70 static int sys_read_int_variant(char *attr1, char *attr2, int default_value)
73 if (sys_read(buf, attr1) && buf[0] ||
74 sys_read(buf, attr2) && buf[0])
80 static void parse_ac(void)
82 ac_online = sys_read_int("online", 0);
85 static int get_batt_id(char *batt_name)
87 for (int i=0; i<MAX_BATTS; i++)
89 if (!strcmp(batt_names[i], batt_name))
91 if (!batt_names[i][0])
93 snprintf(batt_names[i], BATT_NAME_LEN, "%s", batt_name);
100 static void parse_batt(char *batt_name)
102 int batt_id = get_batt_id(batt_name);
103 DBG("\t-> id %d\n", batt_id);
105 if (!sys_read_int("present", 1))
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);
114 present_mask |= 1 << batt_id;
115 total_now += charge_now;
116 total_full += charge_full;
117 if (charging && current_now > 0)
119 charge_mask |= 1 << batt_id;
120 int ch = (long long)(charge_full - charge_now)*3600 / current_now;
121 if (ch > charge_time)
124 else if (current_now > 0)
126 discharge_mask |= 1 << batt_id;
127 discharge_rate += current_now;
131 static void scan(void)
134 charge_time = discharge_time = 0;
135 total_now = total_full = 0;
137 present_mask = charge_mask = discharge_mask = 0;
139 const char dir[] = "/sys/class/power_supply";
140 DIR *d = opendir(dir);
145 while (e = readdir(d))
147 if (e->d_name[0] == '.')
149 snprintf(sys_dir, sizeof(sys_dir), "%s/%s", dir, e->d_name);
150 DBG("%s\n", sys_dir);
153 if (!sys_read(type, "type"))
156 if (!strcmp(type, "Mains"))
158 else if (!strcmp(type, "Battery"))
159 parse_batt(e->d_name);
162 discharge_time = (long long) total_now*3600 / discharge_rate;
164 discharge_time = 1000000;
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);
172 static char *batt_mask(char *p, unsigned int mask)
174 if (present_mask & (present_mask-1))
177 for (int i=0; mask; i++)
180 *p = (p == p0) ? ' ' : '+';
182 p += sprintf(p, "B%d", i);
189 static void show(void)
194 p += sprintf(p, "%d%%", (int)((long long)100*total_now/total_full));
196 p += sprintf(p, "??%%");
197 if (discharge_mask && discharge_time < 1000000)
199 p += sprintf(p, " %d:%02d remains", discharge_time/3600, (discharge_time/60)%60);
200 batt_mask(p, discharge_mask);
202 else if (charge_mask)
204 p += sprintf(p, " %d:%02d charging", charge_time/3600, (charge_time/60)%60);
205 batt_mask(p, charge_mask);
208 p += sprintf(p, " AC");
210 p += sprintf(p, " BATT");
212 struct osd_msg *msg = osd_new_msg();
213 osd_add_line(msg, NULL, status);
217 static void show_if_warn(void)
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))
226 last_charge_mask = charge_mask;
227 last_discharge_mask = discharge_mask;
228 last_ac_online = ac_online;
235 Usage: osd-batt <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\
245 static const char short_opts[] = "ce:w:";
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 },
254 int main(int argc, char **argv)
257 while ((opt = getopt_long(argc, argv, short_opts, long_opts, NULL)) >= 0)
264 check_every = atoi(optarg);
267 warn_threshold = atoi(optarg);
282 osd_wait(check_every);