commit
a337002501
5 changed files with 236854 additions and 0 deletions
@ -0,0 +1,2 @@ |
|||
haSensors.py |
|||
__pycache__ |
|||
@ -0,0 +1,33 @@ |
|||
class Colors: |
|||
""" ANSI color codes """ |
|||
BLACK = "\033[0;30m" |
|||
RED = "\033[0;31m" |
|||
GREEN = "\033[0;32m" |
|||
BROWN = "\033[0;33m" |
|||
BLUE = "\033[0;34m" |
|||
PURPLE = "\033[0;35m" |
|||
CYAN = "\033[0;36m" |
|||
LIGHT_GRAY = "\033[0;37m" |
|||
DARK_GRAY = "\033[1;30m" |
|||
LIGHT_RED = "\033[1;31m" |
|||
LIGHT_GREEN = "\033[1;32m" |
|||
YELLOW = "\033[1;33m" |
|||
LIGHT_BLUE = "\033[1;34m" |
|||
LIGHT_PURPLE = "\033[1;35m" |
|||
LIGHT_CYAN = "\033[1;36m" |
|||
LIGHT_WHITE = "\033[1;37m" |
|||
BOLD = "\033[1m" |
|||
FAINT = "\033[2m" |
|||
ITALIC = "\033[3m" |
|||
UNDERLINE = "\033[4m" |
|||
BLINK = "\033[5m" |
|||
NEGATIVE = "\033[7m" |
|||
CROSSED = "\033[9m" |
|||
END = "\033[0m" |
|||
|
|||
def getColorsDict(): |
|||
d = dict(vars(Colors)) |
|||
for k in list(d.keys()): |
|||
if k in ['__module__', '__firstlineno__', '__doc__', '__annotations__', '__weakref__', '__dict__', '__static_attributes__']: |
|||
del d[k] |
|||
return d |
|||
@ -0,0 +1,172 @@ |
|||
import os |
|||
import time |
|||
from datetime import datetime |
|||
import datetime as datetimesrc |
|||
import psutil |
|||
import subprocess |
|||
from colors import Colors, getColorsDict |
|||
|
|||
def pretty_mac(_mac): |
|||
try: |
|||
mac_split = _mac.split(":") |
|||
mac = "" |
|||
for ch in mac_split: |
|||
if len(ch) == 2: |
|||
mac += f"{ch}:" |
|||
if len(ch) == 1: |
|||
mac += f"0{ch}:" |
|||
|
|||
mac = mac[:-1] |
|||
return mac |
|||
except: |
|||
return _mac |
|||
|
|||
class MacLookup: |
|||
prefixes = {} |
|||
#https://github.com/bauerj/mac_vendor_lookup/blob/master/mac_vendor_lookup.py |
|||
def __init__(self): |
|||
self.load_vendors() |
|||
pass |
|||
|
|||
@staticmethod |
|||
def sanitise(_mac): |
|||
mac = _mac.replace(":", "").replace("-", "").replace(".", "").upper() |
|||
|
|||
try: |
|||
int(mac, 16) |
|||
except ValueError: |
|||
raise Exception("{} contains unexpected character".format(_mac)) |
|||
if len(mac) > 12: |
|||
raise Exception("{} is not a valid MAC address (too long)".format(_mac)) |
|||
return mac |
|||
|
|||
def lookup(self, mac): |
|||
try: |
|||
mac = self.sanitise(mac) |
|||
except: |
|||
return f"Unknown MAC ({mac})" |
|||
if not self.prefixes: |
|||
self.load_vendors() |
|||
if type(mac) == str: |
|||
mac = mac.encode("utf8") |
|||
try: |
|||
return self.prefixes[mac[:6]].decode("utf8") |
|||
except KeyError: |
|||
return f"Unknown MAC ({mac})" |
|||
|
|||
def load_vendors(self): |
|||
from pathlib import Path |
|||
script_dir = Path(__file__).parent.resolve() |
|||
with open(os.path.join(script_dir, "oui.txt"), "rb") as macs: |
|||
for line in macs.readlines(): |
|||
if not line: |
|||
break |
|||
if b"(base 16)" in line: |
|||
prefix, vendor = (i.strip() for i in line.split(b"(base 16)", 1)) |
|||
self.prefixes[prefix] = vendor |
|||
|
|||
global maclookup |
|||
maclookup = MacLookup() |
|||
|
|||
global firstMacLookup |
|||
firstMacLookup = {} |
|||
|
|||
def getAvg(): |
|||
data = os.getloadavg() |
|||
return '{0:.2f} {1:.2f} {2:.2f}'.format(*data) |
|||
|
|||
def getDateTime(): |
|||
return datetime.now().strftime("%Y-%m-%d %H:%M:%S") |
|||
|
|||
def getCpuPercent(colors = True): |
|||
if colors: |
|||
u = psutil.cpu_percent(interval=1) |
|||
color = "" |
|||
if u > 0: |
|||
color = Colors.GREEN |
|||
if u > 33: |
|||
color = Colors.YELLOW |
|||
if u > 66: |
|||
color = Colors.RED |
|||
if u > 99: |
|||
u = 99 |
|||
return "{0}{1:3}%{2}".format(color, u, Colors.END) |
|||
else: |
|||
return "{0:3}%".format(psutil.cpu_percent(interval=1)) |
|||
|
|||
def getMemPercent(colors = True): |
|||
if colors: |
|||
u = psutil.virtual_memory().percent |
|||
color = "" |
|||
if u > 0: |
|||
color = Colors.GREEN |
|||
if u > 33: |
|||
color = Colors.YELLOW |
|||
if u > 66: |
|||
color = Colors.RED |
|||
if u > 99: |
|||
u = 99 |
|||
return "{0}{1:3}%{2}".format(color, u, Colors.END) |
|||
else: |
|||
return "{0:3}%".format(psutil.virtual_memory().percent) |
|||
|
|||
def getRowSeparator(): |
|||
return Colors.NEGATIVE + "".join([" " for i in range(0, 60)]) + Colors.END |
|||
|
|||
def getArp(): |
|||
global maclookup |
|||
if maclookup is None: |
|||
maclookup = MacLookup() |
|||
|
|||
global firstMacLookup |
|||
#linux | ? (192.168.1.136) at a8:7e:ea:64:85:de [ether] on eth0 |
|||
#mac | mdns.mcast.net (224.0.0.251) at 1:0:5e:0:0:fb on en0 ifscope permanent [ethernet] |
|||
arp_res = subprocess.check_output("arp -a", shell=True, text=True) |
|||
result = [] |
|||
|
|||
found_macs = [] |
|||
for line in arp_res.split("\n"): |
|||
if line: |
|||
host, ip, at, mac, *other = line.split(" ") |
|||
mac = pretty_mac(mac) |
|||
|
|||
if mac not in firstMacLookup: |
|||
firstMacLookup[mac] = int(time.time()) |
|||
|
|||
found_macs.append(mac) |
|||
delta = int(time.time()) - firstMacLookup[mac] |
|||
result.append({ |
|||
"host": host, |
|||
"ip": ip[1:-1], |
|||
"mac": mac, |
|||
"mac_name": maclookup.lookup(mac), |
|||
"delta": delta, |
|||
"mac_first_lookup_utime": str(datetimesrc.timedelta(seconds=delta)) |
|||
}) |
|||
|
|||
unused_macs = [mac for mac in list(firstMacLookup.keys()) if mac not in found_macs] |
|||
for mac in unused_macs: |
|||
del firstMacLookup[mac] |
|||
|
|||
txt = "" |
|||
exists = [] |
|||
for host in result: |
|||
host.update(getColorsDict()) |
|||
if host["ip"] not in exists: |
|||
exists.append(host["ip"]) |
|||
host["time_color"] = Colors.RED |
|||
|
|||
if host["delta"] > 60 * 60: |
|||
host["time_color"] = Colors.YELLOW |
|||
|
|||
if host["delta"] > 60 * 60 * 12: |
|||
host["time_color"] = Colors.GREEN |
|||
|
|||
mac_name_len = len(host["mac_name"]) |
|||
host["mac_name"] = host["mac_name"][:31] + ("..." if mac_name_len > 31 else "") |
|||
|
|||
txt += "{time_color}{mac_first_lookup_utime}{END} - {BLUE}{ip}{END} - {mac_name}\n".format(**host) |
|||
return txt[:-1] |
|||
|
|||
if __name__ == "__main__": |
|||
print(getArp()) |
|||
File diff suppressed because it is too large
@ -0,0 +1,91 @@ |
|||
import argparse |
|||
import os |
|||
import time |
|||
import traceback |
|||
|
|||
from fetchers import * |
|||
from colors import getColorsDict |
|||
|
|||
clear = lambda: os.system('clear') |
|||
|
|||
results = { |
|||
"row_separator": getRowSeparator(), |
|||
"ha_sensors": "" |
|||
} |
|||
logging = False |
|||
results.update(getColorsDict()) |
|||
|
|||
def printColors(): |
|||
res = "" |
|||
for k,v in getColorsDict().items(): |
|||
res += f"{v}{k}" |
|||
print(res) |
|||
|
|||
def createDaemon(func, key, sleep = 60): |
|||
from threading import Thread |
|||
results[key] = "" |
|||
def resTo(): |
|||
while True: |
|||
try: |
|||
results[key] = func() |
|||
if logging: |
|||
print(f'[{time.time()}] Save result to {key}') |
|||
except: |
|||
traceback.print_exc() |
|||
finally: |
|||
time.sleep(sleep) |
|||
|
|||
t = Thread(target=resTo, name=f"daemon-{key}") |
|||
t.daemon = True |
|||
t.start() |
|||
|
|||
class Service: |
|||
def __init__(self, args): |
|||
self.args = args |
|||
self.startFetcher() |
|||
self.looper() |
|||
|
|||
def startFetcher(self): |
|||
createDaemon(getAvg, "sys_avg", 1) |
|||
createDaemon(getDateTime, "date_time", 1) |
|||
createDaemon(getCpuPercent, "cpu_percent", 1) |
|||
createDaemon(getMemPercent, "mem_percent", 1) |
|||
createDaemon(getArp, "arp_result", 60) |
|||
try: |
|||
from haSensors import getHaSensors |
|||
createDaemon(getHaSensors, "ha_sensors", 60) |
|||
except: |
|||
pass |
|||
|
|||
def printer(): |
|||
self.printer() |
|||
createDaemon(printer, "printer", 1) |
|||
|
|||
def looper(self): |
|||
try: |
|||
while True: |
|||
time.sleep(1) |
|||
except: |
|||
exit(0) |
|||
|
|||
def printer(self): |
|||
out = ''' |
|||
{YELLOW}{date_time:^19}{END} | {CYAN}{sys_avg:^14}{END} | CPU:{cpu_percent:^5} | MEM:{mem_percent:^5} |
|||
{row_separator} |
|||
{ha_sensors} |
|||
{row_separator} |
|||
{arp_result} |
|||
'''.format(**results)[1:-1] |
|||
format_cr = "" |
|||
for row in out.split("\n")[:self.args.rows]: |
|||
format_cr += f"{row}\n"#[:self.args.cols] |
|||
clear() |
|||
print(format_cr, end="") |
|||
|
|||
if __name__ == "__main__": |
|||
parser = argparse.ArgumentParser() |
|||
parser.add_argument("--cols", default=60) |
|||
parser.add_argument("--rows", default=40) |
|||
args = parser.parse_args() |
|||
Service(args) |
|||
#printColors() |
|||
Loading…
Reference in new issue