]> git.sur5r.net Git - i3/i3status/commitdiff
Implement battery functionality for NetBSD users
authorAlexander Vasarab <alexander@vasaconsulting.com>
Mon, 28 Apr 2014 19:16:48 +0000 (12:16 -0700)
committerMichael Stapelberg <michael@stapelberg.de>
Wed, 30 Apr 2014 07:23:17 +0000 (09:23 +0200)
This patch takes a similar approach as the NetBSD CPU temperature
code in that it uses proplib(3) to walk dictionaries supplied by
envsys(4).

In addition to providing the basic functionality, it:

* Provides all existing format specifiers (%emptytime %consumption
  %status %percentage %remaining)
* Respects all existing config options (hide_seconds, low_threshold,
  integer_battery_capacity, last_full_capacity)
* Projects "time until full" when battery status is CS_CHARGING

src/print_battery_info.c

index 72d291ee8d4c0968b5b82abbf2482ccad9b2d918..72777656d4a5b3f056f230fbb1d6dd901438be65 100644 (file)
 #include <machine/apmvar.h>
 #endif
 
+#if defined(__NetBSD__)
+#include <fcntl.h>
+#include <prop/proplib.h>
+#include <sys/envsys.h>
+#endif
+
 #define BATT_STATUS_NAME(status) \
     (status == CS_CHARGING ? "CHR" : \
         (status == CS_DISCHARGING ? "BAT" : "FULL"))
