Compare commits
	
		
			2 Commits
		
	
	
		
			master
			...
			gtk-status
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| e6f4038fb5 | |||
| 6fc219338f | 
							
								
								
									
										56
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										56
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@ -1,56 +0,0 @@
 | 
			
		||||
# Byte-compiled / optimized / DLL files
 | 
			
		||||
__pycache__/
 | 
			
		||||
*.py[cod]
 | 
			
		||||
 | 
			
		||||
# C extensions
 | 
			
		||||
*.so
 | 
			
		||||
 | 
			
		||||
# Distribution / packaging
 | 
			
		||||
bin/
 | 
			
		||||
build/
 | 
			
		||||
develop-eggs/
 | 
			
		||||
dist/
 | 
			
		||||
eggs/
 | 
			
		||||
lib/
 | 
			
		||||
lib64/
 | 
			
		||||
parts/
 | 
			
		||||
sdist/
 | 
			
		||||
var/
 | 
			
		||||
*.egg-info/
 | 
			
		||||
.installed.cfg
 | 
			
		||||
*.egg
 | 
			
		||||
 | 
			
		||||
# Installer logs
 | 
			
		||||
pip-log.txt
 | 
			
		||||
pip-delete-this-directory.txt
 | 
			
		||||
 | 
			
		||||
# Unit test / coverage reports
 | 
			
		||||
.tox/
 | 
			
		||||
.coverage
 | 
			
		||||
.cache
 | 
			
		||||
nosetests.xml
 | 
			
		||||
coverage.xml
 | 
			
		||||
 | 
			
		||||
# Translations
 | 
			
		||||
*.mo
 | 
			
		||||
 | 
			
		||||
# Mr Developer
 | 
			
		||||
.mr.developer.cfg
 | 
			
		||||
.project
 | 
			
		||||
.pydevproject
 | 
			
		||||
 | 
			
		||||
# Rope
 | 
			
		||||
.ropeproject
 | 
			
		||||
 | 
			
		||||
# Django stuff:
 | 
			
		||||
*.log
 | 
			
		||||
*.pot
 | 
			
		||||
 | 
			
		||||
# Sphinx documentation
 | 
			
		||||
docs/_build/
 | 
			
		||||
 | 
			
		||||
#deleted files
 | 
			
		||||
*~
 | 
			
		||||
 | 
			
		||||
# Temporary graph file
 | 
			
		||||
graph.png
 | 
			
		||||
							
								
								
									
										15
									
								
								COPYING.txt
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								COPYING.txt
									
									
									
									
									
								
							@ -1,15 +0,0 @@
 | 
			
		||||
This package 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 2 of the License, or
 | 
			
		||||
(at your option) any later version.
 | 
			
		||||
.
 | 
			
		||||
This package 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.
 | 
			
		||||
.
 | 
			
		||||
You should have received a copy of the GNU General Public License
 | 
			
		||||
along with this program. If not, see <http://www.gnu.org/licenses/>
 | 
			
		||||
.
 | 
			
		||||
On Debian systems, the complete text of the GNU General
 | 
			
		||||
Public License version 2 can be found in "/usr/share/common-licenses/GPL-2".
 | 
			
		||||
							
								
								
									
										26
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								README.md
									
									
									
									
									
								
							@ -1,26 +0,0 @@
 | 
			
		||||
 
 | 
			
		||||
 | 
			
		||||
Pinger.py
 | 
			
		||||
=========
 | 
			
		||||
A ping tool that sits in your system tray
 | 
			
		||||
---------
 | 
			
		||||
 | 
			
		||||
- Saves your sanity when the wifi sucks
 | 
			
		||||
- Doesn't clutter up your screen with ping windows
 | 
			
		||||
- Lets you know when pings fail instead of silently failing
 | 
			
		||||
- **Currently for Ubuntu only**
 | 
			
		||||
 - (Requires Python, GTK, and AppIndicator3)
 | 
			
		||||
 - Startup Automatically option creates a `~/.config/autostart/pinger.desktop` file
 | 
			
		||||
 - **Contributions welcome to expand to other OSes!**
 | 
			
		||||
 | 
			
		||||
**Usage (in Ubuntu):**
 | 
			
		||||
 | 
			
		||||
Open the Terminal program and enter the following commands:
 | 
			
		||||
 | 
			
		||||
    cd ~
 | 
			
		||||
    git clone https://github.com/zyphlar/pinger.git
 | 
			
		||||
    python pinger/pinger.py &
 | 
			
		||||
 | 
			
		||||
Pinger should open in your system tray. To set Pinger to start automatically (in Ubuntu) click it and choose Start Automatically.
 | 
			
		||||
 | 
			
		||||
Report bugs or feature requests at https://github.com/zyphlar/pinger/issues
 | 
			
		||||
							
								
								
									
										4
									
								
								README.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								README.txt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,4 @@
 | 
			
		||||
Pinger.py -- A ping tool that sits in your system tray
 | 
			
		||||
 | 
			
		||||
+ Saves your sanity when the wifi sucks
 | 
			
		||||
+ Doesn't clutter up your screen with ping windows
 | 
			
		||||
							
								
								
									
										5
									
								
								TODO.md
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								TODO.md
									
									
									
									
									
								
							@ -1,5 +0,0 @@
 | 
			
		||||
- local caching
 | 
			
		||||
- remote submission
 | 
			
		||||
- centralized reporting
 | 
			
		||||
- packaging/distribution
 | 
			
		||||
- cross-platform gui
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								activity.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								activity.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 8.6 KiB  | 
							
								
								
									
										114
									
								
								appindicator.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								appindicator.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,114 @@
 | 
			
		||||
#!/usr/bin/env python
 | 
			
		||||
#
 | 
			
		||||
# Copyright 2009-2012 Canonical Ltd.
 | 
			
		||||
#
 | 
			
		||||
# Authors: Neil Jagdish Patel <neil.patel@canonical.com>
 | 
			
		||||
#          Jono Bacon <jono@ubuntu.com>
 | 
			
		||||
#          David Planella <david.planella@ubuntu.com>
 | 
			
		||||
#
 | 
			
		||||
# This program is free software: you can redistribute it and/or modify it 
 | 
			
		||||
# under the terms of either or both of the following licenses:
 | 
			
		||||
#
 | 
			
		||||
# 1) the GNU Lesser General Public License version 3, as published by the 
 | 
			
		||||
# Free Software Foundation; and/or
 | 
			
		||||
# 2) the GNU Lesser General Public License version 2.1, as published by 
 | 
			
		||||
# the Free Software Foundation.
 | 
			
		||||
#
 | 
			
		||||
# This program is distributed in the hope that it will be useful, but 
 | 
			
		||||
# WITHOUT ANY WARRANTY; without even the implied warranties of 
 | 
			
		||||
# MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR 
 | 
			
		||||
# PURPOSE.  See the applicable version of the GNU Lesser General Public 
 | 
			
		||||
# License for more details.
 | 
			
		||||
#
 | 
			
		||||
# You should have received a copy of both the GNU Lesser General Public 
 | 
			
		||||
# License version 3 and version 2.1 along with this program.  If not, see 
 | 
			
		||||
# <http://www.gnu.org/licenses/>
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
from gi.repository import Gtk
 | 
			
		||||
 | 
			
		||||
# Timer
 | 
			
		||||
from gi.repository import GObject as gobject
 | 
			
		||||
# Pinging
 | 
			
		||||
import subprocess
 | 
			
		||||
# Regex
 | 
			
		||||
import re
 | 
			
		||||
 | 
			
		||||
# Vars
 | 
			
		||||
host = "www.google.com"
 | 
			
		||||
 | 
			
		||||
class HelloWorld:
 | 
			
		||||
 | 
			
		||||
  def ping(self, widget=None, data=None):
 | 
			
		||||
    ping = subprocess.Popen(
 | 
			
		||||
        ["ping", "-c", "1", host],
 | 
			
		||||
        stdout = subprocess.PIPE,
 | 
			
		||||
        stderr = subprocess.PIPE
 | 
			
		||||
    )
 | 
			
		||||
    out, error = ping.communicate()
 | 
			
		||||
    if error:
 | 
			
		||||
      label = "!! FAIL !!"
 | 
			
		||||
    else:
 | 
			
		||||
      m = re.search('time=(.*) ms', out)
 | 
			
		||||
      label = m.group(1)+" ms"
 | 
			
		||||
    #ind.set_label (label, "100.0 ms")
 | 
			
		||||
    status.set_title(label)
 | 
			
		||||
    #self.ping_menu_item.set_label(out)
 | 
			
		||||
    gobject.timeout_add_seconds(self.timeout, self.ping)
 | 
			
		||||
 | 
			
		||||
  def destroy(self, widget, data=None):
 | 
			
		||||
    print "destroy signal occurred"
 | 
			
		||||
    Gtk.main_quit()
 | 
			
		||||
 | 
			
		||||
  def __init__(self):
 | 
			
		||||
    # register a periodic timer
 | 
			
		||||
    self.counter = 0
 | 
			
		||||
    self.timeout = 1
 | 
			
		||||
    gobject.timeout_add_seconds(self.timeout, self.ping)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def menuitem_response(w, buf):
 | 
			
		||||
  print buf
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def create_menu_item(menu, text, callback):
 | 
			
		||||
 | 
			
		||||
  menu_items = Gtk.MenuItem(text)
 | 
			
		||||
 | 
			
		||||
  menu.append(menu_items)
 | 
			
		||||
 | 
			
		||||
  menu_items.connect("activate", callback, text)
 | 
			
		||||
 | 
			
		||||
  # show the items
 | 
			
		||||
  menu_items.show()
 | 
			
		||||
 | 
			
		||||
  return menu_items
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
  status = Gtk.StatusIcon()
 | 
			
		||||
  status.set_title("0.0 ms")
 | 
			
		||||
  status.set_from_stock(Gtk.STOCK_HOME)
 | 
			
		||||
  status.connect("activate",Gtk.Window.present) 
 | 
			
		||||
  #ind = appindicator.Indicator.new (
 | 
			
		||||
  #                      "pinger",
 | 
			
		||||
  #                      "", #indicator-messages
 | 
			
		||||
  #                      appindicator.IndicatorCategory.COMMUNICATIONS)
 | 
			
		||||
  #ind.set_status (appindicator.IndicatorStatus.ACTIVE)
 | 
			
		||||
  #ind.set_attention_icon ("indicator-messages-new")
 | 
			
		||||
  #ind.set_label ("0.0 ms", "100.0 ms")
 | 
			
		||||
 | 
			
		||||
  # create a menu
 | 
			
		||||
  menu = Gtk.Menu()
 | 
			
		||||
 | 
			
		||||
  # and the app
 | 
			
		||||
  hello = HelloWorld()
 | 
			
		||||
 | 
			
		||||
  # create menu items
 | 
			
		||||
  #hello.ping_menu_item = create_menu_item(menu, "Ping", hello.ping)
 | 
			
		||||
  create_menu_item(menu, "Exit", hello.destroy)
 | 
			
		||||
 | 
			
		||||
  # Add the menu to our statusbar
 | 
			
		||||
  #ind.set_menu(menu)
 | 
			
		||||
 | 
			
		||||
  # Runtime loop
 | 
			
		||||
  Gtk.main()
 | 
			
		||||
							
								
								
									
										32
									
								
								new_gtk.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								new_gtk.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,32 @@
 | 
			
		||||
