Retrieving Battery status information

Running out of battery is not fun: maybe one of the worst things that can happen to a mobile phone user! Big screens, fast processors and wireless Internet connection are important, but they are expensive when it comes to energy consumption. That's why mobile apps should be built with battery consumption in mind, performance and memory usage optimization being paramount. This article looks at one way to manage energy consumption: The Battery Status API. Using this we can manage a web application's battery usage, perhaps turning off power hungry features or providing a simplified experience when the battery starts to run down.

Situations in which battery information is useful

In the context of Open Web Apps, knowing the battery status can be useful in a number of situations:

  • Utility apps that collect statistics on battery usage or simply inform the user if the device is charged enough to play a game, watch a movie, or browse the Web
  • High-quality apps that optimize battery consuption: for example an email client may check the server for new email less frequently if the device is low on battery
  • A word processor could save changes automatically before the battery runs out in order to prevent data loss

The Battery Status API

Open Web Apps can retrieve battery status information thanks to the Battery Status API, a W3C Recommendation supported by Firefox since version 16, and by Firefox OS as a Regular API. The Battery Status API is one of the Web APIs, a suite of device compatibility and access APIs that allow Web apps and content to access device hardware, as well as access to data stored on the device. For more information have a look at the Web APIs page on MDN.

Below we will look at using the Battery Status API in an Instant Messagging App for Firefox OS.

Demo: Low Energy Messenger

Low Energy Messenger is an instant messaging Web App that pays very high attention to battery status. Here is a sheenshot taken from the Firefox OS Simulator:

screenshot

Low Energy Messenger has the following features:

  • A battery status bar, containing battery status information
  • A chat section, containing all the messages received or sent
  • An action bar, containing a text field, a button to send a message, a button to take a photo and a button to install the app on Firefox OS

The app doesn’t allow users to take photos when the battery level is low or the device is not charging. It has been made using HTML + CSS + Javascript, with no libs and using static data, so no web services on Internet, but real integration with the Battery Status API and a realistic look & feel. It is published here (short link) and you can find the code on Github.

Let’s dive into the code!

HTML & CSS

The Battery Status API is a Javascript API, so showing the HTML and the CSS might be considered out of scope here, but I think it is crucial to understand at least the elements composing the page on which JavaScript operates.

The main elements are:

  • The battery status bar
  • The chat
  • The action bar

The battery status bar

The battery status bar contains:

  • A visual representation of the battery level
  • The battery level expressed as a percentage (0-100%)
  • Text indicating if the battery is charging or discharging, and the time remaining until the battery is completely charged or discharged

Here is the HTML code:

<div id="battery-status-bar">
    <div class="battery">
        <div class="power">
            <div class="level"></div>
        </div>
    </div>
    <div class="percentage"></div>
    div class="time"></div>
</div>

Each id is used by Javascript to manipulate the DOM at run-time. The battery status bar is not visible initially: it will be shown by JavaScript if the browser supports the Battery Status API.

#battery-status-bar {
    display: none;
    margin-top: 0.5em;
}

The visual representation of the battery level is made entirely in CSS, and the level class is changed by JavaScript at run-time:

.battery .level {
    position: absolute;
    right: 80%; /* this property will be changed via javascript */
    background: blue;
    ...
}

You can find the CSS code in the shapes.css file.

The chat section

The chat section is composed of a set of article elements containing a list of messages.

Here is the HTML code:

<section id="chat">
    <article>
        <div class="avatar"><img alt="franciov" src="img/franciov.png" /></div>
        <div class="message">
            <div>
                <h2>Franciov</h2>
                    <p>Hello, ... </p>
            </div>
        </div>
   </article>
</section>

The first article is visible when the page loads in order to say welcome for demo purposes. The articles display messages on the left columns by default. When the article has class you, the articles display messages on the right column instead. This is handled in CSS using the float property.

section#chat article > div {
    float: left;
}

section#chat article.you > div {
    float: right;
}

Note: The you avatar is made entirely in CSS: you can find the code in the shapes.css file.

