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