Created
January 25, 2026 21:07
-
-
Save ShrootBuck/8402438051c6242bf08d8146af74041c to your computer and use it in GitHub Desktop.
macOS daemon that logs home Wi-Fi bandwidth usage to Postgres. Built to prove to my dad I'm not the one killing our 1.2TB data cap.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import json | |
| import os | |
| import subprocess | |
| import time | |
| import psycopg2 | |
| TARGET_ROUTER_MAC = "" | |
| STATE_FILE = "/Users/zayd/wifi_tracker/state.json" | |
| DB_CONFIG = "" | |
| def get_router_mac(): | |
| try: | |
| gateway_ip = ( | |
| subprocess.check_output( | |
| "route -n get default | grep gateway | awk '{print $2}'", shell=True | |
| ) | |
| .decode("utf-8") | |
| .strip() | |
| ) | |
| arp_out = subprocess.check_output(f"arp {gateway_ip}", shell=True).decode( | |
| "utf-8" | |
| ) | |
| parts = arp_out.split() | |
| for i, part in enumerate(parts): | |
| if part == "at": | |
| return parts[i + 1] | |
| except Exception: | |
| return None | |
| return None | |
| def get_bytes(): | |
| try: | |
| res = subprocess.check_output(["netstat", "-b", "-I", "en0"]).decode("utf-8") | |
| lines = res.split("\n") | |
| parts = lines[1].split() | |
| return int(parts[6]), int(parts[9]) | |
| except Exception: | |
| return 0, 0 | |
| def main(): | |
| current_mac = get_router_mac() | |
| curr_rx, curr_tx = get_bytes() | |
| prev_state = {} | |
| if os.path.exists(STATE_FILE): | |
| with open(STATE_FILE, "r") as f: | |
| prev_state = json.load(f) | |
| # We only log if we were on the target router LAST time and THIS time | |
| # This ensures the delta happened entirely on the target network | |
| if ( | |
| prev_state.get("router_mac") == TARGET_ROUTER_MAC | |
| and current_mac == TARGET_ROUTER_MAC | |
| ): | |
| delta_rx = curr_rx - prev_state.get("rx", 0) | |
| delta_tx = curr_tx - prev_state.get("tx", 0) | |
| # Check for reboot/counter rollover (if current < prev, something reset) | |
| if delta_rx >= 0 and delta_tx >= 0: | |
| # CONNECT AND LOG | |
| try: | |
| conn = psycopg2.connect(DB_CONFIG) | |
| cur = conn.cursor() | |
| cur.execute( | |
| """ | |
| INSERT INTO wifi_logs (network_mac_address, rx_bytes_delta, tx_bytes_delta, total_bytes_delta) | |
| VALUES (%s, %s, %s, %s) | |
| """, | |
| (current_mac, delta_rx, delta_tx, delta_rx + delta_tx), | |
| ) | |
| conn.commit() | |
| cur.close() | |
| conn.close() | |
| except Exception as e: | |
| print(f"DB Error: {e}") | |
| # Update state file | |
| new_state = { | |
| "router_mac": current_mac, | |
| "rx": curr_rx, | |
| "tx": curr_tx, | |
| "timestamp": time.time(), | |
| } | |
| with open(STATE_FILE, "w") as f: | |
| json.dump(new_state, f) | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment