4

I've been running 16.04 LTS on an old Lenovo T410. I'm finding that my network connection status varies a lot. I'd like to have an easier way to see what level my wifi is connecting at (300Mb/s, 150Mb/s, etc.) from moment to moment without having to dig down to Network Indicator>Connection Information>"Speed" every time.

Here's the output I'm looking to put in an indicator

connection info

I'm already running indicator-multiload, so adding another section to that would be great but I'm looking for a digital rather than graphical output and I need connection status rather than bandwidth usage. Can indicator-multiload do this?

Sergiy Kolodyazhnyy
  • 105,154
  • 20
  • 279
  • 497
SLT
  • 43
  • 5
  • Well, I'm not familiar with indicator-multiload, but I can write one for this tasks fairly easily. I have briefly researched who to grab the speed of a link, found a few ways. I'll try to write one, probably will take me couple days, maybe will finish by next weekend ( I am a college student, so have tasks outside of Ask Ubuntu ). Only question so far I have is you want it to be displayed on the panes as the clock indicator does it, or display something like a report in a menuentry ( kind of like the calendar indicator does ) ? – Sergiy Kolodyazhnyy Mar 25 '17 at 11:09
  • 1
    Hey, thanks Serg! I totally understand other priorities and very thankful just for a hand. As for what I'm looking for in how the data displays, I was hoping for something in the panes so that I can have it running but visible in the background when I go about my business. The clock being a perfect analogue. Thanks in advance! – SLT Mar 27 '17 at 03:00
  • Hello, again ! I've posted an answer , with two solutions and also took liberty to simplify your post. Let me know how the solutions work, especially the second one - I'd like to see how my code is working on your machine. Also, now that I've been re-reading your question, you might also consider monitoring signal level in dBm rather than bitrate. From my research the bit rate isn't exact but rather approximate or estimated value. Consider monitoring the signal strength of your wireless also via the iw command I mentioned – Sergiy Kolodyazhnyy Apr 04 '17 at 12:20
  • Thanks Sergiy! I'll try those this evening when I have a few moments to mess around and get back to you! – SLT Apr 05 '17 at 16:41
  • Great ! Let me know how you like it, which one works better for you, and if you have any other requests to improve my answer – Sergiy Kolodyazhnyy Apr 05 '17 at 16:58
  • 1
    Thanks Sergiy! I've used the second solution and I'm very happy with it! It does exactly what I was looking for in a very neat little solution. The only problem is that this makes me want to start learning Python... Thanks again! – SLT Apr 05 '17 at 22:19

1 Answers1

4

Custom indicators to display bitrate

This answer provides two solutions to creating custom indicator to display wireless interface bitrate on the Unity panel. Users are encouraged to choose which suits their needs more appropriately. The answer is made as detailed as possible, so please excuse the wall of text.

Reasoning for two solutions

It is my frequent practice to provide solutions to the user that require minimum dependencies and are as self-reliant as possible, while finding simplest solution. Solution #1 relies on use of an indicator-sysmonitor built by fossfreedom - one of our great community members and moderators, and requires use of two items - custom script and the mentioned indicator. The fact that I couldn't find a way to query bit rate from a statistics file somewhere in /proc or /sys filesystems also bothered me. Hence I created a second indicator, which however relies on Network Manager and dbus. Thus advantage and disadvantage of each method are apparent: one requires installing indicator-sysmonitor and custom script but will work if a user avoids Network Manager , while the other is more suitable for a standard Ubuntu system that uses Network Manager.

Ideally,the solution would be as self-reliant as possible, querying specific library and/or file within /proc or /sys filesystems. In my research so far, however, I didn't find such file, and iw command on which one of the solutions relies does so internally. What also complicates the things is that apparently 802.11 wireless standard has different modulation schemes to encode the signal and bitrate calculations are done considering the modulation scheme and frequency of the interface that is being used. While there are ways to obtain TX and RX bytes per second, the data differs from what iw output displays. On the other hand, going all the way to C and system level seems too much for this task. Thus, even though I'm not entirely satisfied with both solutions, they provide appropriate results for what this question asks.

Solution 1: indicator-sysmonitor with custom shell script

enter image description here