A div with id="bottom" is placed just after the chat section: this element is used by JavaScript to scroll down the page when a message is added to the chat.

<div id="bottom"></div>

The action bar

The action bar contains:

  • A text field
  • A button to send a message
  • A button to take a photo
  • A button to install the app on Firefox OS

 

Here is the HTML code:

<nav>
    <div>
        <div class="send-box">
            <input type="text" id="text-message" value="Hello! :)" />
            <button id="send-message">Send</button>
        </div>
        <div id="take-photo">
            <div class="camera">
                <div class="lens"></div>
            </div>
        </div>
    </div>
    <div>
        <button id="install">Install</button>
    </div>
</nav>

Each id is used by JavaScript to manipulate the DOM at run-time. The action bar has fixed position, so it can be always visible on the bottom of the page.

nav {
    position: fixed;
}

Note: The take-photo button is made entirely in CSS: you can find the code in the shapes.css file.

The install button is not visible initially: it will be shown by JavaScript if the browser is Firefox and the app is not already installed on Firefox OS.

nav button#install {
    display: none;
}

JavaScript

Low Energy Messenger implements the following JavaScript files:

        <script src="scripts/utils/StringUtils.js"></script>
        <script src="scripts/utils/EnergyManager.js"></script>
        <script src="scripts/controllers/BatteryStatusBarController.js"></script>
        <script src="scripts/controllers/ChatViewController.js"></script>
        <script src="scripts/controllers/ActionBarController.js"></script>
        <script src="scripts/install.js"></script>
        <script src="scripts/start.js"></script>

This demo is built using Object Oriented Programming. For an introduction to Object Oriented Javascript have a look at this MDN page.

Each JavaScript file represents a component that executes specific tasks in the application. The EnergyManager is the component that retrieves battery status information, so the most interesting code for the purposes of this tutorial is inside EnergyManager.js. However, in order to understand how the app works, we’ll briefly have a look at all the JavaScript files. Feel free to skip to The Energy Manager section if you are mainly interested in the Battery Status API.

Start

This start.js file creates, initializes and starts the objects needed by the Low Energy Messenger. First of all, controllers and utility objects are added to window, so that they can be used by the app.

window.mEnergyManager = new EnergyManager();
window.mBatteryStatusBarController = new BatteryStatusBarController();
window.mActionBarController = new ActionBarController();
window.mChatViewController = new ChatViewController();

