├── NetMonitor.png ├── NetMonitor@zdyb.tk ├── extension.js ├── indicator.js ├── metadata.json ├── netinterface.js └── stylesheet.css ├── NetMonitorMenu.png ├── README.rst └── org.gnome.shell.extensions.net-monitor.gschema.xml /NetMonitor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azdyb/gnome-shell-extension-netmonitor/2367b6c68ca3b195d4e87143292c11b5a483095f/NetMonitor.png -------------------------------------------------------------------------------- /NetMonitor@zdyb.tk/extension.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 Aleksander Zdyb 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | const St = imports.gi.St; 19 | const Mainloop = imports.mainloop; 20 | const Main = imports.ui.main; 21 | const Lang = imports.lang; 22 | const Gio = imports.gi.Gio; 23 | const PopupMenu = imports.ui.popupMenu; 24 | const PanelMenu = imports.ui.panelMenu; 25 | const Gettext = imports.gettext; 26 | const Clutter = imports.gi.Clutter; 27 | const NMClient = imports.gi.NMClient; 28 | 29 | 30 | const NetMonitor = imports.ui.extensionSystem.extensions["NetMonitor@zdyb.tk"]; 31 | const SpeedIndicator = NetMonitor.indicator.SpeedIndicator; 32 | const NetInterface = NetMonitor.netinterface.NetInterface; 33 | 34 | 35 | const _ = Gettext.gettext; 36 | 37 | const UPDATE_INTERVAL = 2; 38 | const GSETTINGS_SCHEMA = "org.gnome.shell.extensions.net-monitor"; 39 | const HIDDEN_INTERFACES_SETTING = "hidden-interfaces"; 40 | 41 | 42 | function NetSpeed(extensionMeta) { 43 | this._init.apply(this, [extensionMeta]); 44 | } 45 | 46 | NetSpeed.prototype = { 47 | __proto__: PanelMenu.Button.prototype, 48 | 49 | run: false, 50 | indicators: [], 51 | nmclient: null, 52 | 53 | _init: function(extensionMeta) { 54 | PanelMenu.Button.prototype._init.call(this, 0.0); 55 | 56 | this.extensionMeta = extensionMeta; 57 | this.settings = new Gio.Settings({ schema: GSETTINGS_SCHEMA }); 58 | 59 | this.build_ui(); 60 | 61 | this.nmclient = NMClient.Client.new(); 62 | for each (let dev in this.nmclient.get_devices()) { 63 | this.device_added(this.nmclient, dev); 64 | } 65 | this.nmclient.connect("device-added", Lang.bind(this, this.device_added)); 66 | this.nmclient.connect("device-removed", Lang.bind(this, this.device_removed)); 67 | 68 | this.indicator_visibility_changed(); 69 | }, 70 | 71 | build_ui: function() { 72 | this.icon = new St.Icon({ 73 | icon_type: St.IconType.SYMBOLIC, 74 | style_class: "popup-menu-icon", 75 | icon_name: "network-offline" 76 | }); 77 | 78 | this.main_box = new St.BoxLayout(); 79 | this.main_box.add_actor(this.icon); 80 | 81 | this.actor.add_actor(this.main_box); 82 | 83 | this.menu_section_interfaces = new PopupMenu.PopupMenuSection(_("Show interfaces")); 84 | 85 | let title = new PopupMenu.PopupMenuItem(_("Show interfaces when connected"), { reactive: false, style_class: "section-title" }); 86 | this.menu_section_interfaces.addMenuItem(title); 87 | this.menu.addMenuItem(this.menu_section_interfaces); 88 | 89 | this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); 90 | 91 | this.menu_section_settings = new PopupMenu.PopupMenuSection("Settings"); 92 | 93 | this.menu_section_settings.addSettingsAction(_("Network Settings"), "gnome-network-panel.desktop"); 94 | this.menu.addMenuItem(this.menu_section_settings); 95 | }, 96 | 97 | /// Fired every UPDATE_INTERVAL seconds 98 | on_timeout: function() { 99 | for each (let ind in this.indicators) { 100 | ind.update(); 101 | } 102 | return this.run; 103 | }, 104 | 105 | enable: function() { 106 | this.run = true; 107 | this.on_timeout(); 108 | this._update_handler = Mainloop.timeout_add_seconds(UPDATE_INTERVAL, Lang.bind(this, this.on_timeout)); 109 | Main.panel._rightBox.insert_actor(this.actor, 0); 110 | Main.panel._menus.addMenu(this.menu) 111 | }, 112 | 113 | disable: function() { 114 | this.run = false; 115 | Mainloop.source_remove(this._update_handler); 116 | Main.panel._rightBox.remove_actor(this.actor); 117 | Main.panel._menus.removeMenu(this.menu) 118 | }, 119 | 120 | device_added: function(sender, dev) { 121 | let ni = new NetInterface(this.extensionMeta, dev); 122 | let ind = new SpeedIndicator(this.extensionMeta, ni); 123 | 124 | ind.actor.connect("show", Lang.bind(this, this.indicator_visibility_changed, ind)); 125 | ind.actor.connect("hide", Lang.bind(this, this.indicator_visibility_changed, ind)); 126 | ind.menu.connect("toggled", Lang.bind(this, this.indicator_menu_toggled, ind)); 127 | 128 | ind.set_hidden(this.settings.get_strv(HIDDEN_INTERFACES_SETTING).indexOf(ind.net_interface.get_ifname()) >= 0); 129 | this.main_box.add(ind.actor); 130 | this.menu_section_interfaces.addMenuItem(ind.menu); 131 | this.indicators.push(ind); 132 | }, 133 | 134 | device_removed: function(sender, dev) { 135 | for (let i in this.indicators) { 136 | let ind = this.indicators[i]; 137 | if (dev == ind.net_interface.nmdevice) { 138 | this.indicators.splice(i, 1); 139 | this.main_box.remove_actor(ind.actor); 140 | ind.destroy(); 141 | break; 142 | } 143 | } 144 | }, 145 | 146 | indicator_visibility_changed: function(indicator_actor, indicator) { 147 | for each(let ind in this.indicators) { 148 | if (ind.actor.visible) { 149 | this.icon.hide(); 150 | return; 151 | } 152 | } 153 | this.icon.show(); 154 | }, 155 | 156 | indicator_menu_toggled: function(menu, state, indicator) { 157 | indicator.set_hidden(!state); 158 | 159 | let ifname = indicator.net_interface.get_ifname(); 160 | let list = this.settings.get_strv(HIDDEN_INTERFACES_SETTING); 161 | let i = list.indexOf(ifname); 162 | 163 | if (!state && (i < 0)) { 164 | list.push(ifname); 165 | } else if (state && (i >= 0)) { 166 | list.splice(i, 1); 167 | } 168 | 169 | this.settings.set_strv(HIDDEN_INTERFACES_SETTING, list); 170 | } 171 | }; 172 | 173 | function init(extensionMeta) { 174 | let userExtensionLocalePath = extensionMeta.path + '/locale'; 175 | Gettext.bindtextdomain("NetMonitor", userExtensionLocalePath); 176 | Gettext.textdomain("NetMonitor"); 177 | 178 | return new NetSpeed(extensionMeta); 179 | } 180 | -------------------------------------------------------------------------------- /NetMonitor@zdyb.tk/indicator.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 Aleksander Zdyb 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | const St = imports.gi.St; 19 | const Lang = imports.lang; 20 | const NetworkManager = imports.gi.NetworkManager; 21 | const Mainloop = imports.mainloop; 22 | const PopupMenu = imports.ui.popupMenu; 23 | 24 | function SpeedIndicator(extensionMeta, net_interface) { 25 | this._init.apply(this, [extensionMeta, net_interface]); 26 | }; 27 | 28 | SpeedIndicator.prototype = { 29 | net_interface: null, 30 | is_hidden: false, 31 | actor: null, 32 | menu: null, 33 | 34 | _init: function(extensionMeta, net_interface) { 35 | this.net_interface = net_interface; 36 | this.build_ui(); 37 | this.net_interface.nmdevice.connect("state-changed", Lang.bind(this, this.state_changed)); 38 | }, 39 | 40 | build_ui: function() { 41 | this.icon = new St.Icon({ 42 | icon_type: St.IconType.SYMBOLIC, 43 | style_class: "popup-menu-icon", 44 | icon_name: this.get_icon_name() 45 | }); 46 | 47 | this.actor = new St.BoxLayout( { style_class: "speed-indicator", visible: (!this.is_hidden) } ); 48 | this.actor.add_actor(this.icon); 49 | 50 | this.label_in = new St.Label({ style_class: "bandwidth-label", text: "---" }); 51 | this.label_out = new St.Label({ style_class: "bandwidth-label", text: "---" }); 52 | 53 | this.actor.add_actor(this.label_in); 54 | this.actor.add_actor(this.label_out); 55 | this.actor.connect("parent-set", Lang.bind(this, this.parent_set)); 56 | 57 | this.menu = new PopupMenu.PopupSwitchMenuItem(this.net_interface.get_ifname(), !this.is_hidden); 58 | }, 59 | 60 | parent_set: function(sender, old_parent) { 61 | this.state_changed(this.net_interface.nmdevice, this.net_interface.get_state()); 62 | }, 63 | 64 | state_changed: function(sender, new_state, old_state, reason) { 65 | this.show(); 66 | }, 67 | 68 | update: function() { 69 | this.net_interface.update(); 70 | let speeds = this.net_interface.get_formated_speeds(); 71 | this.label_in.set_text(speeds[0]); 72 | this.label_out.set_text(speeds[1]); 73 | this.icon.icon_name = this.get_icon_name(); 74 | }, 75 | 76 | show: function(force) { 77 | if (force) this.is_hidden = false; 78 | if ( (this.is_hidden == false) && this.is_active()) this.actor.show(); 79 | else this.actor.hide(); 80 | this.update_menu(); 81 | }, 82 | 83 | hide: function() { 84 | this.actor.hide(); 85 | this.update_menu(); 86 | }, 87 | 88 | set_hidden: function(hidden) { 89 | this.is_hidden = hidden; 90 | this.show(); 91 | }, 92 | 93 | is_active_for_state: function(state) { 94 | return (state == NetworkManager.DeviceState.ACTIVATED); 95 | }, 96 | 97 | is_active: function() { 98 | return this.is_active_for_state(this.net_interface.get_state()) 99 | }, 100 | 101 | get_icon_name: function() { 102 | // TODO: Add 3G, Bluetooth and so on... 103 | let icon_name = "network-wired"; 104 | let type = this.net_interface.get_type(); 105 | 106 | if (type == NetworkManager.DeviceType.ETHERNET) { 107 | icon_name = "network-wired"; 108 | } else if (type == NetworkManager.DeviceType.WIFI) { 109 | let ap = this.net_interface.get_active_access_point(); 110 | if (ap) { 111 | let strength = ap.get_strength(); 112 | 113 | if (strength > 80) strength = "excellent"; 114 | else if (strength > 55) strength = "good"; 115 | else if (strength > 30) strength = "ok"; 116 | else if (strength > 5) strength = "weak"; 117 | else strength = "none"; 118 | 119 | icon_name = "network-wireless-signal-" + strength; 120 | } else { 121 | icon_name = "network-offline"; 122 | } 123 | } 124 | 125 | return icon_name; 126 | }, 127 | 128 | update_menu: function() { 129 | let active = this.is_active(); 130 | let label = this.net_interface.get_ifname(); 131 | if (!active) label += _(" (disconnected)"); 132 | 133 | this.menu.actor.reactive = active; 134 | this.menu.label.set_text(label); 135 | this.menu.setToggleState(!this.is_hidden); 136 | }, 137 | 138 | destroy: function() { 139 | this.actor.destroy(); 140 | this.menu.destroy(); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /NetMonitor@zdyb.tk/metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "shell-version": ["3.2"], 3 | "uuid": "NetMonitor@zdyb.tk", 4 | "name": "NetMonitor", 5 | "description": "Shows current bandwidth on network interfaces.", 6 | "url": "http://github.com/ojo/gnome-shell-extension-netmonitor", 7 | "author": "Aleksander Zdyb" 8 | } 9 | -------------------------------------------------------------------------------- /NetMonitor@zdyb.tk/netinterface.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 Aleksander Zdyb 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | const GLib = imports.gi.GLib; 19 | const NetworkManager = imports.gi.NetworkManager; 20 | const NMClient = imports.gi.NMClient; 21 | const GTop = imports.gi.GTop; 22 | 23 | function NetInterface(extensionMeta, nmdevice) { 24 | this._init.apply(this, [extensionMeta, nmdevice]); 25 | }; 26 | 27 | NetInterface.prototype = { 28 | nmdevice: null, 29 | bytes_in: 0, 30 | bytes_out: 0, 31 | speed_in: 0, 32 | speed_out: 0, 33 | last_probe_time: 0, 34 | 35 | _init: function(extensionMeta, nmdevice) { 36 | this.nmdevice = nmdevice; 37 | }, 38 | 39 | update: function() { 40 | let netload = new GTop.glibtop_netload(); 41 | let probe_time = GLib.get_monotonic_time(); 42 | 43 | GTop.glibtop_get_netload(netload, this.get_ifname()); 44 | 45 | let bytes_in_delta = netload.bytes_in - this.bytes_in; 46 | let bytes_out_delta = netload.bytes_out - this.bytes_out; 47 | 48 | this.bytes_in = netload.bytes_in; 49 | this.bytes_out = netload.bytes_out; 50 | 51 | let time_interval = (probe_time - this.last_probe_time) / 1000000; 52 | this.last_probe_time = probe_time; 53 | 54 | this.speed_in = bytes_in_delta / time_interval; 55 | this.speed_out = bytes_out_delta / time_interval; 56 | }, 57 | 58 | get_ifname: function() { 59 | return this.nmdevice.interface; 60 | }, 61 | 62 | get_type: function() { 63 | return this.nmdevice.device_type; 64 | }, 65 | 66 | get_state: function() { 67 | return this.nmdevice.get_state(); 68 | }, 69 | 70 | is_connected: function() { 71 | return (this.nmdevice.get_state() == NetworkManager.DeviceState.ACTIVATED); 72 | }, 73 | 74 | get_formated_speeds: function() { 75 | return [this.format_string(this.speed_in), this.format_string(this.speed_out)]; 76 | }, 77 | 78 | get_active_access_point: function() { 79 | if (this.nmdevice.device_type == NetworkManager.DeviceType.WIFI) 80 | return this.nmdevice.get_active_access_point(); 81 | return null; 82 | }, 83 | 84 | get_ip4: function() { 85 | let ip4_config = this.nmdevice.get_ip4_config(); 86 | 87 | if (ip4_config != null) { 88 | let addresses = []; 89 | 90 | for each (let addr in ip4_config.get_addresses()) { 91 | let ip_uint32 = addr.get_address(); 92 | let ip = []; 93 | 94 | for (let i=0; i<4; ++i) 95 | ip.push(ip_uint32 >> i*8 & 0xFF); 96 | 97 | addresses.push(ip.join(".")); 98 | } 99 | 100 | return addresses; 101 | } else 102 | return []; 103 | }, 104 | 105 | /// Formats bytes per second as IEC 60027-2 units 106 | /// For example: 483 B/s, 67.3 KiB/s, 1.28 MiB/s 107 | format_string: function(Bps) { 108 | let unit = 0; 109 | 110 | while(Bps >= 1024) { 111 | Bps /= 1024; 112 | ++unit; 113 | } 114 | 115 | // Can't use toPrecision as it may return exponential notation 116 | // let label = Bps.toPrecision(3); 117 | 118 | let precision = 0; 119 | if (Bps < 10) precision = 2; 120 | else if (Bps < 100) precision = 1; 121 | else precision = 0; 122 | 123 | let label = Bps.toFixed(precision); 124 | if (unit == 0) label += " B/s"; 125 | if (unit == 1) label += " KiB/s"; 126 | if (unit == 2) label += " MiB/s"; 127 | if (unit == 3) label += " GiB/s"; // envy 128 | 129 | return label; 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /NetMonitor@zdyb.tk/stylesheet.css: -------------------------------------------------------------------------------- 1 | .bandwidth-label { 2 | font-size: x-small; 3 | padding-left: 0.4em; 4 | min-width: 5.5em; 5 | } 6 | 7 | .speed-indicator { 8 | padding-right: 0.5em; 9 | } 10 | 11 | .speed-indicator:last-child { 12 | padding-right: 0px; 13 | } 14 | -------------------------------------------------------------------------------- /NetMonitorMenu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azdyb/gnome-shell-extension-netmonitor/2367b6c68ca3b195d4e87143292c11b5a483095f/NetMonitorMenu.png -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | What is NetMonitor? 2 | =================== 3 | 4 | NetMonitor is a humble Netspeed_ replacement for gnome-shell_. 5 | 6 | .. _Netspeed: http://projects.gnome.org/netspeed/ 7 | .. _gnome-shell: https://live.gnome.org/GnomeShell 8 | 9 | What it looks like? 10 | =================== 11 | 12 | Everybody loves screenshots, right? 13 | 14 | .. image:: http://img848.imageshack.us/img848/883/netmonitor.png 15 | :alt: Normal view 16 | 17 | .. image:: http://img11.imageshack.us/img11/4089/netmonitormenu.png 18 | :alt: Menu 19 | 20 | 21 | Disclaimer 22 | ========== 23 | 24 | As I couldn't find any real documentation for writing gnome-shell extensions, I based my code on better or worse snippets and tutorials found on internet. Some of the sources are mentioned below: 25 | 26 | * `gnome-shell-extensions `_ 27 | * `Musings of an OS plumber `_ 28 | * `gnome-shell-system-monitor-applet `_ 29 | 30 | 31 | How it works? 32 | ============= 33 | 34 | The extension lists available network devices using NMClient and uses GTop to get devices' statistics. 35 | 36 | 37 | Instalation 38 | =========== 39 | 40 | In order to install gsettings schema, copy org.gnome.shell.extensions.net-monitor.gschema.xml file to /usr/share/glib-2.0/schemas [1]_ and invoke glib-compile-schemas on that direcory. Note that you need to be root or use sudo to do that:: 41 | 42 | # cp org.gnome.shell.extensions.net-monitor.gschema.xml /usr/share/glib-2.0/schemas 43 | # glib-compile-schemas /usr/share/glib-2.0/schemas 44 | 45 | The NetMonitor@zdyb.tk directory should be copied to /usr/share/gnome-shell/extensions or ~/.local/share/gnome-shell/extensions/:: 46 | 47 | # cp -r NetMonitor\@zdyb.tk /usr/share/gnome-shell/extensions 48 | 49 | or:: 50 | 51 | $ cp -r NetMonitor\@zdyb.tk ~/.local/share/gnome-shell/extensions/ 52 | 53 | .. [1] Note, that paths may differ depending on distribution (eg /usr/local/share/) 54 | 55 | License 56 | ======= 57 | 58 | Copyright 2011 Aleksander Zdyb 59 | 60 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 61 | 62 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 63 | 64 | You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. 65 | -------------------------------------------------------------------------------- /org.gnome.shell.extensions.net-monitor.gschema.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | [] 5 | Hidden iterfaces 6 | A list of interfaces which won't be shown on panel even if they're connected. 7 | 8 | 9 | --------------------------------------------------------------------------------