自定义指标显示比特率

自定义指标显示比特率

我一直在一台旧的联想 T410 上运行 16.04 LTS。我发现我的网络连接状态变化很大。我希望有一种更简单的方法来随时查看我的 wifi 连接速度(300Mb/s、150Mb/s 等),而不必每次都深入到网络指示器 > 连接信息 >“速度”。

这是我希望放入指示器的输出

连接信息

我已经在运行了indicator-multiload,因此添加另一个部分会很棒,但我正在寻找数字输出而不是图形输出,并且我需要连接状态而不是带宽使用情况。indicator-multiload 可以做到这一点吗?

答案1

自定义指标显示比特率

本回答提供了两种在 Unity 面板上创建自定义指示器以显示无线接口比特率的解决方案。鼓励用户选择更适合自己需求的方案。答案尽可能详细,所以请原谅文字太长。

两种解决方案的推理

我经常向用户提供需要最少依赖且尽可能自力更生的解决方案,同时寻找最简单的解决方案。解决方案 1 依赖于使用indicator-sysmonitor由 fossfreedom(我们伟大的社区成员和版主之一)构建的,并且需要使用两个项目——自定义脚本和提到的指标。我无法找到从文件系统中的某个统计文件查询比特率的方法,这一事实/proc/sys困扰着我。因此,我创建了第二个指标,但它依赖于网络管理器和 dbus。因此,每种方法的优缺点都很明显:一种方法需要安装indicator-sysmonitor和自定义脚本,但如果用户避免使用网络管理器,它会起作用,而另一种方法更适合使用网络管理器的标准 Ubuntu 系统。

理想情况下,解决方案应尽可能独立,查询文件系统中的特定库和/或文件/proc/sys然而,在我迄今为止的研究中,我没有找到这样的文件,并且iw其中一个解决方案所依赖的命令在内部这样做。使事情复杂化的是,显然 802.11 无线标准具有不同的调制方案来编码信号,并且比特率计算是考虑到正在使用的接口的调制方案和频率来完成的。虽然有多种方法可以获得每秒的字节TXRX,但数据与iw输出显示的不同。另一方面,对于这项任务来说,一直到 C 和系统级别似乎太多了。因此,尽管我对这两种解决方案都不完全满意,但它们为这个问题提供了适当的结果。

解决方案 1:使用自定义 shell 脚本的 indicator-sysmonitor

在此处输入图片描述

Sysmonitor 指标是由福斯自由,通常用于显示常见的统计数据,如 CPU 和内存使用情况,但它最好的一点是它允许使用自定义脚本。因此,我们的想法是有一个脚本可以以某种方式查询比特率,并通过显示它indicator-sysmonitor。为了总结以下是简要的步骤列表:

  1. 通过在终端中运行安装 indicator-sysmonitor

    sudo add-apt-repository ppa:fossfreedom/indicator-sysmonitor
    sudo apt-get update
    sudo apt-get install indicator-sysmonitor
    
  2. 创建脚本,并使用以下内容~/bin/bitrate_script.sh使其可执行chmod +x ~/bin/bitrate_script.sh

    #!/bin/bash
    iw dev wlan7 link | awk -F '[ ]' '/tx bitrate:/{print $3,$4}'
    
  3. 配置 indicator-sysmonitor 以将脚本的输出显示为自定义传感器,从 Preferences->Advanced->New ,使用br传感器和脚本的完整路径作为命令。

获取指标 sysmonitor

正如GitHub 页面该项目的安装可以通过源代码或 PPA 进行。我认为 PPA 方式最简单,因此这里给出:

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

一旦安装,就可以通过命令行indicator-sysmonitor或从 Unity dash 调用它。

设置比特率 shell 脚本

此类 shell 脚本可以通过解析命令来完成iw dev <IFACE> link。该命令以无线接口作为参数,例如wlan1wlp6s5(注意由于systemd 中的预测接口命名) 并显示各种数据,其中包括tx bitrate。例如:

$ 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

如您所见,输出需要文本处理才能输出一行文本(因为这是必需的indicator-sysmonitor),这可以通过命令完成awk。例如,这是我设置脚本的方式:

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

如果在命令行上运行脚本,输出将只是比特率和单位字符串:

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

请记住使用chmod +x命令使您的脚本可执行,否则它将无法运行。

在 indicator-sysmonitor 中显示脚本的输出

确切地说,指示器在首选项中有几个设置,它称之为“传感器”。例如,如果您设置为显示,CPU: {cpu}它将显示CPU为文本,并用实际的 CPU 使用率百分比代替花括号字符串。它的妙处在于,通过单击New按钮,您可以添加要显示为“传感器”的自定义脚本。

在屏幕截图中,您可以看到我已将自定义脚本添加为br传感器格式字符串,并且对于命令,我已提供位于文件~/WIP夹中的脚本的完整路径。该文件夹没有什么特别之处,它恰好是我当前工作内容的位置(基本上是“正在进行的工作”)。但是,通常鼓励用户将脚本放入~/bin文件夹中,因此我建议您这样做。

如果您到目前为止正确地遵循了说明,那么您应该已经indicator-sysmonitor显示了如屏幕截图中所示的比特率。


解决方案 2:使用 Python 和网络管理器的 dbus 属性自定义指标

指示器比特率实际作用

我对上述解决方案不太满意,因为它依赖于indicator-sysmonitor自定义脚本。我想要一个单一的指标,它可以在内部完成所有工作。因此,我用 Python 3 编写了一个自己的指标,它依赖于网络管理器通过dbus进程间通信系统公开的信息。简而言之,大部分工作都是由网络管理器完成的 - 我们只需使用它提供的信息。这个指标的优点是用户不必定义要使用的无线接口 - 它是自动完成的。

工作原理

指示器实际上重复相同的操作 -get_bitrates每 1 秒执行一次该函数。该函数按顺序查询多个接口上的 dbus 属性。首先,它获取所有设备并过滤掉 802.11 无线设备。接下来,我们遍历每个 wifi 设备并查询它们的名称和比特率。所有内容都构建成一个文本字符串,然后显示在指示器标签上。

理论上,指示器应该能够确定多个无线接口的比特率。例如,用户可能有内部 PCI-e 无线芯片组和 USB 芯片。不幸的是,由于我的笔记本电脑的问题以及时间限制,我没有机会测试这一点。一旦有机会,我可能会在另一台笔记本电脑上测试这一点,并会相应地编辑答案。

源代码

也可在GitHub在我的个人存储库中。

#!/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()

设置指标

所有使文件可执行的标准规则也适用于此。

  1. 将代码保存为文件夹indicator-bitrate中的文件~/bin
  2. 使用以下命令使文件可执行chmod +x ~/bin/indicator-bitrate
  3. 通过命令行运行~/bin/indicator-bitrate。如果您希望在登录时自动启动,请从 Unity Dash 打开启动应用程序并将完整路径添加到脚本作为命令。

结论

本答案中给出的两种解决方案应能满足多种情况,以便为用户提供所需的结果。第一个指示器稍微灵活一些 - 它允许显示除比特率之外的其他信息和统计数据,而第二个解决方案仅针对比特率;但是,它确实利用了网络管理器的 dbus 信息,因此我们可以确保该信息与问题中请求的信息相同。

自定义指示器可能会进一步开发或成为其他指示器的基础,以更详细地显示无线信息。欢迎在链接的 github 页面上提交错误报告和功能请求。

相关内容