#!/usr/bin/python ### This program is free software; you can redistribute it and/or modify ### it under the terms of the GNU Library General Public License as published by ### the Free Software Foundation; version 2 only ### ### 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 Library General Public License for more details. ### ### You should have received a copy of the GNU Library General Public License ### along with this program; if not, write to the Free Software ### Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ### Copyright 2004, 2005, 2006 Dag Wieers import sys, signal, re, time, string from pythonwifi import iwlibs from rhpl import ethtool VERSION = '0.2svn' ### Scan delay in milliseconds #delay = 30 #delay = 200 delay = 500 ansi = { 'default': '\033[0;0m', 'black': '\033[0;30m', 'darkred': '\033[0;31m', 'darkgreen': '\033[0;32m', 'darkyellow': '\033[0;33m', 'darkblue': '\033[0;34m', 'darkmagenta': '\033[0;35m', 'darkcyan': '\033[0;36m', 'silver': '\033[0;37m', 'gray': '\033[1;30m', 'red': '\033[1;31m', 'green': '\033[1;32m', 'yellow': '\033[1;33m', 'blue': '\033[1;34m', 'magenta': '\033[1;35m', 'cyan': '\033[1;36m', 'white': '\033[1;37m', 'blackbg': '\033[40m', 'redbg': '\033[41m', 'greenbg': '\033[42m', 'yellowbg': '\033[43m', 'bluebg': '\033[44m', 'magentabg': '\033[45m', 'cyanbg': '\033[46m', 'whitebg': '\033[47m', 'reset': '\033[0;0m', 'bold': '\033[1m', 'reverse': '\033[2m', 'underline': '\033[4m', 'home': '\033[H', 'up': '\033[1A', 'down': '\033[1B', 'right': '\033[1C', 'left': '\033[1D', 'clear': '\033[2J', # 'cleareol': '\033[K', 'cleareol': '\033[0K', 'clearline': '\033[2K', 'cleareos': '\033[J', # 'save': '\033[s', # 'restore': '\033[u', 'save': '\0337', 'restore': '\0338', } a = ''; b = '' for i in range(0, 255): a = a + chr(i) if 32 < i < 127: b = b + chr(i) else: b = b + '.' asciitrans = string.maketrans(a, b) def asciify(str): return str.translate(asciitrans) def sortfunc(a, b): if a['active'] and b['active']: return cmp(b['avgquality15'], a['avgquality15']) elif not a['active'] and b['active']: return 1 elif a['active'] and not b['active']: return -1 else: return cmp(b['timestamp'], a['timestamp']) def sorted(aps): list = [] for key in aps.keys(): list.append(aps[key]) list.sort(sortfunc) return list def timestr(timestamp): interval = time.time() * 1000.0 - timestamp if interval / 1000 / 15 < 1: return ' %5dms ' % interval elif interval / 1000 / 60 < 1: return ' %6ds ' % (interval / 1000) elif interval / 1000 / 60 / 60 < 1: return ' %2dm %02ds ' % (interval / 1000 / 60, interval / 1000 % 60) elif interval / 1000 / 60 / 60 / 24 < 1: return ' %2dh %02dm ' % (interval / 1000 / 60 / 60, interval / 1000 / 60 % 60) else: return '%3dd %02dh ' % (interval / 1000 / 60 / 60 / 24, interval / 1000 / 60 / 60 % 24) def strength(snr): ecol = ansi['white'] signalname = 'Excellent' if 20 < snr <= 25: ecol = ansi['green'] signalname = 'Very Good' elif 15 < snr <= 20: ecol = ansi['darkgreen'] signalname = 'Good' elif 10 < snr <= 15: ecol = ansi['yellow'] signalname = 'Low' elif 8 < snr <= 10: ecol = ansi['darkyellow'] signalname = 'Very Low' elif 6 < snr <= 8: ecol = ansi['red'] signalname = 'Very Low' elif 0 < snr <= 6: ecol = ansi['darkred'] signalname = 'Ultra Low' elif snr == 0: ecol = ansi['silver'] signalname = 'No Signal' elif snr < 0: ecol = ansi['gray'] signalname = 'Not Assoc' return ecol + signalname def interface(iface): try: ip = ethtool.get_ipaddr(iface) # netmask = ethtool.get_netmask(iface) except Exception: ip = '' try: module = ethtool.get_module(iface) except Exception: module = '' return ip, module if not iwlibs.getNICnames(): print "No wireless interfaces found on the system." sys.exit(1) print ansi['clear'] aps = {} assaps = {} try: while True: ### Collect Access Point information and Association information for iface in iwlibs.getNICnames(): ifobj = iwlibs.Wireless(iface) ### Create Association mapping stats, quality, discard, missed_beacon = ifobj.getStatistics() snr = quality.signallevel - quality.noiselevel if snr < 0: snr = 0 ### Create average if not assaps.has_key(iface) or snr == 0: if snr == 0: ticks = 0.0 else: ticks = 1.0 avgsnr30 = snr avgsnr15 = snr avgsnr = snr else: ticks = assaps[iface]['ticks'] + 1.0 avgsnr = (assaps[iface]['avgsnr'] * assaps[iface]['ticks'] + snr) / ticks if ticks * delay >= 30 * 1000.0: ticks30 = (30 * 1000.0 / delay) - 1 avgsnr30 = (assaps[iface]['avgsnr30'] * ticks30 + snr) / (ticks30 + 1) else: avgsnr30 = avgsnr if ticks * delay >= 15 * 1000.0: ticks15 = (15 * 1000.0 / delay) - 1 avgsnr15 = (assaps[iface]['avgsnr15'] * ticks15 + snr) / (ticks15 + 1) else: avgsnr15 = avgsnr # print ticks, avgsnr, avgsnr15, avgsnr30 # print 'avg15: %4.2f' % avgsnr15 # print 'avg30: %4.2f' % avgsnr30 # print 'avg: %4.2f' % avgsnr # print ifobj.getFrequency() ip, module = interface(iface) assaps[iface] = { 'avgsnr': avgsnr, 'avgsnr15': avgsnr15, 'avgsnr30': avgsnr30, 'bitrate': ifobj.getBitrate(), 'bssid': ifobj.getAPaddr(), # 'channel': ifobj.getChannel(ifobj.getFrequency(), d), 'discard': discard, 'essid': ifobj.getEssid(), # 'frequency': ifobj.getFrequency(), 'iface': iface, 'ip': ip, 'module': module, 'missed_beacon': missed_beacon, 'noise': quality.noiselevel, 'stats': stats, 'signal': quality.signallevel, 'snr': snr, 'strength': strength(avgsnr15), 'ticks': ticks, } ### Create Access Point mapping scanresults = [] try: scanresults = ifobj.scan() except: pass for result in scanresults: bssid = result.bssid if not bssid: print 'Error...' continue try: beacon = int(re.match('.*Last beacon: ([0-9]+)ms ago', result.custom[1]).group(1)) except: try: beacon = int(re.match('.*Last beacon: ([0-9]+)ms ago', result.custom[0]).group(1)) except: try: beacon = int(re.match('.*bcn_int=([0-9]+)', result.custom[0]).group(1)) except: beacon = 100 if not aps.has_key(iface+bssid): ticks = 1.0 avgquality = result.quality.quality avgquality15 = result.quality.quality else: ticks = aps[iface+bssid]['ticks'] + 1.0 avgquality = (aps[iface+bssid]['avgquality'] * ticks + result.quality.quality ) / (ticks + 1) if ticks * delay >= 15 * 1000.0: ticks15 = (15 * 1000.0 / delay) - 1 avgquality15 = ( aps[iface+bssid]['avgquality15'] * ticks15 + result.quality.quality ) / (ticks15 + 1) else: avgquality15 = avgquality # print dir(result.frequency) # print result.frequency.getBitrate() # print result.frequency.getChannel(result.frequency.getFrequency(), result.rate) # print result.frequency.getFrequency() # print # print '----' # print result.frequency.getFrequency() # print result.range.frequencies # print dir(result.range.frequencies) aps[iface+bssid] = { 'active': True, 'avgquality': avgquality, 'avgquality15': avgquality15, 'beacon': beacon, 'bssid': result.bssid, 'channel': result.frequency.getChannel(result.frequency.getFrequency(), result.range), # 'frequency': result.frequency.getFrequency(), 'encode': result.encode, 'essid': asciify(result.essid), 'iface': iface, 'maxrate': result.rate[-1], 'noise': result.quality.getNoiselevel(), 'quality': result.quality.quality, 'signal': result.quality.getSignallevel(), 'ticks': ticks, 'timestamp': time.time() * 1000.0 - beacon, } ### Display Association information print ansi['home'] + ansi['blue'] + ansi['underline'] + ' %(iface)-5s %(module)-10s %(essid)-20s %(ip)-15s %(signal)4s %(noise)4s %(bitrate)8s %(missed_beacon)3s %(snr)3s %(avgsnr)4s %(avgsnr15)4s %(signalname)-10s' % { 'iface': 'Iface', 'module': 'Module', 'essid': 'ESSID/Name', 'ip': 'IP address', 'signal': 'Sgnl', 'noise': 'Nois', 'snr': 'SNR', 'avgsnr': 'Avg', 'avgsnr15': 'Av15', 'bitrate': 'Cur rate', 'missed_beacon': 'Mis', 'signalname': 'Strength' } + ansi['cleareol'] # print ansi['blue'] + "%-5s %-20s %4s %4s %3s %8s %3s %-10s" % ('Iface', 'ESSID/Name', 'Sgnl', 'Nois', 'SNR', 'Cur rate', 'Mis', 'Strength') + ansi['cleareol'] ifaces = assaps.keys() ifaces.sort() for iface in ifaces: obj = assaps[iface] color = 'default' if obj['bssid'] != '00:00:00:00:00:00' and obj['ip']: color = 'green' elif obj['bssid'] != '00:00:00:00:00:00': color = 'green' elif obj['ip']: color = 'red' print ansi['default'] + (' %(iface)-5s %(module)-10s %(essid)-20s %(ip)-15s %(signal)4s %(noise)4s %(bitrate)8s %(missed_beacon)3s %(snr)3s %(avgsnr)4.1f %(avgsnr15)4.1f %(strength)-10s' % obj) + ansi['default'] + ansi['cleareol'] print ansi['clearline'] ### Display Access Point information print ansi['blue'] + ansi['underline'] + " %-22s %2s %-17s %-5s %-8s %8s %3s %4s %4s " % ('ESSID/Name', 'Ch', 'BSSID/HW address', 'Iface', 'Max rate', 'Last bcn', 'Qua', 'Avg', 'Av15') + ansi['cleareol'] for obj in sorted(aps): if obj['bssid'] == assaps[obj['iface']]['bssid']: symbol = '+' if obj['active']: color = 'white' else: color = 'silver' else: if obj['active']: color = 'default' symbol = ' ' else: color = 'gray' symbol = '!' if (map(lambda x: hex(ord(x)), obj['encode']) == ['0x0','0x0','0x0','0x8']): if obj['active']: ecolor = 'red' else: ecolor = 'darkred' else: if obj['active']: ecolor = 'green' else: ecolor = 'darkgreen' beacon = timestr(obj['timestamp']) print ansi['default'] + ansi[color] + symbol + ansi[ecolor] + ('%(essid)-22s ' % obj) + ansi[color] + (' %(channel)2s %(bssid)-17s %(iface)-5s %(maxrate)8s ' % obj) + beacon + (' %(quality)3s %(avgquality)4.1f %(avgquality15)4.1f' % obj) + ansi['silver'] + ansi['cleareol'] # if obj['bssid'] == ifobj.getAPaddr(): # print '%4s %4s %3s %8s %3s' % (qual.signallevel, qual.noiselevel, qual.signallevel - qual.noiselevel, ifobj.getBitrate(), missed_beacon), # else: # print ansi['gray'] + '%4s %4s %3s %8s %3s' % (obj['signal'], obj['noise'], int(obj['signal']) - int(obj['noise']), '-', '-'), active = len([x for x in aps.keys() if aps[x]['active']]) print ansi['clearline'] print ansi['clearline'] + ansi['default'] + ansi['blue'] + ' %s access points (%s active and %s inactive)' % (len(aps), active, len(aps) - active) + ansi['cleareos'] for key in aps.keys(): aps[key]['active'] = False time.sleep(delay / 1000.0) except KeyboardInterrupt, e: print ansi['default'] except RuntimeError, e: print ansi['default'], e sys.exit(1) # vim:ts=4:sw=4:et