Decorate i3status with custom scripts

Freedom, that’s what you get when running Linux. You get the potential for almost unlimited customizability.

In this post, we’ll see how to add custom information to the i3status bar using our custom scripts. In my case, I wanted to display the count of visitors on my blog on any given day. I’ve used the googleapiclient lib for that, as you can see bellow.


I’m honestly sorry for forcing you to bulge your eyes over this tiny image on the screen, I’m really bad at image editing, but That! The blue globe, that’s what we’ll be working on - 48 daily views, not bad for a newbie, ham!? The same process can be used for virtually anything that you want to display on i3status.

But how does one config i3status exactly?

i3 + i3status configuration

First of all, make sure to add the i3bar output format to your i3status configuration.

# ~/.i3status.conf
general {
    output_format = "i3bar"

Update your i3 status_command, pointing to the wrapper script.

# ~/.config/i3/config
bar {
    status_command i3status | ~/scripts/

Adapted from here. This piece of code listens to the JSON like output of i3status that is piped through, in the invocation above, and adds our custom data.

#!/usr/bin/env python3

import sys
import json

from analytics import get_report, initialize_analyticsreporting

SCOPES = ['']
VIEW_ID = ''

def print_line(message):
    """ Non-buffered printing to stdout. """
    sys.stdout.write(message + '\n')

def read_line():
    """ Interrupted respecting reader for stdin. """
    # try reading a line, removing any extra whitespace
        line = sys.stdin.readline().strip()
        # i3status sends EOF, or an empty line
        if not line:
        return line
    # exit on ctrl-c
    except KeyboardInterrupt:

if __name__ == '__main__':
    # Skip the first line which contains the version header.

    # The second line contains the start of the infinite array.

    analytics = initialize_analyticsreporting(KEY_FILE_LOCATION, SCOPES)

    while True:
        line, prefix = read_line(), ''

        # ignore comma at start of lines
        if line.startswith(','):
            line, prefix = line[1:], ','

        j = json.loads(line)
        report = get_report(analytics, VIEW_ID)
        # abominable data parsing, but this isn't a data-science course, so who cares
        views = "🌐 " + \
            report['reports'][0]['data']['rows'][0]['metrics'][0]['values'][0] + " views"

        # this is where the magic happens
        j.insert(0, {
            'full_text': '%s' % views,
            'name': 'views',
            'separator_block_width': 25})


Adapted from here.

def initialize_analyticsreporting(key_file_location, scopes):
  """Initializes an Analytics Reporting API V4 service object.

    An authorized Analytics Reporting API V4 service object.
  from googleapiclient.discovery import build
  from oauth2client.service_account import ServiceAccountCredentials

  credentials = ServiceAccountCredentials.from_json_keyfile_name(
      key_file_location, scopes)

  # Build the service object.
  analytics = build('analyticsreporting', 'v4', credentials=credentials)

  return analytics

def get_report(analytics, view_id):
  """Queries the Analytics Reporting API V4.

    analytics: An authorized Analytics Reporting API V4 service object.
    The Analytics Reporting API V4 response.
  return analytics.reports().batchGet(
        'reportRequests': [
          'viewId': view_id,
          'dateRanges': [{'startDate': 'today', 'endDate': 'today'}],
          'metrics': [{'expression': 'ga:users'}],

That’s it, just copy and paste the wrapper script, tweak it, and you are now able to add custom info to your i3status bar.

· i3, i3status, analytics, linux