import gtk
 | 
			
		||||
 | 
			
		||||
class Main(gtk.Window):
 | 
			
		||||
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        super(Main, self).__init__()
 | 
			
		||||
        self.connect('delete-event', self.on_delete_event)
 | 
			
		||||
        self.set_title("Virtual Machine Monitor")
 | 
			
		||||
        self.set_position(gtk.WIN_POS_CENTER)
 | 
			
		||||
        self.set_default_size(640,600)
 | 
			
		||||
        self.set_geometry_hints(min_width=640, min_height=600)
 | 
			
		||||
        self.set_icon_from_file("activity.png")
 | 
			
		||||
        #menubar = self.add_menubar()
 | 
			
		||||
 | 
			
		||||
        pixbuf = gtk.gdk.pixbuf_new_from_file_at_size("activity.png",25,25)
 | 
			
		||||
        statusicon = gtk.StatusIcon()
 | 
			
		||||
        statusicon.set_title("0.0 ms")
 | 
			
		||||
        statusicon = gtk.status_icon_new_from_pixbuf(pixbuf)
 | 
			
		||||
        statusicon.connect("activate",self.tray_activate)
 | 
			
		||||
        self.show_all()
 | 
			
		||||
 | 
			
		||||
    def on_delete_event(self, widget, event):
 | 
			
		||||
        self.hide()
 | 
			
		||||
        return True    
 | 
			
		||||
 | 
			
		||||
    def tray_activate(self, widget):
 | 
			
		||||
        self.present()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    Main()
 | 
			
		||||
    gtk.main()
 | 
			
		||||
							
								
								
									
										146
									
								
								ping.inc.py
									
									
									
									
									
								
							
							
						
						
									
										146
									
								
								ping.inc.py
									
									
									
									
									
								
							@ -1,146 +0,0 @@
 | 
			
		||||
#!/usr/bin/env python
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
#
 | 
			
		||||
# Pinger.py -- A ping tool that sits in your system tray
 | 
			
		||||
# Copyright 2013 Will Bradley
 | 
			
		||||
#
 | 
			
		||||
# Contributors: Will Bradley <bradley.will@gmail.com>
 | 
			
		||||
#               AltF4 <altf4@phx2600.org>
 | 
			
		||||
#
 | 
			
		||||
# This program is free software: you can redistribute it and/or modify it
 | 
			
		||||
# under the terms of either or both of the following licenses:
 | 
			
		||||
#
 | 
			
		||||
# 1) the GNU Lesser General Public License version 3, as published by the
 | 
			
		||||
# Free Software Foundation; and/or
 | 
			
		||||
# 2) the GNU Lesser General Public License version 2.1, as published by
 | 
			
		||||
# the Free Software Foundation.
 | 
			
		||||
#
 | 
			
		||||
# This program is distributed in the hope that it will be useful, but
 | 
			
		||||
# WITHOUT ANY WARRANTY; without even the implied warranties of
 | 
			
		||||
# MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
 | 
			
		||||
# PURPOSE.  See the applicable version of the GNU Lesser General Public
 | 
			
		||||
# License for more details.
 | 
			
		||||
#
 | 
			
		||||
# You should have received a copy of both the GNU Lesser General Public
 | 
			
		||||
# License version 3 and version 2.1 along with this program.  If not, see
 | 
			
		||||
# <http://www.gnu.org/licenses/>
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Dependencies
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
# System Tray Icon
 | 
			
		||||
 | 
			
		||||
# Timer
 | 
			
		||||
 | 
			
		||||
# Date
 | 
			
		||||
import datetime
 | 
			
		||||
# Pinging
 | 
			
		||||
import subprocess
 | 
			
		||||
# Regex
 | 
			
		||||
import re
 | 
			
		||||
# Ctrl-c
 | 
			
		||||
