This is the second version of small program which continuously reads battery level from the phone and executes a script when it changes.

I first wrote this program to draw a graph of battery discharge over time, but since the FBUS protocol has only 5 levels (0, 1, 2 ,3 and 4 which this program converts to 0, 25, 50, 75 and 100) it turned out pretty dull; the AT driver supports 101 levels (0 to 100) so it could produce a more interesting graph, if phone firmware supports more than 5 levels, that is. This program outputs -1 when battery level can't be read.

To use this proggy you need to write a small script; for example if you installed the program as $HOME/gnobatmon the script must be named $HOME/gnobatmon.script (don't forget to make it executable, eg. chmod 700 $HOME/gnobatmon.script or similar).
You can be informed when battery is almost discharged (probably you can't use a value lower than 25 because FBUS steps from 1 to 0 (25 to 0) when the battery is fully discharged, which means that either the phone is off, so you can't read from it anymore, or it is already hooked to a charger and you don't need this program, unless you're in the business of recharging batteries :-) )

if [ "$1" -le 25 ]; then
        xmessage "Battery low"

Or you can plot the graph I mentioned above (adjust date format to suit your spreadsheet, see man date).

echo "$(date), $1"

This version cleans up the code a bit and corrects a bug in the way the percentage level was calculated for some drivers. This bug was found by Bastien Nocera, maintainer of Gnome Phone Manager (see the discussion started yesterday on gnokii-users mailing list, eg. at gmane).

/* gnobatmon.c version 0.2

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

  Copyright (C) 2006, 2007 by Daniele Forsi

  An utility program that using libgnokii checks for battery level and executes
  a script when it changes.
  Script name is derived after the name of this program with ".script" appended;
  if you want more scripts make a symbolic link to the main program.
  Sample script gnobatmon.script:
  echo "$(date) battery charge $1 (previous $2)"
  compile with
  gcc -Wall -o gnobatmon gnobatmon.c `pkg-config --libs gnokii`


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>

#include <gnokii.h>

/* prepare for i18n */
#define _(x) x

/* time to wait before reading again */

struct gn_statemachine *state;
int quit = 0;
char *program_name;

void busterminate(void)

void businit(void)
	gn_error	error;
	error = gn_lib_phoneprofile_load(NULL, &state);
	if (GN_ERR_NONE == error) {
		error = gn_lib_phone_open(state);
	if (GN_ERR_NONE != error) {
		fprintf(stderr, "%s\n", gn_error_print(error));

void signal_handler(int signal)
	quit = 1;

void execute_script(float battery_level, float previous_level)
	char buf[128];
	snprintf(buf, sizeof(buf), "%s.script %d %d", program_name, (int)battery_level, (int)previous_level);
	fprintf(stdout,"executing %s\n", buf);

float get_battery_percent(gn_data *data, struct gn_statemachine *state)
	if (*data->battery_unit == GN_BU_Percentage) {
		return *data->battery_level;
	} else {
		return *data->battery_level * 100 / state->;

gn_error battery_monitor(int first, int once)
	gn_data		data;
	gn_error	error;
	float		battery_level = 0, previous_percent = -1, percent_level;
	gn_battery_unit	battery_unit = GN_BU_Arbitrary;
	data.battery_unit = &battery_unit;
	data.battery_level = &battery_level;
	error = gn_sm_functions(GN_OP_GetBatteryLevel, &data, state);
	if (error != GN_ERR_NONE) {
		fprintf(stderr, _("Error getting battery level\n"));
		fprintf(stderr, "%s\n", gn_error_print(error));
		return error;
	fprintf(stdout, _("Ready.\n"));
	percent_level = get_battery_percent(&data, state);
	if (once) {
		execute_script(percent_level, previous_percent);
		return GN_ERR_NONE;
	if (!first) previous_percent = percent_level;
	signal(SIGINT, signal_handler);
	while (!quit) {
		sleep(SLEEP_MINUTES * 60);

		error = gn_sm_functions(GN_OP_GetBatteryLevel, &data, state);
		switch (error) {
		case GN_ERR_NONE:
			percent_level = get_battery_percent(&data, state);
			fprintf(stderr, _("Error getting battery level\n"));
			fprintf(stderr, "%s\n", gn_error_print(error));
			percent_level = -1;
		if (previous_percent != percent_level) {;
			execute_script(percent_level, previous_percent);
			previous_percent = percent_level;
	return GN_ERR_NONE;

int main(int argc, char *argv[])
	int first = 0, once = 0;
	program_name = argv[0];
	if ((argc != 1) && (argc != 2)) {
		fprintf(stderr, _("gnobatmon 0.2 by Daniele Forsi\nUsage: %s [--first|--once]\n" \
			"Purpose: monitor battery level and run a script when it changes\n" \
			"\t--first : run script also on the first value read\n" \
			"\t--once  : run script once then exit\n"), argv[0]);
	if (argc == 2) {
		first = !strcmp(argv[1], "--first");
		once = !strcmp(argv[1], "--once");
	fprintf(stdout, _("Initializing...\n"));
	return battery_monitor(first, once);