Please note, this is a STATIC archive of website developer.mozilla.org from 03 Nov 2016, cach3.com does not collect or store any user information, there is no "phishing" involved.

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 screenshot 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 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:

        <!-- UTILS -->
        <script src="scripts/utils/string-utils.js"></script>
        <script src="scripts/utils/energy-manager.js"></script>
        <script src="scripts/utils/proximity-manager.js"></script>

        <!-- CONTROLLERS -->
        <script src="scripts/controllers/battery-status-bar.js"></script>
        <script src="scripts/controllers/action-bar.js"></script>
        <script src="scripts/controllers/chat.js"></script>

        <!-- APP -->
        <script src="scripts/app.js"></script>
        <script src="scripts/install.js"></script>
        <script src="scripts/index.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.

App init

The App.init() function is created when window is loaded. It creates, initializes and starts the objects needed by the Low Energy Messenger. First of all, controllers and utility objects are initialized, so that they can be used by the app.

BatteryStatusBar.init();
ActionBar.init();
Chat.init();

Then the app checks whether the Battery Status API is supported:

   if (EnergyManager.isBatteryStatusAPISupported()) {

If so, it initialize the EnergyManager

        EnergyManager.init(function() {

proviging a callback function called when the battery is retrieved. This callback function adds an handler to manage battery status change events

            EnergyManager.handleChangeEvents(BatteryStatusBar.update.bind(BatteryStatusBar));

updates the battery status bar for the first time

            BatteryStatusBar.update();

 and notifies the user that the Battery Status API is supported

           Chat.receiveMessage('Congratulations! ...');

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

       Chat.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 ActionBar

The ActionBar 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:

/* ActionBar */
init: function() {
    /* 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() method simply sends the text contained in the input field to the chat:

           Chat.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 (EnergyManager.getBatteryPercentage() > 30 // not low
                    || EnergyManager.isBatteryCharging() // charging
                    || EnergyManager.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 Chat

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

/* Chat */
init: function() {
    /* 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:

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

The BatteryStatusBar 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 BatteryStatusBar.update() method performs a number of interesting tasks. First of all it updates the battery level information:

/* Level */
var percentage = EnergyManager.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 (EnergyManager.isBatteryFullyCharged()) { // battery fully charged
            chargingStatusAndTime = 'fully charged';
        } else if (EnergyManager.isBatteryCharging()) { // battery charging
            var batteryChargingTime = EnergyManager.getBatteryChargingTime();
            chargingStatusAndTime = 'Charging: ';

            if (batteryChargingTime) {
                chargingStatusAndTime += batteryChargingTime;
                chargingStatusAndTime += ' until full';
            } else { // charging time unknown
                chargingStatusAndTime += 'calculating time until full ...';
            }
        } else { // battery discharging
            var batteryDischarginTime = EnergyManager.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 navigator.getBattery method or by the deprecated navigator.battery property.

/* EnergyManager */
init: function(callback) {
    var _self = this;
    
    /* Initialize the battery object */
    if (navigator.getBattery) {
        navigator.getBattery().then(function(battery) {
            _self.battery = battery;
            callback();
        });
    } else if (navigator.battery || navigator.mozBattery) { // deprecated battery object
        _self.battery = navigator.battery || navigator.mozBattery;
        callback();
    }
}

The navigator.getBattery method returns a battery promise, which is resolved in a BatteryManager object providing events you can handle to monitor the battery status. The deprecated navigator.battery attribute returns the BatteryManager object directly; the implementation above checks for vendor prefixes as well. Once the battery object has retrieved, it is saved as attribute of EnergyManager, so that can be referred by the this keyword.

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 (navigator.getBattery || navigator.battery || navigator.mozBattery) {
        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 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 the battery object:

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.

Document Tags and Contributors

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