Indicator Sysmonitor is an indicator created by fossfreedom, and typically is used to display common statistical data like CPU and memory usage, but the best thing about it is that it allows use of custom scripts. Thus, the idea is to have a script that can query bitrate somehow, and display it via indicator-sysmonitor. For the sake of TL;DR here's abbreviated list of steps:

  1. install indicator-sysmonitor by running in terminal

    sudo add-apt-repository ppa:fossfreedom/indicator-sysmonitor
    sudo apt-get update
    sudo apt-get install indicator-sysmonitor
    
  2. Create the ~/bin/bitrate_script.sh script , make it executable with chmod +x ~/bin/bitrate_script.sh with the following contents

    #!/bin/bash
    iw dev wlan7 link | awk -F '[ ]' '/tx bitrate:/{print $3,$4}'
    
  3. Configure indicator-sysmonitor to display output of your script as custom sensor from Preferences->Advanced->New , using br for sensor and full path to script as command.

Obtaining indicator sysmonitor

As explained on the GitHub page of the project, you can install it from source or via PPA. The PPA method, in my opinion, is the simplest, hence given here:

sudo add-apt-repository ppa:fossfreedom/indicator-sysmonitor
sudo apt-get update
sudo apt-get install indicator-sysmonitor

Once installed, it can be called via command-line as indicator-sysmonitor or from Unity dash.

Setting up bitrate shell script

Such shell script can be done via parsing iw dev <IFACE> link command. The command takes wireless interface as argument, for example wlan1 or wlp6s5 ( note the difference due to predictive interface naming in systemd ) and displays various data, among which we have tx bitrate. For instance:

$ iw dev wlan7 link
Connected to XX:XX:XX:XX:XX:XX (on wlan7)
    SSID: my_wifi
    freq: 2447
    RX: 102456319 bytes (171120 packets)
    TX: 6992103 bytes (50371 packets)
    signal: -46 dBm
    tx bitrate: 150.0 MBit/s MCS 7 40MHz short GI

    bss flags:  short-preamble short-slot-time
    dtim period:    1
    beacon int: 100

