Merging upstream version 0.5.9.
[debian/lxpanel.git] / src / 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 void battery_update(battery *b)
147 {
148 gchar *gctmp;
149
150 /* read from sysfs */
151 b->charge_now = get_gint_from_infofile(b, "charge_now");
152 b->energy_now = get_gint_from_infofile(b, "energy_now");
153
154 b->current_now = get_gint_from_infofile(b, "current_now");
155 b->power_now = get_gint_from_infofile(b, "power_now");
156 /* FIXME: Some battery drivers report -1000 when the discharge rate is
157 * unavailable. Others use negative values when discharging. Best we can do
158 * is to treat -1 as an error, and take the absolute value otherwise.
159 * Ideally the kernel would not export the sysfs file when the value is not
160 * available. */
161 if (b->current_now < -1)
162 b->current_now = - b->current_now;
163 if (b->power_now < -1)
164 b->power_now = - b->power_now;
165
166 b->charge_full = get_gint_from_infofile(b, "charge_full");
167 b->energy_full = get_gint_from_infofile(b, "energy_full");
168
169 b->charge_full_design = get_gint_from_infofile(b, "charge_full_design");
170 b->energy_full_design = get_gint_from_infofile(b, "energy_full_design");
171
172 b->voltage_now = get_gint_from_infofile(b, "voltage_now");
173
174 gctmp = get_gchar_from_infofile(b, "type");
175 b->type_battery = gctmp ? (strcasecmp(gctmp, "battery") == 0) : TRUE;
176
177 b->state = get_gchar_from_infofile(b, "status");
178 if (!b->state)
179 b->state = get_gchar_from_infofile(b, "state");
180 if (!b->state) {
181 if (b->charge_now != -1 || b->energy_now != -1
182 || b->charge_full != -1 || b->energy_full != -1)
183 b->state = "available";
184 else
185 b->state = "unavailable";
186 }
187
188
189 /* convert energy values (in mWh) to charge values (in mAh) if needed and possible */
190
191 if (b->energy_full != -1 && b->charge_full == -1) {
192 if (b->voltage_now != -1) {
193 b->charge_full = b->energy_full * 1000 / b->voltage_now;
194 } else {
195 b->charge_full = b->energy_full;
196 b->capacity_unit = "mWh";
197 }
198 }
199
200 if (b->energy_full_design != -1 && b->charge_full_design == -1) {
201 if (b->voltage_now != -1) {
202 b->charge_full_design = b->energy_full_design * 1000 / b->voltage_now;
203 } else {
204 b->charge_full_design = b->energy_full_design;
205 b->capacity_unit = "mWh";
206 }
207 }
208
209 if (b->energy_now != -1 && b->charge_now == -1) {
210 if (b->voltage_now != -1) {
211 b->charge_now = b->energy_now * 1000 / b->voltage_now;
212 if (b->current_now != -1)
213 b->current_now = b->current_now * 1000 / b->voltage_now;
214 } else {
215 b->charge_now = b->energy_now;
216 }
217 }
218
219 if (b->power_now != -1 && b->current_now == -1) {
220 if (b->voltage_now != -1 && b->voltage_now != 0)
221 b->current_now = b->power_now * 1000 / b->voltage_now;
222 }
223
224
225 if (b->charge_full < MIN_CAPACITY)
226 b->percentage = 0;
227 else {
228 int promille = (b->charge_now * 1000) / b->charge_full;
229 b->percentage = (promille + 5) / 10; /* round properly */
230 }
231 if (b->percentage > 100)
232 b->percentage = 100;
233
234
235 if (b->current_now == -1) {
236 b->poststr = "rate information unavailable";
237 b->seconds = -1;
238 } else if (!strcasecmp(b->state, "charging")) {
239 if (b->current_now > MIN_PRESENT_RATE) {
240 b->seconds = 3600 * (b->charge_full - b->charge_now) / b->current_now;
241 b->poststr = " until charged";
242 } else {
243 b->poststr = "charging at zero rate - will never fully charge.";
244 b->seconds = -1;
245 }
246 } else if (!strcasecmp(b->state, "discharging")) {
247 if (b->current_now > MIN_PRESENT_RATE) {
248 b->seconds = 3600 * b->charge_now / b->current_now;
249 b->poststr = " remaining";
250 } else {
251 b->poststr = "discharging at zero rate - will never fully discharge.";
252 b->seconds = -1;
253 }
254 } else {
255 b->poststr = NULL;
256 b->seconds = -1;
257 }
258 }
259
260
261 battery *battery_get() {
262 GError * error = NULL;
263 const gchar *entry;
264 GDir * dir = g_dir_open( ACPI_PATH_SYS_POWER_SUPPY, 0, &error );
265 battery *b = NULL;
266 if ( dir == NULL )
267 {
268 g_warning( "NO ACPI/sysfs support in kernel: %s", error->message );
269 return NULL;
270 }
271 while ( ( entry = g_dir_read_name (dir) ) != NULL )
272 {
273 b = battery_new();
274 b->path = g_strdup( entry );
275 battery_update ( b );
276 if ( b->type_battery == TRUE )
277 break;
278 /* ignore non-batteries */
279 else {
280 g_free(b);
281 b = NULL;
282 }
283 }
284 g_dir_close( dir );
285 return b;
286 }
287
288 gboolean battery_is_charging( battery *b )
289 {
290 if (!b->state)
291 return TRUE; // Same as "Unkown"
292 return ( strcasecmp( b->state, "Unknown" ) == 0 ||
293 strcasecmp( b->state, "Full" ) == 0
294 || strcasecmp( b->state, "Charging" ) == 0 );
295 }
296
297 gint battery_get_remaining( battery *b )
298 {
299 return b->seconds;
300 }
301
302