@@ -316,6 +322,266 @@ void print_battery_info(yajl_gen json_gen, char *buffer, int number, const char
 
        if (colorful_output)
                END_COLOR;
+#elif defined(__NetBSD__)
+        /*
+         * Using envsys(4) via sysmon(4).
+         */
+        int fd, rval, last_full_cap;
+        bool is_found = false;
+        char *sensor_desc;
+        bool is_full = false;
+
+        prop_dictionary_t dict;
+        prop_array_t array;
+        prop_object_iterator_t iter;
+        prop_object_iterator_t iter2;
+        prop_object_t obj, obj2, obj3, obj4, obj5;
+
+        asprintf(&sensor_desc, "acpibat%d", number);
+
+        fd = open("/dev/sysmon", O_RDONLY);
+        if (fd < 0) {
+                OUTPUT_FULL_TEXT("can't open /dev/sysmon");
+                return;
+        }
+
+        rval = prop_dictionary_recv_ioctl(fd, ENVSYS_GETDICTIONARY, &dict);
+        if (rval == -1) {
+                close(fd);
+                return;
+        }
+
+        if (prop_dictionary_count(dict) == 0) {
+                prop_object_release(dict);
+                close(fd);
+                return;
+        }
+
+        iter = prop_dictionary_iterator(dict);
+        if (iter == NULL) {
+                prop_object_release(dict);
+                close(fd);
+        }
+
+        /* 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))
+                        continue;
+
+                is_found = true;
+
+                array = prop_dictionary_get_keysym(dict, obj);
+                if (prop_object_type(array) != PROP_TYPE_ARRAY) {
+                        prop_object_iterator_release(iter);
+                        prop_object_release(dict);
+                        close(fd);
+                        return;
+                }
+
+                iter2 = prop_array_iterator(array);
+                if (!iter2) {
+                        prop_object_iterator_release(iter);
+                        prop_object_release(dict);
+                        close(fd);
+                        return;
+                }
+
+                /* iterate over array of dicts specific to target battery */
+                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)
+                        {
+                                obj3 = prop_dictionary_get(obj2, "cur-value");
+
+                                if (prop_number_integer_value(obj3))
+                                        status = CS_CHARGING;
+                                else
+                                        status = CS_DISCHARGING;
+
+                                continue;
+                        }
+
+                        if (obj3 &&
+                            strlen(prop_string_cstring_nocopy(obj3)) == 6 &&
+                            strncmp("charge",
+                                    prop_string_cstring_nocopy(obj3),
+                                    6) == 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);
+
+                                if (remaining == full_design)
+                                        is_full = true;
+
+                                if (strncmp("Ampere hour",
+                                            prop_string_cstring_nocopy(obj5),
+                                            11) == 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)
+                        {
+                                obj3 = prop_dictionary_get(obj2, "cur-value");
+                                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)
+                        {
+                                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)
+                        {
+                                obj3 = prop_dictionary_get(obj2, "cur-value");
+                                voltage = prop_number_integer_value(obj3);
+                                continue;
+                        }
+                }
+                prop_object_iterator_release(iter2);
+        }
+
+        prop_object_iterator_release(iter);
+        prop_object_release(dict);
+        close(fd);
+
+        if (! is_found) {
+                OUTPUT_FULL_TEXT(format_down);
+                return;
+        }
+
+        if (last_full_capacity)
+                full_design = last_full_cap;
+
+        if (! watt_as_unit) {
+                present_rate = (((float)voltage / 1000.0) * ((float)present_rate / 1000.0));
+                remaining = (((float)voltage / 1000.0) * ((float)remaining / 1000.0));
+                full_design = (((float)voltage / 1000.0) * ((float)full_design / 1000.0));
+        }
+
+        float percentage_remaining =
+                (((float)remaining / (float)full_design) * 100);
+
+        if (integer_battery_capacity)
+                (void)snprintf(percentagebuf,
+                           sizeof(percentagebuf),
+                           "%d%%",
+                           (int) percentage_remaining);
+        else
+                (void)snprintf(percentagebuf,
+                           sizeof(percentagebuf),
+                           "%.02f%%",
+                           percentage_remaining);
+
+        /*
+         * Handle percentage low_threshold here, and time low_threshold when
+         * we have it.
+         */
+        if (status == CS_DISCHARGING && low_threshold > 0) {
+                if (strcasecmp(threshold_type, "percentage") == 0
+                    && (((float)remaining / (float)full_design) * 100) < low_threshold) {
+                        START_COLOR("color_bad");
+                        colorful_output = true;
+                }
+        }
+
+        if (is_full)
+                (void)snprintf(statusbuf, sizeof(statusbuf), "%s", BATT_STATUS_NAME(CS_FULL));
+        else
+                (void)snprintf(statusbuf, sizeof(statusbuf), "%s", BATT_STATUS_NAME(status));
+
+        /*
+         * The envsys(4) ACPI routines do not appear to provide a 'time
+         * remaining' figure, so we must deduce it.
+         */
+        float remaining_time;
+        int seconds, hours, minutes, seconds_remaining;
+
+        if (status == CS_CHARGING)
+                remaining_time = ((float)full_design - (float)remaining)
+                        / (float)present_rate;
+        else if (status == CS_DISCHARGING)
+                remaining_time = ((float)remaining / (float)present_rate);
+        else remaining_time = 0;
+
+        seconds_remaining = (int)(remaining_time * 3600.0);
+
+        hours = seconds_remaining / 3600;
+        seconds = seconds_remaining - (hours * 3600);
+        minutes = seconds / 60;
+        seconds -= (minutes * 60);
+
+        if (status != CS_CHARGING) {
+                if (hide_seconds)
+                        (void)snprintf(remainingbuf, sizeof(remainingbuf), "%02d:%02d",
+                                max(hours, 0), max(minutes, 0));
+                else
+                        (void)snprintf(remainingbuf, sizeof(remainingbuf), "%02d:%02d:%02d",
+                                max(hours, 0), max(minutes, 0), max(seconds, 0));
+
+                if (low_threshold > 0) {
+                        if (strcasecmp(threshold_type, "time") == 0
+                            && ((float) seconds_remaining / 60.0) < (u_int) low_threshold) {
+                                START_COLOR("color_bad");
+                                colorful_output = true;
+                        }
+                }
+        } else {
+                if (hide_seconds)
+                        (void)snprintf(remainingbuf, sizeof(remainingbuf), "(%02d:%02d until full)",
+                                max(hours, 0), max(minutes, 0));
+                else
+                        (void)snprintf(remainingbuf, sizeof(remainingbuf), "(%02d:%02d:%02d until full)",
+                                max(hours, 0), max(minutes, 0), max(seconds, 0));
+        }
+
+        empty_time = time(NULL);
+        empty_time += seconds_remaining;
+        empty_tm = localtime(&empty_time);
+
+        /* No need to show empty time if battery is charging */
+        if (status != CS_CHARGING) {
+                if (hide_seconds)
+                        (void)snprintf(emptytimebuf, sizeof(emptytimebuf), "%02d:%02d",
+                                max(empty_tm->tm_hour, 0), max(empty_tm->tm_min, 0));
+                else
+                        (void)snprintf(emptytimebuf, sizeof(emptytimebuf), "%02d:%02d:%02d",
+                                max(empty_tm->tm_hour, 0), max(empty_tm->tm_min, 0), max(empty_tm->tm_sec, 0));
+        }
+
+        (void)snprintf(consumptionbuf, sizeof(consumptionbuf), "%1.2fW",
+                ((float)present_rate / 1000.0 / 1000.0));
 #endif
 
 #define EAT_SPACE_FROM_OUTPUT_IF_EMPTY(_buf) \