Then, when the window is loaded, the app checks whether the Battery Status API is supported:

   if (window.mEnergyManager.isBatteryStatusAPISupported()) {

If so, it adds an handler to manage battery status change events

        window.mEnergyManager.handleChangeEvents(window.mBatteryStatusBarController.update);

updates the battery status bar for the first time

        window.mBatteryStatusBarController.update();

 and notifies the user that the Battery Status API is supported

       window.mChatViewController.receiveMessage('Congratulations! ...');

If it is not supported, it instead notifies the user that the Battery Status API is not supported

       window.mChatViewController.receiveMessage('I\'m sorry: ...');

Install

The install.js file contains the code to install the Low Energy Messenger on Firefox OS. There’s no need to cover this topic here: if you don’t know how to build a Web App for Firefox OS, please have a look at our Quickstart app tutorial.

The ActionBarController

The ActionBarController manages the actions that can be performed on the chat: 

  • Sending a message
  • Sending a photo

Visually, this is like so:

 

This is the constructor:

function ActionBarController() {
    /* Initialize DOM Objects */
    this.takePhotoButton = document.querySelector('#take-photo');
    this.sendMessageButton = document.querySelector('#send-message');
    this.textMessageField = document.querySelector('#text-message');

    /* Call takePhoto() when the takePhoto button is clicked */
    this.takePhotoButton.onclick = this.takePhoto;

    /* Call send() When the send button is clicked */
    this.sendMessageButton.onclick = this.send;
}

The send() and takePhoto() methods are specified in the prototype.

The send() method simply sends the text contained in the input field to the chat:

           window.mChatViewController.sendMessage(this.textMessageField.value);

The takePhoto() method on the other hand contains much more interesting code: it has to check the battery status before performing the action.

            if (window.mEnergyManager.getBatteryPercentage() > 30 // not low
                    || window.mEnergyManager.isBatteryCharging() // charging
                    || window.mEnergyManager.isBatteryFullyCharged() // fully charged
                    ) {
                // send the photo
                  …
}

So the user is allowed to take a photo only if the battery level is greater than 30% or the battery is charging or fully charged. The EnergyManager object, with all the methods called above, will be described in full detail further down this page, along with the Battery Status API.

The ChatViewController

The ChatViewController manages the DOM object with id="chat":

function ChatViewController() {
    /* Initialize DOM Objects */
    var chat = document.querySelector('#chat');
}

It also provides the methods sendMessage() and receiveMessage(), which simply add HTML article elements to the chat section in order to show messages passed as parameters:

ChatViewController.prototype = {
    sendMessage: function(message) {
      … 
    },

    receiveMessage: function(message) {
      …
    },
    scrollDown: function() {
        window.location = "#bottom";
    }
};

The scrollDown() method simply scrolls the chat to the bottom in order to make sure that the last sent or received message is always visible by the user.

The BatteryStatusBar Controller

The BatteryStatusBarController manages the battery indicators in the battery status bar, which are:

    this.batteryStatusBar = document.querySelector('#battery-status-bar');
    this.batteryLevelDomObj = document.querySelector('#battery-status-bar .level');
    this.batteryPercentageDomObj = document.querySelector('#battery-status-bar .percentage');
    this.batteryTimeDomObj = document.querySelector('#battery-status-bar .time');

The prototype is composed by the update() method:

BatteryStatusBarController.prototype = {
    update: function(event) {
      ...
    }
};

The update() method performs a number of interesting tasks. First of all it updates the battery level information:

        /* Level */
        var percentage = window.mEnergyManager.getBatteryPercentage();
        this.batteryLevelDomObj.style.right = (100 - percentage) + '%';
        this.batteryPercentageDomObj.innerHTML = percentage + '%';

        /* Color */
        if (percentage < 20) {
            this.batteryLevelDomObj.setAttribute('class', 'low level');
        } else if (percentage < 60) {
            this.batteryLevelDomObj.setAttribute('class', 'medium level');
        } else {
            this.batteryLevelDomObj.setAttribute('class', 'high level');
        }

So the visual representation of the battery changes depending on the charge level. For example:

Then the battery charging status and time are updated.

        /* Charging status and time */
        var chargingStatusAndTime = '';

        if (window.mEnergyManager.isBatteryFullyCharged()) { // battery fully charged
            chargingStatusAndTime = 'fully charged';
        } else if (window.mEnergyManager.isBatteryCharging()) { // battery charging
            var batteryChargingTime = window.mEnergyManager.getBatteryChargingTime();
            chargingStatusAndTime = 'Charging: ';

            if (batteryChargingTime) {
                chargingStatusAndTime += batteryChargingTime;
                chargingStatusAndTime += ' until full';
            } else { // charging time unknown
                chargingStatusAndTime += 'calculating time until full ...';
            }
        } else { // battery discharging
            var batteryDischarginTime = window.mEnergyManager.getBatteryDischargingTime();
            chargingStatusAndTime = 'Discharging: ';

            if (batteryDischarginTime) {
                chargingStatusAndTime += batteryDischarginTime;
                chargingStatusAndTime += ' remaining';
            } else { // discharging time unknown
                chargingStatusAndTime += 'calculating time remaining ...';
            }
        }

        this.batteryTimeDomObj.innerHTML = chargingStatusAndTime;

Finally update() makes sure the battery status bar is visible.

        /* Show the battery status bar */
        this.batteryStatusBar.style.display = 'block';

The EnergyManager

Now that we know how the Web App works, it's time to reveal EnergyManager.js and find out how the Battery Status API is used.

The EnergyManager constructor initializes the battery object, of type EnergyManager, provided by the Battery Status API as attribute of the navigator object.

function EnergyManager() {
    /* Initialize the battery object */
    this.battery = navigator.battery || navigator.mozBattery || navigator.webkitBattery;
}

The assignment above means that this.battery equals navigator.battery if it exists (as per the W3C specification), otherwise it checks to see if one of the vendor prefixed vertsions exists, and will one one of those instead if so. The EnergyManager() provides a number of methods based on the battery object initialized in the constructor. Let’s have a look at each of them.

First, isBatteryStatusAPISupported() checks whether the Battery Status API is supported by the browser and returns a boolean accordingly. The code is pretty easy to understand.

isBatteryStatusAPISupported: function() {
        if (this.battery) {
            return true;
        }
        return false;
    },
}

