64f5ce0804d28b79394409f7c903321185c33bf0
[debian/lxpanel.git] / plugins / batt / batt_sys.c
1 /*
2 * batt_sys.h
3 *
4 * Copyright 2009 Juergen Hötzel <juergen@archlinux.org>
5 * 2015 Henry Gebhardt <hsggebhardt@googlemail.com>
6 *
7 * Parts shameless stolen and glibified from acpi package
8 * Copyright (C) 2001 Grahame Bowland <grahame@angrygoats.net>
9 * (C) 2008-2009 Michael Meskes <meskes@debian.org>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
24 * MA 02110-1301, USA.
25 */
26
27
28 #ifdef HAVE_CONFIG_H
29 # include <config.h>
30 #endif
31
32 #include "batt_sys.h"
33 #include <glib/gstdio.h>
34
35 /* shrug: get rid of this */
36 #include <stdlib.h>
37 #include <string.h>
38
39 battery* battery_new() {
40 static int battery_num = 1;
41 battery * b = g_new0 ( battery, 1 );
42 b->type_battery = TRUE;
43 b->capacity_unit = "mAh";
44 b->energy_full = -1;
45 b->charge_full = -1;
46 b->voltage_now = -1;
47 b->energy_full_design = -1;
48 b->charge_full_design = -1;
49 b->energy_now = -1;
50 b->charge_now = -1;
51 b->current_now = -1;
52 b->power_now = -1;
53 b->state = NULL;
54 b->battery_num = battery_num;
55 b->seconds = -1;
56 b->percentage = -1;
57 b->poststr = NULL;
58 battery_num++;
59 return b;
60 }
61
62
63 static gchar* parse_info_file(battery *b, char *sys_file)
64 {
65 char *buf = NULL;
66 gchar *value = NULL;
67 GString *filename = g_string_new(ACPI_PATH_SYS_POWER_SUPPY);
68
69 g_string_append_printf (filename, "/%s/%s", b->path, sys_file);
70
71 if (g_file_get_contents(filename->str, &buf, NULL, NULL) == TRUE) {
72 value = g_strdup( buf );
73 value = g_strstrip( value );
74 g_free( buf );
75 }
76
77 g_string_free(filename, TRUE);
78
79 return value;
80 }
81
82 /* get_gint_from_infofile():
83 * If the sys_file exists, then its value is converted to an int,
84 * divided by 1000, and returned.
85 * Failure is indicated by returning -1. */
86 static gint get_gint_from_infofile(battery *b, gchar *sys_file)
87 {
88 gchar *file_content = parse_info_file(b, sys_file);
89
90 if (file_content != NULL)
91 return atoi(file_content) / 1000;
92
93 return -1;
94 }
95
96 static gchar* get_gchar_from_infofile(battery *b, gchar *sys_file)
97 {
98 return parse_info_file(b, sys_file);
99 }
100
101 void battery_print(battery *b, int show_capacity)
102 {
103 if ( b->type_battery )
104 {
105 if (b->state) {
106
107 printf("%s %d: %s, %d%%", BATTERY_DESC, b->battery_num - 1, b->state, b->percentage);
108
109 if (b->seconds > 0) {
110 int hours = b->seconds / 3600;
111 int seconds = b->seconds - 3600 * hours;
112 int minutes = seconds / 60;
113 seconds -= 60 * minutes;
114 printf(", %02d:%02d:%02d%s", hours, minutes, seconds,
115 b->poststr);
116 } else if (b->poststr != NULL) {
117 printf(", %s", b->poststr);
118 }
119
120 printf("\n");
121
122 if (show_capacity && b->charge_full_design > 0) {
123 int percentage = -1;
124 int charge_full = -1;
125 if (b->charge_full <= 100) {
126 /* some broken systems just give a percentage here */
127 percentage = b->charge_full;
128 charge_full = percentage * b->charge_full_design / 100;
129 } else {
130 percentage = b->charge_full * 100 / b->charge_full_design;
131 charge_full = b->charge_full;
132 }
133 if (percentage > 100)
134 percentage = 100;
135
136 printf ("%s %d: design capacity %d %s, "
137 "last full capacity %d %s = %d%%\n",
138 BATTERY_DESC, b->battery_num - 1, b->charge_full_design,
139 b->capacity_unit, charge_full, b->capacity_unit,
140 percentage);
141 }
142 }
143 }
144 }
145
146
147 static gboolean battery_inserted(gchar* path)
148 {
149 if (path == NULL)
150 return FALSE;
151
152 GString *dirname = g_string_new(ACPI_PATH_SYS_POWER_SUPPY);
153 GDir *dir;
154
155 g_string_append_printf (dirname, "/%s/", path);
156 dir = g_dir_open(dirname->str, 0, NULL);
157 if (dir)
158 g_dir_close(dir);
159 g_string_free(dirname, TRUE);
160
161 return dir ? TRUE : FALSE;
162 }
163
164
165 battery* battery_update(battery *b)
166 {
167 gchar *gctmp;
168
169 if (b == NULL)
170 return NULL;
171
172 if (!battery_inserted(b->path))
173 return NULL;
174
175 /* read from sysfs */
176 b->charge_now = get_gint_from_infofile(b, "charge_now");
177 b->energy_now = get_gint_from_infofile(b, "energy_now");
178
179 b->current_now = get_gint_from_infofile(b, "current_now");
180 b->power_now = get_gint_from_infofile(b, "power_now");
181 /* FIXME: Some battery drivers report -1000 when the discharge rate is
182 * unavailable. Others use negative values when discharging. Best we can do
183 * is to treat -1 as an error, and take the absolute value otherwise.
184 * Ideally the kernel would not export the sysfs file when the value is not
185 * available. */
186 if (b->current_now < -1)
187 b->current_now = - b->current_now;
188
189 b->charge_full = get_gint_from_infofile(b, "charge_full");
190 b->energy_full = get_gint_from_infofile(b, "energy_full");
191
192 b->charge_full_design = get_gint_from_infofile(b, "charge_full_design");
193 b->energy_full_design = get_gint_from_infofile(b, "energy_full_design");
194
195 b->voltage_now = get_gint_from_infofile(b, "voltage_now");
196
197 gctmp = get_gchar_from_infofile(b, "type");
198 b->type_battery = gctmp ? (strcasecmp(gctmp, "battery") == 0) : TRUE;
199
200 b->state = get_gchar_from_infofile(b, "status");
201 if (!b->state)
202 b->state = get_gchar_from_infofile(b, "state");
203 if (!b->state) {
204 if (b->charge_now != -1 || b->energy_now != -1
205 || b->charge_full != -1 || b->energy_full != -1)
206 b->state = "available";
207 else
208 b->state = "unavailable";
209 }
210
211
212 /* convert energy values (in mWh) to charge values (in mAh) if needed and possible */
213
214 if (b->energy_full != -1 && b->charge_full == -1) {
215 if (b->voltage_now != -1 && b->voltage_now != 0) {
216 b->charge_full = b->energy_full * 1000 / b->voltage_now;
217 } else {
218 b->charge_full = b->energy_full;
219 b->capacity_unit = "mWh";
220 }
221 }
222
223 if (b->energy_full_design != -1 && b->charge_full_design == -1) {
224 if (b->voltage_now != -1 && b->voltage_now != 0) {
225 b->charge_full_design = b->energy_full_design * 1000 / b->voltage_now;
226 } else {
227 b->charge_full_design = b->energy_full_design;
228 b->capacity_unit = "mWh";
229 }
230 }
231
232 if (b->energy_now != -1 && b->charge_now == -1) {
233 if (b->voltage_now != -1 && b->voltage_now != 0) {
234 b->charge_now = b->energy_now * 1000 / b->voltage_now;
235 if (b->current_now != -1)
236 b->current_now = b->current_now * 1000 / b->voltage_now;
237 } else {
238 b->charge_now = b->energy_now;
239 }
240 }
241
242 if (b->power_now < -1)
243 b->power_now = - b->power_now;
244 else if (b->power_now == -1 && b->voltage_now != -1 && b->current_now != -1)
245 b->power_now = b->voltage_now * b->current_now / 1000; // P = U*I
246 if (b->power_now != -1 && b->current_now == -1) {
247 if (b->voltage_now != -1 && b->voltage_now != 0)
248 b->current_now = b->power_now * 1000 / b->voltage_now;
249 }
250
251
252 if (b->charge_full < MIN_CAPACITY)
253 b->percentage = 0;
254 else {
255 int promille = (b->charge_now * 1000) / b->charge_full;
256 b->percentage = (promille + 5) / 10; /* round properly */
257 }
258 if (b->percentage > 100)
259 b->percentage = 100;
260
261
262 if (b->current_now == -1) {
263 b->poststr = "rate information unavailable";
264 b->seconds = -1;
265 } else if (!strcasecmp(b->state, "charging")) {
266 if (b->current_now > MIN_PRESENT_RATE) {
267 b->seconds = 3600 * (b->charge_full - b->charge_now) / b->current_now;
268 b->poststr = " until charged";
269 } else {
270 b->poststr = "charging at zero rate - will never fully charge.";
271 b->seconds = -1;
272 }
273 } else if (!strcasecmp(b->state, "discharging")) {
274 if (b->current_now > MIN_PRESENT_RATE) {
275 b->seconds = 3600 * b->charge_now / b->current_now;
276 b->poststr = " remaining";
277 } else {
278 b->poststr = "discharging at zero rate - will never fully discharge.";
279 b->seconds = -1;
280 }
281 } else {
282 b->poststr = NULL;
283 b->seconds = -1;
284 }
285
286 return b;
287 }
288
289
290 battery *battery_get() {
291 GError * error = NULL;
292 const gchar *entry;
293 GDir * dir = g_dir_open( ACPI_PATH_SYS_POWER_SUPPY, 0, &error );
294 battery *b = NULL;
295 if ( dir == NULL )
296 {
297 g_warning( "NO ACPI/sysfs support in kernel: %s", error->message );
298 return NULL;
299 }
300 while ( ( entry = g_dir_read_name (dir) ) != NULL )
301 {
302 b = battery_new();
303 b->path = g_strdup( entry );
304 battery_update ( b );
305 if ( b->type_battery == TRUE )
306 break;
307 /* ignore non-batteries */
308 else {
309 g_free(b);
310 b = NULL;
311 }
312 }
313 g_dir_close( dir );
314 return b;
315 }
316
317 void battery_free(battery* bat)
318 {
319 if (bat) {
320 g_free(bat->path);
321 g_free(bat);
322 }
323 }
324
325 gboolean battery_is_charging( battery *b )
326 {
327 if (!b->state)
328 return TRUE; // Same as "Unkown"
329 return ( strcasecmp( b->state, "Unknown" ) == 0
330 || strcasecmp( b->state, "Full" ) == 0
331 || strcasecmp( b->state, "Charging" ) == 0
332 || b->current_now == 0 ); /* bug sf.net, #720 */
333 }
334
335 gint battery_get_remaining( battery *b )
336 {
337 return b->seconds;
338 }
339
340
341 /* vim: set sw=4 et sts=4 : */