import signal
 | 
			
		||||
# File paths
 | 
			
		||||
import os
 | 
			
		||||
# OS naming
 | 
			
		||||
import platform
 | 
			
		||||
# Argument parsing
 | 
			
		||||
import argparse
 | 
			
		||||
# For exit
 | 
			
		||||
import sys
 | 
			
		||||
# For graphing
 | 
			
		||||
 | 
			
		||||
# For IP addresses
 | 
			
		||||
import socket, struct
 | 
			
		||||
# For API
 | 
			
		||||
import json
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Main Class
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
class Pinger:
 | 
			
		||||
 | 
			
		||||
  def ping(self, target, log=None, widget=None, data=None):
 | 
			
		||||
      pingResult = Util.doPing(target)
 | 
			
		||||
      ssidResult = Util.getSsid()
 | 
			
		||||
 | 
			
		||||
      print json.dumps({
 | 
			
		||||
        'target': target,
 | 
			
		||||
        'ssid': ssidResult,
 | 
			
		||||
        'loss': float(pingResult['loss']),
 | 
			
		||||
        'rtt_avg': float(pingResult['rtt_avg']),
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
  def __init__(self):
 | 
			
		||||
    # Print welcome message
 | 
			
		||||
    print "Starting Pinger..."
 | 
			
		||||
 | 
			
		||||
    self.ping("4.2.2.2")
 | 
			
		||||
 | 
			
		||||
    # Print started message
 | 
			
		||||
    print "Finished."
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Utility class for platform-agnosticism and reusability
 | 
			
		||||
#
 | 
			
		||||
class Util:
 | 
			
		||||
  @staticmethod
 | 
			
		||||
  def getSsid():
 | 
			
		||||
    if platform.system() == "Linux":
 | 
			
		||||
      ssid = subprocess.Popen(["nmcli","-t","-f","active,ssid","dev","wifi"],
 | 
			
		||||
          stdout = subprocess.PIPE,
 | 
			
		||||
          stderr = subprocess.PIPE
 | 
			
		||||
      ) #  | egrep '^yes(.*)' | cut -d\' -f2
 | 
			
		||||
      out, error = ssid.communicate()
 | 
			
		||||
      m = re.search(r'yes:(.*)', out)
 | 
			
		||||
      result = m.group(1)
 | 
			
		||||
    elif platform.system() == "Darwin":
 | 
			
		||||
      ssid = subprocess.Popen(["/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport","-I"],
 | 
			
		||||
          stdout = subprocess.PIPE,
 | 
			
		||||
          stderr = subprocess.PIPE
 | 
			
		||||
      )
 | 
			
		||||
      out, error = ssid.communicate()
 | 
			
		||||
      result = out
 | 
			
		||||
    return result
 | 
			
		||||
 | 
			
		||||
  @staticmethod
 | 
			
		||||
  def doPing(target):
 | 
			
		||||
    if platform.system() == "Linux":
 | 
			
		||||
      ping = subprocess.Popen(
 | 
			
		||||
          ["ping", "-c", "5", target],
 | 
			
		||||
          stdout = subprocess.PIPE,
 | 
			
		||||
          stderr = subprocess.PIPE
 | 
			
		||||
      )
 | 
			
		||||
      pingOut, pingError = ping.communicate()
 | 
			
		||||
      # Doing two regexes because Python doesn't like doing both in one
 | 
			
		||||
      lossResult = re.search(r' ([\d\.]+)% packet loss', pingOut, re.MULTILINE)
 | 
			
		||||
      rttAvgResult = re.search(r' = [\d\.]+/([\d\.]+)/[\d\.]+/[\d\.]+', pingOut, re.MULTILINE)
 | 
			
		||||
      if pingError or lossResult is None or rttAvgResult is None:
 | 
			
		||||
        output = {'loss':-1, 'rtt_avg':-1};
 | 
			
		||||
      else:
 | 
			
		||||
        output = {'loss':lossResult.group(1), 'rtt_avg':rttAvgResult.group(1)};
 | 
			
		||||
    elif platform.system() == "Darwin": # Macintosh
 | 
			
		||||
      ping = subprocess.Popen(
 | 
			
		||||
          ["ping", "-c", "5", target],
 | 
			
		||||
          stdout = subprocess.PIPE,
 | 
			
		||||
          stderr = subprocess.PIPE
 | 
			
		||||
      )
 | 
			
		||||
      pingOut, pingError = ping.communicate()
 | 
			
		||||
      # print out
 | 
			
		||||
      lossResult = re.search(r' ([\d\.]+)% packet loss', pingOut, re.MULTILINE)
 | 
			
		||||
      rttAvgResult = re.search(r' = [\d\.]+/([\d\.]+)/[\d\.]+/[\d\.]+', pingOut, re.MULTILINE)
 | 
			
		||||
      if pingError or lossResult is None or rttAvgResult is None:
 | 
			
		||||
        output = {'loss':-1, 'rtt_avg':-1};
 | 
			
		||||
      else:
 | 
			
		||||
        output = {'loss':lossResult.group(1), 'rtt_avg':rttAvgResult.group(1)};
 | 
			
		||||
    return output
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Runtime
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
  pinger = Pinger()
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								pinger.png
									
									
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								pinger.png
									
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 18 KiB  | 
							
								
								
									
										366
									
								
								pinger.py
									
									
									
									
									
								
							
							
						
						
									
										366
									
								
								pinger.py
									
									
									
									
									
								
							@ -1,366 +0,0 @@
 | 
			
		||||
#!/usr/bin/env python
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
#
 | 
			
		||||
# Pinger.py -- A ping tool that sits in your system tray
 | 
			
		||||
# Copyright 2013 Will Bradley
 | 
			
		||||
#
 | 
			
		||||
# Contributors: Will Bradley <bradley.will@gmail.com>
 | 
			
		||||
#               AltF4 <altf4@phx2600.org>
 | 
			
		||||
#
 | 
			
		||||
# This program is free software: you can redistribute it and/or modify it 
 | 
			
		||||
# under the terms of either or both of the following licenses:
 | 
			
		||||
#
 | 
			
		||||
# 1) the GNU Lesser General Public License version 3, as published by the 
 | 
			
		||||
# Free Software Foundation; and/or
 | 
			
		||||
# 2) the GNU Lesser General Public License version 2.1, as published by 
 | 
			
		||||
# the Free Software Foundation.
 | 
			
		||||
#
 | 
			
		||||
# This program is distributed in the hope that it will be useful, but 
 | 
			
		||||
# WITHOUT ANY WARRANTY; without even the implied warranties of 
 | 
			
		||||
# MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR 
 | 
			
		||||
# PURPOSE.  See the applicable version of the GNU Lesser General Public 
 | 
			
		||||
# License for more details.
 | 
			
		||||
#
 | 
			
		||||
# You should have received a copy of both the GNU Lesser General Public 
 | 
			
		||||
# License version 3 and version 2.1 along with this program.  If not, see 
 | 
			
		||||
# <http://www.gnu.org/licenses/>
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Dependencies
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
# System Tray Icon
 | 
			
		||||
from gi.repository import Gtk
 | 
			
		||||
from gi.repository import AppIndicator3 as appindicator
 | 
			
		||||
# Timer
 | 
			
		||||
from gi.repository import GObject as gobject
 | 
			
		||||
import datetime
 | 
			
		||||
# Pinging
 | 
			
		||||
import subprocess
 | 
			
		||||
# Regex
 | 
			
		||||
import re
 | 
			
		||||
# Ctrl-c
 | 
			
		||||
import signal
 | 
			
		||||
# File paths
 | 
			
		||||
import os
 | 
			
		||||
# Argument parsing
 | 
			
		||||
import argparse
 | 
			
		||||
# For exit
 | 
			
		||||
import sys
 | 
			
		||||
# For graphing
 | 
			
		||||
import cairo
 | 
			
		||||
# For IP addresses
 | 
			
		||||
import socket, struct
 | 
			
		||||
 | 
			
		||||
# Vars
 | 
			
		||||
startup_active_label = "✓ Start Automatically"
 | 
			
		||||
startup_inactive_label = "Start Automatically"
 | 
			
		||||
pause_label = "Pause"
 | 
			
		||||
play_label = "Resume"
 | 
			
		||||
home_path = os.path.expanduser("~")
 | 
			
		||||
startup_path = home_path+'/.config/autostart/pinger.desktop'
 | 
			
		||||
startup_dir = home_path+'/.config/autostart/'
 | 
			
		||||
 | 
			
		||||
parser = argparse.ArgumentParser()
 | 
			
		||||
parser.add_argument("-t", "--target", help="Target to PING against. (IP / Hostname / Domain name). Defaults to 8.8.8.8")
 | 
			
		||||
parser.add_argument("-f", "--freq", help="Timeout between pings, in seconds. Defaults to 5")
 | 
			
		||||
parser.add_argument("-m", "--maxlog", help="Maximum amount of pings to log. Defaults to 40")
 | 
			
		||||
parser.add_argument("-c", "--color", help="Color scheme ('dark' or 'light'). Defaults to dark.")
 | 
			
		||||
args = parser.parse_args()
 | 
			
		||||
 | 
			
		||||
ubuntu_mono_dark_rgba = [0xdf, 0xd8, 0xc8, 0xff]
 | 
			
		||||
ubuntu_mono_light_rgba = [0x3a, 0x39, 0x35, 0xff]
 | 
			
		||||
black = [0, 0, 0, 0xff]
 | 
			
		||||
red = [0xdf, 0x38, 0x2c, 0xff]
 | 
			
		||||
orange = [0xdd, 0x48, 0x14, 0xff]
 | 
			
		||||
yellow = [0xef, 0xb7, 0x3e, 0xff]
 | 
			
		||||
white = [0xff, 0xff, 0xff, 0xff]
 | 
			
		||||
dark_bg = [0, 0, 0, 0x3f]
 | 
			
		||||
light_bg = [0xff, 0xff, 0xff, 0x3f]
 | 
			
		||||
 | 
			
		||||
#accumulate the arguments for use later
 | 
			
		||||
arguments = " "
 | 
			
		||||
for arg in sys.argv[1:]:
 | 
			
		||||
  arguments += arg + " "
 | 
			
		||||
 | 
			
		||||
# User-editable variables
 | 
			
		||||
if args.target:
 | 
			
		||||
  default_host = args.target
 | 
			
		||||
else:
 | 
			
		||||
  print "Using default Internet target of 8.8.8.8"
 | 
			
		||||
  default_host = "8.8.8.8" # IP or hostname of WAN
 | 
			
		||||
 | 
			
		||||
default_router = "192.168.1.1" # IP or hostname of router
 | 
			
		||||
 | 
			
		||||
if args.freq:
 | 
			
		||||
  try:
 | 
			
		||||
    ping_frequency = int(args.freq)
 | 
			
		||||
  except ValueError:
 | 
			
		||||
    sys.stderr.write("Error parsing argument '--freq'\n")
 | 
			
		||||
    sys.exit(1)
 | 
			
		||||
else:
 | 
			
		||||
  ping_frequency = 5 # in seconds
 | 
			
		||||
 | 
			
		||||
if args.maxlog:
 | 
			
		||||
  try:
 | 
			
		||||
    ping_log_max_size = int(args.maxlog)
 | 
			
		||||
  except ValueError:
 | 
			
		||||
    sys.stderr.write("Error parsing argument '--maxlog'\n")
 | 
			
		||||
    sys.exit(1)
 | 
			
		||||
else:
 | 
			
		||||
  ping_log_max_size = 40
 | 
			
		||||
 | 
			
		||||
if args.color == "light":
 | 
			
		||||
  graph_color = ubuntu_mono_light_rgba
 | 
			
		||||
  danger_color = red
 | 
			
		||||
  warning_color = yellow
 | 
			
		||||
  graph_highlight = ubuntu_mono_dark_rgba
 | 
			
		||||
  graph_background = light_bg
 | 
			
		||||
else:
 | 
			
		||||
  graph_color = ubuntu_mono_dark_rgba
 | 
			
		||||
  danger_color = red
 | 
			
		||||
  warning_color = yellow
 | 
			
		||||
  graph_highlight = ubuntu_mono_light_rgba
 | 
			
		||||
  graph_background = dark_bg
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Main Class
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
class Pinger:
 | 
			
		||||
  host = None
 | 
			
		||||
  router = None
 | 
			
		||||
  host_log = []
 | 
			
		||||
  router_log = []
 | 
			
		||||
  paused = False
 | 
			
		||||
  autostart = False
 | 
			
		||||
  icon_height = 22
 | 
			
		||||
 | 
			
		||||
  def ping(self, target, log, widget=None, data=None):
 | 
			
		||||
    if not self.paused:
 | 
			
		||||
      #print "Pinging "+str(target)
 | 
			
		||||
      ping = subprocess.Popen(
 | 
			
		||||
          ["ping", "-c", "1", target],
 | 
			
		||||
          stdout = subprocess.PIPE,
 | 
			
		||||
          stderr = subprocess.PIPE
 | 
			
		||||
      )
 | 
			
		||||
      out, error = ping.communicate()
 | 
			
		||||
      m = re.search('time=(.*) ms', out)
 | 
			
		||||
      if error or m == None:
 | 
			
		||||
        label = "PING FAIL"
 | 
			
		||||
        self.log_ping(log, -1)
 | 
			
		||||
      else:
 | 
			
		||||
        latency = "%.2f" % float(m.group(1))
 | 
			
		||||
        label = latency+" ms"
 | 
			
		||||
        self.log_ping(log, latency)
 | 
			
		||||
 | 
			
		||||
      #self.ind.set_label(label, "100.0 ms")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  def ping_both(self):
 | 
			
		||||
    self.ping(self.host, self.host_log)
 | 
			
		||||
    self.ping(self.router, self.router_log)
 | 
			
		||||
    self.update_log_menu()
 | 
			
		||||
 | 
			
		||||
    # If we have 5 router failures, try getting the default GW again
 | 
			
		||||
    if (self.routerLastUpdated != None
 | 
			
		||||
      and (datetime.datetime.now()-self.routerLastUpdated).seconds > 60):
 | 
			
		||||
 | 
			
		||||
      if (len(self.router_log) > 5
 | 
			
		||||
        and self.router_log[-1] == -1
 | 
			
		||||
        and self.router_log[-2] == -1
 | 
			
		||||
        and self.router_log[-3] == -1):
 | 
			
		||||
 | 
			
		||||
        new_router = self.get_default_gateway_linux()
 | 
			
		||||
        # Only update router if it's not blank
 | 
			
		||||
        if new_router != None:
 | 
			
		||||
          self.router = new_router
 | 
			
		||||
          print "Updated router target to "+str(self.router)
 | 
			
		||||
          self.routerLastUpdated = datetime.datetime.now()
 | 
			
		||||
 | 
			
		||||
    gobject.timeout_add_seconds(self.timeout, self.ping_both)
 | 
			
		||||
 | 
			
		||||
  def log_ping(self, log, value):
 | 
			
		||||
    log.append(float(value))
 | 
			
		||||
    # limit the size of the log
 | 
			
		||||
    if len(log) >= ping_log_max_size:
 | 
			
		||||
      # remove the earliest ping, not the latest
 | 
			
		||||
      log.pop(0) 
 | 
			
		||||
 | 
			
		||||
  def create_menu_item(self, text, callback):
 | 
			
		||||
    menu_item = Gtk.MenuItem(text)
 | 
			
		||||
    self.menu.append(menu_item)
 | 
			
		||||
    if callback:
 | 
			
		||||
      menu_item.connect("activate", callback, text)
 | 
			
		||||
    menu_item.show()
 | 
			
		||||
    return menu_item
 | 
			
		||||
 | 
			
		||||
  def destroy(self, widget, data=None):
 | 
			
		||||
    print "Quitting..."
 | 
			
		||||
    Gtk.main_quit()
 | 
			
		||||
 | 
			
		||||
  def toggle_autostart(self, widget, data=None):
 | 
			
		||||
    if not self.autostart:
 | 
			
		||||
      if not os.path.exists(startup_dir):
 | 
			
		||||
        os.makedirs(startup_dir)
 | 
			
		||||
      with open(startup_path,'w') as f:
 | 
			
		||||
        f.write("[Desktop Entry]\r\n"
 | 
			
		||||
                "Type=Application\r\n"
 | 
			
		||||
                "Exec=python "+os.path.abspath( __file__ )+arguments+"\r\n"
 | 
			
		||||
                "X-GNOME-Autostart-enabled=true\r\n"
 | 
			
		||||
                "Name=Pinger\r\n"
 | 
			
		||||
                "Comment=Pings the internet every few seconds")
 | 
			
		||||
      self.autostart = True
 | 
			
		||||
      self.startup_menu.set_label(startup_active_label)
 | 
			
		||||
    else:
 | 
			
		||||
      os.remove(startup_path)
 | 
			
		||||
      self.autostart = False
 | 
			
		||||
      self.startup_menu.set_label(startup_inactive_label)
 | 
			
		||||
 | 
			
		||||
  def toggle_pause(self, widget, data=None):
 | 
			
		||||
    if self.paused:
 | 
			
		||||
      self.paused = False
 | 
			
		||||
      self.pause_menu.set_label(pause_label)
 | 
			
		||||
    else:
 | 
			
		||||
      self.paused = True
 | 
			
		||||
      self.pause_menu.set_label(play_label)
 | 
			
		||||
 | 
			
		||||
  def update_log_menu(self):
 | 
			
		||||
    surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, ping_log_max_size, self.icon_height)
 | 
			
		||||
    ctx = cairo.Context(surface)
 | 
			
		||||
 | 
			
		||||
    # draw semitransparent box
 | 
			
		||||
    self.draw_rect( ctx, [0,0], [ping_log_max_size,self.icon_height], graph_background )
 | 
			
		||||
 | 
			
		||||
    if len(self.host_log) > 0:
 | 
			
		||||
      self.draw_log(ctx, self.host_log, 0)
 | 
			
		||||
      host_avg = sum(self.host_log)/len(self.host_log)
 | 
			
		||||
      self.ping_menu.set_label("Internet: "+str(int(round(self.host_log[-1])))+" ms "+str(int(round(host_avg)))+" avg")
 | 
			
		||||
    if len(self.router_log) > 0:
 | 
			
		||||
      self.draw_log(ctx, self.router_log, (self.icon_height/2))
 | 
			
		||||
      router_avg = sum(self.router_log)/len(self.router_log)
 | 
			
		||||
      self.router_menu.set_label("Router: "+str(int(round(self.router_log[-1])))+" ms "+str(int(round(router_avg)))+" avg")
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
      os.remove("/tmp/graph.png")
 | 
			
		||||
    except:
 | 
			
		||||
      pass
 | 
			
		||||
    surface.write_to_png("/tmp/graph.png")
 | 
			
		||||
    self.ind.set_icon("") # gotta set it to nothing in order to update
 | 
			
		||||
    self.ind.set_icon("graph")
 | 
			
		||||
 
 | 
			
		||||
 | 
			
		||||
  def draw_log(self, ctx, log, yOffset):
 | 
			
		||||
    if(max(log) < 100):
 | 
			
		||||
      max_ping = 100
 | 
			
		||||
    elif(max(log) > 1000):
 | 
			
		||||
      max_ping = 1000
 | 
			
		||||
    else:
 | 
			
		||||
      max_ping = max(log)
 | 
			
		||||
 | 
			
		||||
    for index, ping in enumerate(log):
 | 
			
		||||
 | 
			
		||||
      if float(ping) == -1: # Ping error
 | 
			
		||||
        # Draw full-height error bar
 | 
			
		||||
 | 
			
		||||
        self.draw_rect( ctx, [index,(self.icon_height/2)+yOffset], [1,(-self.icon_height/2)-1], danger_color )
 | 
			
		||||
      else:
 | 
			
		||||
        # draw normal bar
 | 
			
		||||
        bar_height = -int(self.scale(ping, (0,max_ping), (0,(self.icon_height/2)-1)))
 | 
			
		||||
 | 
			
		||||
        if bar_height > -1:
 | 
			
		||||
          bar_height = -1
 | 
			
		||||
 | 
			
		||||
        if ping > 100:
 | 
			
		||||
          color = warning_color
 | 
			
		||||
        else:
 | 
			
		||||
          color = graph_color
 | 
			
		||||
 | 
			
		||||
        self.draw_rect( ctx, [index,self.icon_height/2+yOffset], [1,bar_height], color )
 | 
			
		||||
 | 
			
		||||
   
 | 
			
		||||
  def draw_rect(self, ctx, point, size, rgba):
 | 
			
		||||
    ctx.rectangle( point[0], point[1], size[0], size[1] )
 | 
			
		||||
    ctx.set_source_rgba(rgba[0]/float(255), rgba[1]/float(255), rgba[2]/float(255), rgba[3]/float(255))
 | 
			
		||||
    ctx.fill()
 | 
			
		||||
 | 
			
		||||
  def scale(self, val, src, dst):
 | 
			
		||||
    """
 | 
			
		||||
    Scale the given value from the scale of src to the scale of dst.
 | 
			
		||||
    """
 | 
			
		||||
    scale = ((val - src[0]) / (src[1]-src[0])) * (dst[1]-dst[0]) + dst[0]
 | 
			
		||||
    return scale
 | 
			
		||||
 | 
			
		||||
  def get_default_gateway_linux(self):
 | 
			
		||||
    # Read the default gateway directly from /proc.
 | 
			
		||||
    with open("/proc/net/route") as fh:
 | 
			
		||||
      for line in fh:
 | 
			
		||||
        fields = line.strip().split()
 | 
			
		||||
        if fields[1] != '00000000' or not int(fields[3], 16) & 2:
 | 
			
		||||
          continue
 | 
			
		||||
 | 
			
		||||
        return str(socket.inet_ntoa(struct.pack("<L", int(fields[2], 16))))
 | 
			
		||||
 | 
			
		||||
  def __init__(self):
 | 
			
		||||
    # Handle ctrl-c
 | 
			
		||||
    signal.signal(signal.SIGINT, self.destroy)
 | 
			
		||||
 | 
			
		||||
    # Print welcome message
 | 
			
		||||
    print "Starting Pinger..."
 | 
			
		||||
 | 
			
		||||
    # Create systray icon
 | 
			
		||||
    self.ind = appindicator.Indicator.new (
 | 
			
		||||
               "pinger",
 | 
			
		||||
               "", # no icon
 | 
			
		||||
               appindicator.IndicatorCategory.SYSTEM_SERVICES)
 | 
			
		||||
    self.ind.set_status (appindicator.IndicatorStatus.ACTIVE)
 | 
			
		||||
    #self.ind.set_label ("Pinger Loading...", "Pinger Loading...")
 | 
			
		||||
    self.ind.set_icon_theme_path("/tmp")
 | 
			
		||||
 | 
			
		||||
    # create a menu
 | 
			
		||||
    self.menu = Gtk.Menu()
 | 
			
		||||
    # with ping numbers
 | 
			
		||||
    self.ping_menu = self.create_menu_item("", None)
 | 
			
		||||
    self.router_menu = self.create_menu_item("", None)
 | 
			
		||||
    # with pause option
 | 
			
		||||
    self.pause_menu = self.create_menu_item(pause_label, self.toggle_pause)
 | 
			
		||||
    # with autostart option
 | 
			
		||||
    # first, check current autostart state by checking existance of .desktop file
 | 
			
		||||
    if os.path.exists(startup_path):
 | 
			
		||||
      self.autostart = True
 | 
			
		||||
      self.startup_menu = self.create_menu_item(startup_active_label, self.toggle_autostart)
 | 
			
		||||
    else:
 | 
			
		||||
      self.autostart = False
 | 
			
		||||
      self.startup_menu = self.create_menu_item(startup_inactive_label, self.toggle_autostart)
 | 
			
		||||
    # and exit option
 | 
			
		||||
    self.create_menu_item("Exit", self.destroy)
 | 
			
		||||
    self.ind.set_menu(self.menu)
 | 
			
		||||
 | 
			
		||||
    # load host/router vars
 | 
			
		||||
    self.host = default_host
 | 
			
		||||
 | 
			
		||||
    # set router / gateway dynamically
 | 
			
		||||
    self.router = self.get_default_gateway_linux()
 | 
			
		||||
    if self.router == None:
 | 
			
		||||
      self.router = default_router
 | 
			
		||||
    print "Set router target to "+str(self.router)
 | 
			
		||||
    self.routerLastUpdated = datetime.datetime.now()
 | 
			
		||||
 | 
			
		||||
    # start the ping process
 | 
			
		||||
    self.counter = 0
 | 
			
		||||
    self.timeout = ping_frequency
 | 
			
		||||
    self.ping_both()
 | 
			
		||||
 | 
			
		||||
    # Print started message
 | 
			
		||||
    print "Started."
 | 
			
		||||
 | 
			
		||||
    # Begin runtime loop
 | 
			
		||||
    Gtk.main()
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Runtime
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
  pinger = Pinger()
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user