#endif
struct battery_info {
+ int full_design;
+ int full_last;
+ int remaining;
+
int present_rate;
int seconds_remaining;
float percentage_remaining;
charging_status_t status;
};
-/*
- * Estimate the number of seconds remaining in state 'status'.
- *
- * Assumes a constant (dis)charge rate.
- */
-static int seconds_remaining_from_rate(charging_status_t status, float full_design, float remaining, float present_rate) {
- if (status == CS_CHARGING)
- return 3600.0 * (full_design - remaining) / present_rate;
- else if (status == CS_DISCHARGING)
- return 3600.0 * remaining / present_rate;
- else
- return 0;
-}
+static bool slurp_battery_info(struct battery_info *batt_info, yajl_gen json_gen, char *buffer, int number, const char *path, const char *format_down) {
+ char *outwalk = buffer;
-static bool slurp_battery_info(struct battery_info *batt_info, yajl_gen json_gen, char *buffer, int number, const char *path, const char *format_down, bool last_full_capacity) {
+#if defined(LINUX)
char buf[1024];
const char *walk, *last;
- char *outwalk = buffer;
bool watt_as_unit = false;
- int full_design = -1,
- remaining = -1,
- voltage = -1;
-
-#if defined(LINUX)
+ int voltage = -1;
char batpath[512];
sprintf(batpath, path, number);
INSTANCE(batpath);
if (BEGINS_WITH(last, "POWER_SUPPLY_ENERGY_NOW")) {
watt_as_unit = true;
- remaining = atoi(walk + 1);
+ batt_info->remaining = atoi(walk + 1);
} else if (BEGINS_WITH(last, "POWER_SUPPLY_CHARGE_NOW")) {
watt_as_unit = false;
- remaining = atoi(walk + 1);
+ batt_info->remaining = atoi(walk + 1);
} else if (BEGINS_WITH(last, "POWER_SUPPLY_CURRENT_NOW"))
batt_info->present_rate = abs(atoi(walk + 1));
else if (BEGINS_WITH(last, "POWER_SUPPLY_VOLTAGE_NOW"))
batt_info->status = CS_DISCHARGING;
else if (BEGINS_WITH(last, "POWER_SUPPLY_STATUS="))
batt_info->status = CS_UNKNOWN;
- else {
- /* The only thing left is the full capacity */
- if (last_full_capacity) {
- if (!BEGINS_WITH(last, "POWER_SUPPLY_ENERGY_FULL") &&
- !BEGINS_WITH(last, "POWER_SUPPLY_CHARGE_FULL"))
- continue;
- } else {
- if (!BEGINS_WITH(last, "POWER_SUPPLY_CHARGE_FULL_DESIGN") &&
- !BEGINS_WITH(last, "POWER_SUPPLY_ENERGY_FULL_DESIGN"))
- continue;
- }
-
- full_design = atoi(walk + 1);
- }
+ else if (BEGINS_WITH(last, "POWER_SUPPLY_CHARGE_FULL_DESIGN") ||
+ BEGINS_WITH(last, "POWER_SUPPLY_ENERGY_FULL_DESIGN"))
+ batt_info->full_design = atoi(walk + 1);
+ else if (BEGINS_WITH(last, "POWER_SUPPLY_ENERGY_FULL") ||
+ BEGINS_WITH(last, "POWER_SUPPLY_CHARGE_FULL"))
+ batt_info->full_last = atoi(walk + 1);
}
/* the difference between POWER_SUPPLY_ENERGY_NOW and
* POWER_SUPPLY_CHARGE_NOW is the unit of measurement. The energy is
* given in mWh, the charge in mAh. So calculate every value given in
* ampere to watt */
- if (!watt_as_unit) {
+ if (!watt_as_unit && voltage != -1) {
batt_info->present_rate = (((float)voltage / 1000.0) * ((float)batt_info->present_rate / 1000.0));
-
- if (voltage != -1) {
- remaining = (((float)voltage / 1000.0) * ((float)remaining / 1000.0));
- full_design = (((float)voltage / 1000.0) * ((float)full_design / 1000.0));
- }
- }
-
- if ((full_design == -1) || (remaining == -1)) {
- OUTPUT_FULL_TEXT(format_down);
- return false;
- }
-
- batt_info->percentage_remaining = (((float)remaining / (float)full_design) * 100);
- /* Some batteries report POWER_SUPPLY_CHARGE_NOW=<full_design> when fully
- * charged, even though that’s plainly wrong. For people who chose to see
- * the percentage calculated based on the last full capacity, we clamp the
- * value to 100%, as that makes more sense.
- * See http://bugs.debian.org/785398 */
- if (last_full_capacity && batt_info->percentage_remaining > 100) {
- batt_info->percentage_remaining = 100;
- }
-
- if (batt_info->present_rate > 0 && batt_info->status != CS_FULL) {
- batt_info->seconds_remaining = seconds_remaining_from_rate(batt_info->status, full_design, remaining, batt_info->present_rate);
+ batt_info->remaining = (((float)voltage / 1000.0) * ((float)batt_info->remaining / 1000.0));
+ batt_info->full_design = (((float)voltage / 1000.0) * ((float)batt_info->full_design / 1000.0));
}
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
int state;
batt_info->status = CS_CHARGING;
else
batt_info->status = CS_DISCHARGING;
-
- full_design = sysctl_rslt;
#elif defined(__OpenBSD__)
/*
* We're using apm(4) here, which is the interface to acpi(4) on amd64/i386 and
/*
* Using envsys(4) via sysmon(4).
*/
- int fd, rval, last_full_cap;
+ bool watt_as_unit = false;
+ int voltage = -1;
+ int fd, rval;
bool is_found = false;
char *sensor_desc;
bool is_full = false;
/* iterate over the dictionary returned by the kernel */
while ((obj = prop_object_iterator_next(iter)) != NULL) {
/* skip this dict if it's not what we're looking for */
- if ((strlen(prop_dictionary_keysym_cstring_nocopy(obj)) == strlen(sensor_desc)) &&
- (strncmp(sensor_desc,
- prop_dictionary_keysym_cstring_nocopy(obj),
- strlen(sensor_desc)) != 0))
+ if (strcmp(sensor_desc,
+ prop_dictionary_keysym_cstring_nocopy(obj)) != 0)
continue;
is_found = true;
while ((obj2 = prop_object_iterator_next(iter2)) != NULL) {
obj3 = prop_dictionary_get(obj2, "description");
- if (obj3 &&
- strlen(prop_string_cstring_nocopy(obj3)) == 8 &&
- strncmp("charging",
- prop_string_cstring_nocopy(obj3),
- 8) == 0) {
+ if (obj3 == NULL)
+ continue;
+
+ if (strcmp("charging", prop_string_cstring_nocopy(obj3)) == 0) {
obj3 = prop_dictionary_get(obj2, "cur-value");
if (prop_number_integer_value(obj3))
batt_info->status = CS_CHARGING;
else
batt_info->status = CS_DISCHARGING;
-
- continue;
- }
-
- if (obj3 &&
- strlen(prop_string_cstring_nocopy(obj3)) == 6 &&
- strncmp("charge",
- prop_string_cstring_nocopy(obj3),
- 6) == 0) {
+ } else if (strcmp("charge", prop_string_cstring_nocopy(obj3)) == 0) {
obj3 = prop_dictionary_get(obj2, "cur-value");
obj4 = prop_dictionary_get(obj2, "max-value");
obj5 = prop_dictionary_get(obj2, "type");
- remaining = prop_number_integer_value(obj3);
- full_design = prop_number_integer_value(obj4);
+ batt_info->remaining = prop_number_integer_value(obj3);
+ batt_info->full_design = prop_number_integer_value(obj4);
- if (remaining == full_design)
+ if (batt_info->remaining == batt_info->full_design)
is_full = true;
- if (strncmp("Ampere hour",
- prop_string_cstring_nocopy(obj5),
- 11) == 0)
+ if (strcmp("Ampere hour", prop_string_cstring_nocopy(obj5)) == 0)
watt_as_unit = false;
else
watt_as_unit = true;
-
- continue;
- }
-
- if (obj3 &&
- strlen(prop_string_cstring_nocopy(obj3)) == 14 &&
- strncmp("discharge rate",
- prop_string_cstring_nocopy(obj3),
- 14) == 0) {
+ } else if (strcmp("discharge rate", prop_string_cstring_nocopy(obj3)) == 0) {
obj3 = prop_dictionary_get(obj2, "cur-value");
batt_info->present_rate = prop_number_integer_value(obj3);
- continue;
- }
-
- if (obj3 &&
- strlen(prop_string_cstring_nocopy(obj3)) == 13 &&
- strncmp("last full cap",
- prop_string_cstring_nocopy(obj3),
- 13) == 0) {
+ } else if (strcmp("charge rate", prop_string_cstring_nocopy(obj3)) == 0) {
obj3 = prop_dictionary_get(obj2, "cur-value");
- last_full_cap = prop_number_integer_value(obj3);
- continue;
- }
-
- if (obj3 &&
- strlen(prop_string_cstring_nocopy(obj3)) == 7 &&
- strncmp("voltage",
- prop_string_cstring_nocopy(obj3),
- 7) == 0) {
+ batt_info->present_rate = prop_number_integer_value(obj3);
+ } else if (strcmp("last full cap", prop_string_cstring_nocopy(obj3)) == 0) {
+ obj3 = prop_dictionary_get(obj2, "cur-value");
+ batt_info->full_last = prop_number_integer_value(obj3);
+ } else if (strcmp("voltage", prop_string_cstring_nocopy(obj3)) == 0) {
obj3 = prop_dictionary_get(obj2, "cur-value");
voltage = prop_number_integer_value(obj3);
- continue;
}
}
prop_object_iterator_release(iter2);
return false;
}
- if (last_full_capacity)
- full_design = last_full_cap;
-
- if (!watt_as_unit) {
+ if (!watt_as_unit && voltage != -1) {
batt_info->present_rate = (((float)voltage / 1000.0) * ((float)batt_info->present_rate / 1000.0));
- remaining = (((float)voltage / 1000.0) * ((float)remaining / 1000.0));
- full_design = (((float)voltage / 1000.0) * ((float)full_design / 1000.0));
+ batt_info->remaining = (((float)voltage / 1000.0) * ((float)batt_info->remaining / 1000.0));
+ batt_info->full_design = (((float)voltage / 1000.0) * ((float)batt_info->full_design / 1000.0));
}
- batt_info->percentage_remaining =
- (((float)remaining / (float)full_design) * 100);
-
if (is_full)
batt_info->status = CS_FULL;
-
- /*
- * The envsys(4) ACPI routines do not appear to provide a 'time
- * remaining' figure, so we must deduce it.
- */
- batt_info->seconds_remaining = seconds_remaining_from_rate(batt_info->status, full_design, remaining, batt_info->present_rate);
#endif
return true;
const char *walk;
char *outwalk = buffer;
struct battery_info batt_info = {
+ .full_design = -1,
+ .full_last = -1,
.present_rate = -1,
.seconds_remaining = -1,
.percentage_remaining = -1,
hide_seconds = true;
#endif
- if (!slurp_battery_info(&batt_info, json_gen, buffer, number, path, format_down, last_full_capacity))
+ if (!slurp_battery_info(&batt_info, json_gen, buffer, number, path, format_down))
+ return;
+
+ int full = (last_full_capacity ? batt_info.full_last : batt_info.full_design);
+ if (full < 0 && batt_info.percentage_remaining < 0) {
+ /* We have no physical measurements and no estimates. Nothing
+ * much we can report, then. */
+ OUTPUT_FULL_TEXT(format_down);
return;
+ }
+
+ if (batt_info.percentage_remaining < 0) {
+ batt_info.percentage_remaining = (((float)batt_info.remaining / (float)full) * 100);
+ /* Some batteries report POWER_SUPPLY_CHARGE_NOW=<full_design> when fully
+ * charged, even though that’s plainly wrong. For people who chose to see
+ * the percentage calculated based on the last full capacity, we clamp the
+ * value to 100%, as that makes more sense.
+ * See http://bugs.debian.org/785398 */
+ if (last_full_capacity && batt_info.percentage_remaining > 100) {
+ batt_info.percentage_remaining = 100;
+ }
+ }
+
+ if (batt_info.seconds_remaining < 0 && batt_info.present_rate > 0 && batt_info.status != CS_FULL) {
+ if (batt_info.status == CS_CHARGING)
+ batt_info.seconds_remaining = 3600.0 * (full - batt_info.remaining) / batt_info.present_rate;
+ else if (batt_info.status == CS_DISCHARGING)
+ batt_info.seconds_remaining = 3600.0 * batt_info.remaining / batt_info.present_rate;
+ else
+ batt_info.seconds_remaining = 0;
+ }
if (batt_info.status == CS_DISCHARGING && low_threshold > 0) {
if (batt_info.percentage_remaining >= 0 && strcasecmp(threshold_type, "percentage") == 0 && batt_info.percentage_remaining < low_threshold) {
}
}
-#define EAT_SPACE_FROM_OUTPUT_IF_NO_OUTPUT() \
- do { \
- if (outwalk == prevoutwalk) { \
- if (outwalk > buffer && isspace(outwalk[-1])) \
- outwalk--; \
- else if (isspace(*(walk + 1))) \
- walk++; \
- } \
+#define EAT_SPACE_FROM_OUTPUT_IF_NO_OUTPUT() \
+ do { \
+ if (outwalk == prevoutwalk) { \
+ if (outwalk > buffer && isspace((int)outwalk[-1])) \
+ outwalk--; \
+ else if (isspace((int)*(walk + 1))) \
+ walk++; \
+ } \
} while (0)
for (walk = format; *walk != '\0'; walk++) {