As you can see, the output requires text processing to output one line of text (since that's what indicator-sysmonitor requires), and that can be done via awk command. Here's for example the way I've set up my script:

#!/bin/bash
iw dev wlan7 link | awk -F '[ ]' '/tx bitrate:/{print $3,$4}'

If you run the script on the command-line, the output will be simply the bitrate and the units string:

$ iw dev wlan7 link | awk -F '[ ]' '/tx bitrate:/{print $3,$4}'
135.0 MBit/s

Remember to make your script executable with chmod +x command, otherwise it won't run.

Displaying output of script in indicator-sysmonitor

To be precise, the indicator has several settings in the preferences which it calls "sensors". For instance, if you set to display CPU: {cpu} it would display CPU as text and substitute actual percent of CPU usage instead of the curly braces string. The great thing about it is that by clicking New button, you can add a custom script to be displayed as "sensor".

In the screenshot you can see that I've added my custom script as br sensor formatting string , and for Command I've given full path to the script which lives in my ~/WIP folder. There is nothing special about that folder, it just happened to be location for the stuff on which I currently work ( Work-In-Progress, basically ). Users however are usually encouraged to place scripts into ~/bin folder, thus I would recommend you do that.

If you have followed the instructions properly up to this point, you should have indicator-sysmonitor show the bitrate just like in the screenshot.


Solution 2: Custom indicator using Python and Network Manager's dbus properties

indicator-bitrate in action

I wasn't quite satisfied with the above solution, which relies on indicator-sysmonitor and a custom script. I wanted to have a singular indicator which does the job all from within itself. Hence I've written an indicator of my own in Python 3 , which relies on information that Network Manager exposes via dbus interprocess communication system. In short, the big work is done by Network Manager - we just use the information it provides. Advantage of this indicator is that the user doesn't have to define the wireless interface to be used - it's done automatically.

Theory of operation

Indicator effectively repeats same action - the get_bitrates function every 1 second. That function queries dbus properties on several interfaces in sequence. First, it obtains all devices and filters out 802.11 wireless devices. Next, we iterate over each wifi device and query their name and bitrate. Everything is built up into one string of text which is then displayed on the indicator label.

In theory, the indicator should be able to determine bitrate for multiple wireless interfaces. For instance, users may have internal PCI-e wireless chipset, and USB chip. Unfortunately, I did not get to test this due to issues with my laptop , as well as due to time constrains. I may test this on another laptop once I have the chance and will edit the answer accordingly.

Source code

Also available on GitHub in my personal repository.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import dbus
import gi
gi.require_version('AppIndicator3', '0.1')
import signal
from gi.repository import AppIndicator3,Gtk,GLib

class IndicatorBitrate(object):

    def __init__(self):
        self.app = AppIndicator3.Indicator.new(
            'indicator-bitrate', "gtk-network",
            AppIndicator3.IndicatorCategory.HARDWARE
        )
        self.app_menu = Gtk.Menu()
        self.quit_button = Gtk.MenuItem('quit')
        self.quit_button.connect('activate', lambda *args: Gtk.main_quit())
        self.app_menu.append(self.quit_button)
        self.app.set_menu(self.app_menu)
        self.app_menu.show_all()
        self.app.set_status(AppIndicator3.IndicatorStatus.ACTIVE)
        self.update_label()

    def call_dbus_method(self, bus_type, obj, path, interface, method, arg):
        """ utility: executes dbus method on specific interface"""
        if bus_type == "session":
            bus = dbus.SessionBus()
        if bus_type == "system":
            bus = dbus.SystemBus()
        proxy = bus.get_object(obj, path)
        method = proxy.get_dbus_method(method, interface)
        if arg:
            return method(arg)
        else:
            return method()

    def get_dbus_property(self, bus_type, obj, path, iface, prop):
        """ utility:reads properties defined on specific dbus interface"""
        if bus_type == "session":
            bus = dbus.SessionBus()
        if bus_type == "system":
            bus = dbus.SystemBus()
        proxy = bus.get_object(obj, path)
        aux = 'org.freedesktop.DBus.Properties'
        props_iface = dbus.Interface(proxy, aux)
        try:
            props = props_iface.Get(iface, prop)
            return props
        except:
            return None

    def update_label(self):
        self.app.set_label(self.get_bitrates(), '')
        GLib.timeout_add_seconds(1, self.set_app_label)

    def set_app_label(self):
        self.update_label()

    def get_bitrates(self):
        # https://people.freedesktop.org/~lkundrak/nm-docs/nm-dbus-types.html#NMDeviceType
        base = ['system', 'org.freedesktop.NetworkManager']
        call = base + [ '/org/freedesktop/NetworkManager', 'org.freedesktop.NetworkManager',
                        'GetAllDevices',None]
        devs = list(self.call_dbus_method(*call))

        wifi_devs = []
        for dev in devs:
            call = base + [dev,'org.freedesktop.NetworkManager.Device',
                           'DeviceType']

            if int(self.get_dbus_property(*call)) == 2:
                wifi_devs.append(dev)

        stats = []
        for dev in wifi_devs:
            # org.freedesktop.NetworkManager.Device.ActiveConnection
            call = base + [dev, 'org.freedesktop.NetworkManager.Device', 'IpInterface']
            iface = self.get_dbus_property(*call)
            call = base + [dev, 'org.freedesktop.NetworkManager.Device.Wireless',
                           'Bitrate']
            bitrate = int(self.get_dbus_property(*call))/1000
            # bitrate given by this property is in kilobits/second (Kb/s)
            # according to documentation 
            stats.append((iface,bitrate))
        return " ".join([str(i[0])+": "+str(i[1])+'Mb/s' for i in stats]) if stats else "None"

ind = IndicatorBitrate()
signal.signal(signal.SIGINT, signal.SIG_DFL)
Gtk.main()

Setting up the indicator

All the standard rules of making files executable apply here as well.

  1. Save the code as indicator-bitrate file in ~/bin folder
  2. Make the file executable with chmod +x ~/bin/indicator-bitrate
  3. Run from command-line via ~/bin/indicator-bitrate. If you want this to start automatically when you log-in , open Startup Applications from Unity Dash and add full path to script as command.

Conclusion

The two solutions given in this answer should satisfy multiple cases to provide desired results to the users. The first indicator is a little more flexible - it allows displaying other information and statistics in addition to bitrate, while second solution does the job for bitrate but only that; however, it does make use of Network Manager's dbus information, thus we can be sure the information is the same as what has been requested in the question.

The custom indicator may be further developed or become foundation for other indicators to display wireless information in more detail. Bug reports and feature requests are welcome on the linked github page.

Sergiy Kolodyazhnyy
  • 105,154
  • 20
  • 279
  • 497
  • 1
    Thanks, nice solutions. For the second solution, I was getting "ValueError: Namespace AppIndicator3 not available".

    Had to run the following to allow the indicator to work (Ubuntu 17.10):

    sudo apt install libappindicator3-dev
    
    – Gordon Mckeown Jan 19 '18 at 13:33