log() writes logs into the console. This is useful to understand how the Battery Status API actually works.

    log: function(event) {
        if (event) {
            console.warn(event);
        }

        console.log('battery.level: ' + this.battery.level);
        console.log('battery.charging: ' + this.battery.charging);
        console.log('battery.chargingTime: ' + this.battery.chargingTime);
        console.log('battery.dischargingTime: ' + this.battery.dischargingTime);
    }

Here is how the logs appear on the console:

getBatteryPercentage() gets the battery level from the navigator.battery object — expressed as a number between 0 and 1, calculates the percentage, then returns a number between 0 and 100.

    getBatteryPercentage: function() {
        var percentage = Math.round(this.battery.level * 100);
        return percentage;
    },

isBatteryFullyCharged() checks whether the battery is fully charged and returns a boolean accordingly:

    isBatteryFullyCharged: function() {
        if (this.battery.level === 1) {
            return true;
        }
        return false;
    },

isBatteryCharging() checks whether the battery is charging and returns a boolean accordingly:

    isBatteryCharging: function() {
        // the battery cannot be charging because is completely charged
        if (this.battery.level === 1) {
            return false;
        }

        return this.battery.charging;
    },

getBatteryChargingTime() gets the battery charging time in seconds and converts it to a human readable time, using the StringUtils provided by the app:

    getBatteryChargingTime: function() {
        if (this.battery.chargingTime === Infinity) {
            return undefined;
        }        

        var time = StringUtils.getHumanReadableTime(this.battery.chargingTime);
        return time;
    },

Similarly, getBatteryDischargingTime() gets the battery discharging time in seconds and converts it to a human readable time, using the StringUtils provided by the app:

    getBatteryDischargingTime: function() {
        if (this.battery.dischargingTime === Infinity) {
            return undefined;
        }

        var time = StringUtils.getHumanReadableTime(this.battery.dischargingTime);
        return time;
    },

The handleChangeEvents() method is called when the Web App starts. It registers the handler passed as a parameter to every *-change event fired by navigator.battery:

    handleChangeEvents: function(handler) {
        /* Update the battery status bar on battery level change */
        this.battery.onlevelchange = function(e) {
            handler(e);
        };

        /* Update the battery status bar on battery charging change */
        this.battery.onchargingchange = function(e) {
            handler(e);
        };

        /* Update the battery status bar on battery charging time change */
        this.battery.onchargingtimechange = function(e) {
            handler(e);
        };

        /* Update the battery status bar on battery discharging time change */
        this.battery.ondischargingtimechange = function(e) {
            handler(e);
        };
    }

As you may have noticed, the EnergyManager uses all the attributes and events specified by the Battery Status API to retrieve battery status information.

Attachments

File Size Date Attached by
chat
34818 bytes 2013-12-21 03:04:02 franciov
install
15788 bytes 2013-12-21 03:04:18 franciov
log
90380 bytes 2013-12-21 03:04:35 franciov
battery-high
13465 bytes 2013-12-21 03:04:49 franciov
battery-low
14453 bytes 2013-12-21 03:04:59 franciov
battery-medium
14244 bytes 2013-12-21 03:05:11 franciov
action-bar
10755 bytes 2013-12-21 03:09:13 franciov
battery-status-bar
12643 bytes 2013-12-21 03:09:28 franciov
firefoxos-simulator
100916 bytes 2014-01-25 08:38:51 franciov

Document Tags and Contributors

Contributors to this page: franciov, chrisdavidmills
Last updated by: chrisdavidmills,