diff --git a/opnsense_device_manager.py b/opnsense_device_manager.py index fdf6fa8..54861f3 100644 --- a/opnsense_device_manager.py +++ b/opnsense_device_manager.py @@ -6,6 +6,7 @@ Helps discover devices on the network and manage MAC-based internet blocking import requests import json +import socket from typing import List, Dict from urllib3.exceptions import InsecureRequestWarning requests.packages.urllib3.disable_warnings(InsecureRequestWarning) @@ -44,6 +45,15 @@ class OPNsenseDeviceManager: print(f"Error making request to {endpoint}: {e}") return None + def _get_hostname(self, ip: str) -> str: + """Try to resolve hostname from IP address""" + try: + hostname, _, _ = socket.gethostbyaddr(ip) + return hostname + except (socket.herror, socket.gaierror, socket.timeout): + return "" + + def discover_devices(self) -> List[Dict]: """Discover all devices from ARP table""" result = self._make_request("diagnostics/interface/getArp") @@ -72,20 +82,30 @@ class OPNsenseDeviceManager: seen_macs.add(mac) + # Try to get hostname + hostname = self._get_hostname(ip) + device_info = { 'mac': mac, 'ip': ip, + 'hostname': hostname, 'manufacturer': manufacturer, 'intf_description': entry.get('intf_description', 'LAN'), - 'suggested_name': self._suggest_device_name(manufacturer, ip) + 'suggested_name': self._suggest_device_name(manufacturer, ip, hostname) } devices.append(device_info) return sorted(devices, key=lambda x: x['ip']) - def _suggest_device_name(self, manufacturer: str, ip: str) -> str: - """Suggest a device name based on manufacturer""" + def _suggest_device_name(self, manufacturer: str, ip: str, hostname: str = "") -> str: + """Suggest a device name based on manufacturer and hostname""" + # If we have a hostname, use it + if hostname: + # Clean up hostname (remove domain suffix if present) + clean_hostname = hostname.split('.')[0] + return clean_hostname + manufacturer = manufacturer.lower() if 'apple' in manufacturer: @@ -215,6 +235,8 @@ class OPNsenseDeviceManager: for device in device_list: report += f" MAC: {device['mac']}\n" report += f" IP: {device['ip']}\n" + if device['hostname']: + report += f" Hostname: {device['hostname']}\n" report += f" Suggested Name: {device['suggested_name']}\n" report += "\n" @@ -225,14 +247,16 @@ class OPNsenseDeviceManager: """Export devices in a format easy to copy into Home Assistant""" devices = self.discover_devices() - with open(output_file, 'w') as f: + with open(output_file, 'w', encoding='utf-8') as f: f.write("# Copy these MAC addresses into your Home Assistant input_text entities\n") - f.write("# Format: device_name | MAC Address | IP | Manufacturer\n\n") + f.write("# Format: device_name | MAC Address | IP | Hostname | Manufacturer\n\n") for device in devices: + hostname = device['hostname'] if device['hostname'] else "N/A" f.write(f"{device['suggested_name']:<40} | " f"{device['mac']:<20} | " f"{device['ip']:<15} | " + f"{hostname:<30} | " f"{device['manufacturer']}\n") print(f"Device list exported to: {output_file}") @@ -272,6 +296,8 @@ def main(): print(f"{idx}. {device['suggested_name']}") print(f" MAC: {device['mac']}") print(f" IP: {device['ip']}") + if device['hostname']: + print(f" Hostname: {device['hostname']}") print(f" Manufacturer: {device['manufacturer']}") print() @@ -281,7 +307,7 @@ def main(): save = input("Save report to file? (y/n): ").strip().lower() if save == 'y': - with open('device_report.txt', 'w') as f: + with open('device_report.txt', 'w', encoding='utf-8') as f: f.write(report) print("Report saved to: device_report.txt")