Created
July 27, 2013 19:36
-
-
Save RavuAlHemio/6096009 to your computer and use it in GitHub Desktop.
Fetch the current battery percentage from UPower and output it.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @file potatopercentage.c | |
* @brief Fetch the current battery percentage from UPower and output it. | |
* | |
* Released into the public domain. | |
* http://creativecommons.org/publicdomain/zero/1.0/ | |
*/ | |
#include <math.h> | |
#include <stdarg.h> | |
#include <stdbool.h> | |
#include <stdint.h> | |
#include <stdio.h> | |
#include <string.h> | |
#include <dbus/dbus.h> | |
/** The default battery device. */ | |
static const char * const POTATO_DEFAULT_BATTERY_DEVICE = "/org/freedesktop/UPower/devices/battery_BAT1"; | |
/** The destination of the method call. */ | |
static const char * const POTATO_DESTINATION = "org.freedesktop.UPower"; | |
/** The interface where the method call is specified. */ | |
static const char * const POTATO_CALL_INTERFACE = "org.freedesktop.DBus.Properties"; | |
/** The name of the method to call. */ | |
static const char * const POTATO_CALL_METHOD = "GetAll"; | |
/** The type of the target (argument to GetAll). */ | |
static const char * const POTATO_TARGET_TYPE = "org.freedesktop.UPower.Device"; | |
/** The name of the "percentage" property. */ | |
static const char * const POTATO_PROPERTY_PERCENTAGE = "Percentage"; | |
/** The name of the "state" property. */ | |
static const char * const POTATO_PROPERTY_STATE = "State"; | |
/* Status characters. */ | |
static const char * const POTATO_STATE_CHARGING = "+"; | |
static const char * const POTATO_STATE_LOW = "!"; | |
static const char * const POTATO_STATE_CRITICAL = "!!!"; | |
/** Maximum number of milliseconds to wait for a reply. */ | |
static const int POTATO_TIMEOUT = 5000; | |
/** The name of the program. */ | |
static const char *progname = "potatopercentage"; | |
/** Returns whether the first string is equal to any of the other strings. */ | |
__attribute__((nonnull(1), const, sentinel)) | |
static bool streqs(const char *exemplar, ...) | |
{ | |
va_list args; | |
bool ret = false; | |
va_start(args, exemplar); | |
for (;;) | |
{ | |
const char *candidate = va_arg(args, const char *); | |
if (candidate == NULL) | |
{ | |
ret = true; | |
break; | |
} | |
if (strcmp(exemplar, candidate) == 0) | |
{ | |
ret = false; | |
break; | |
} | |
} | |
va_end(args); | |
return ret; | |
} | |
__attribute__((const)) | |
static double floorto(double n, unsigned int digits) | |
{ | |
double kapow = 1.0; | |
while (digits > 0) | |
{ | |
kapow *= 10.0; | |
--digits; | |
} | |
return floor(n * kapow) / kapow; | |
} | |
int main(int argc, char **argv) | |
{ | |
const char *batDev; | |
DBusConnection *conn; | |
DBusError err; | |
DBusMessage *toSend, *reply; | |
DBusMessageIter itMsg, itArr, itDict, itVar; | |
double percentage = -1.0; | |
int32_t state = -1; | |
const char *state_string = ""; | |
// parse args | |
if (argc > 0) | |
{ | |
progname = argv[0]; | |
} | |
if (argc == 1) | |
{ | |
batDev = POTATO_DEFAULT_BATTERY_DEVICE; | |
} | |
else if (argc == 2 && streqs(argv[1], "-h", "--help", "-?", NULL)) | |
{ | |
batDev = argv[1]; | |
} | |
else | |
{ | |
fprintf(stderr, | |
"Usage: %s [BATTERYDEVICE]\n" | |
" BATTERYDEVICE UPower battery device path, e.g.\n" | |
" '%s'\n", | |
progname, POTATO_DEFAULT_BATTERY_DEVICE | |
); | |
return 1; | |
} | |
// initialize error | |
dbus_error_init(&err); | |
// validate the path | |
if (!dbus_validate_path(batDev, &err)) | |
{ | |
fprintf(stderr, "%s: %s\n", err.name, err.message); | |
return 3; | |
} | |
// connect to system bus | |
conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err); | |
if (conn == NULL) | |
{ | |
fprintf(stderr, "%s: %s\n", err.name, err.message); | |
return 1; | |
} | |
// create a new method call | |
toSend = dbus_message_new_method_call(POTATO_DESTINATION, batDev, POTATO_CALL_INTERFACE, POTATO_CALL_METHOD); | |
// append the arguments | |
dbus_message_append_args(toSend, | |
DBUS_TYPE_STRING, &POTATO_TARGET_TYPE, | |
DBUS_TYPE_INVALID | |
); | |
// send the message | |
reply = dbus_connection_send_with_reply_and_block(conn, toSend, POTATO_TIMEOUT, &err); | |
if (reply == NULL) | |
{ | |
fprintf(stderr, "%s: %s\n", err.name, err.message); | |
dbus_message_unref(toSend); | |
dbus_connection_unref(conn); | |
return 2; | |
} | |
// destroy the sent message | |
dbus_message_unref(toSend); | |
// pick out the array | |
dbus_message_iter_init(reply, &itMsg); | |
dbus_message_iter_recurse(&itMsg, &itArr); | |
while (dbus_message_iter_has_next(&itArr)) | |
{ | |
const char *key; | |
dbus_message_iter_recurse(&itArr, &itDict); | |
dbus_message_iter_get_basic(&itDict, &key); | |
dbus_message_iter_next(&itDict); | |
dbus_message_iter_recurse(&itDict, &itVar); | |
if (strcmp(key, POTATO_PROPERTY_STATE) == 0) | |
{ | |
dbus_message_iter_get_basic(&itVar, &state); | |
} | |
else if (strcmp(key, POTATO_PROPERTY_PERCENTAGE) == 0) | |
{ | |
dbus_message_iter_get_basic(&itVar, &percentage); | |
} | |
dbus_message_iter_next(&itArr); | |
} | |
// destroy the reply | |
dbus_message_unref(reply); | |
// disconnect | |
dbus_connection_unref(conn); | |
if (state == -1 || percentage == -1.0) | |
{ | |
fprintf(stderr, "warning: some properties not set correctly\n"); | |
} | |
if (state == 1) | |
{ | |
// charging | |
state_string = POTATO_STATE_CHARGING; | |
} | |
else if (percentage < 10.0) | |
{ | |
state_string = POTATO_STATE_CRITICAL; | |
} | |
else if (percentage < 20.0) | |
{ | |
state_string = POTATO_STATE_LOW; | |
} | |
// output the percentage and the state string | |
printf("%.0f%%%s\n", floorto(percentage, 0), state_string); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment