Add hostname resolution and fix UTF-8 encoding issues
This commit is contained in:
@@ -6,6 +6,7 @@ Helps discover devices on the network and manage MAC-based internet blocking
|
|||||||
|
|
||||||
import requests
|
import requests
|
||||||
import json
|
import json
|
||||||
|
import socket
|
||||||
from typing import List, Dict
|
from typing import List, Dict
|
||||||
from urllib3.exceptions import InsecureRequestWarning
|
from urllib3.exceptions import InsecureRequestWarning
|
||||||
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
|
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
|
||||||
@@ -44,6 +45,15 @@ class OPNsenseDeviceManager:
|
|||||||
print(f"Error making request to {endpoint}: {e}")
|
print(f"Error making request to {endpoint}: {e}")
|
||||||
return None
|
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]:
|
def discover_devices(self) -> List[Dict]:
|
||||||
"""Discover all devices from ARP table"""
|
"""Discover all devices from ARP table"""
|
||||||
result = self._make_request("diagnostics/interface/getArp")
|
result = self._make_request("diagnostics/interface/getArp")
|
||||||
@@ -72,20 +82,30 @@ class OPNsenseDeviceManager:
|
|||||||
|
|
||||||
seen_macs.add(mac)
|
seen_macs.add(mac)
|
||||||
|
|
||||||
|
# Try to get hostname
|
||||||
|
hostname = self._get_hostname(ip)
|
||||||
|
|
||||||
device_info = {
|
device_info = {
|
||||||
'mac': mac,
|
'mac': mac,
|
||||||
'ip': ip,
|
'ip': ip,
|
||||||
|
'hostname': hostname,
|
||||||
'manufacturer': manufacturer,
|
'manufacturer': manufacturer,
|
||||||
'intf_description': entry.get('intf_description', 'LAN'),
|
'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)
|
devices.append(device_info)
|
||||||
|
|
||||||
return sorted(devices, key=lambda x: x['ip'])
|
return sorted(devices, key=lambda x: x['ip'])
|
||||||
|
|
||||||
def _suggest_device_name(self, manufacturer: str, ip: str) -> str:
|
def _suggest_device_name(self, manufacturer: str, ip: str, hostname: str = "") -> str:
|
||||||
"""Suggest a device name based on manufacturer"""
|
"""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()
|
manufacturer = manufacturer.lower()
|
||||||
|
|
||||||
if 'apple' in manufacturer:
|
if 'apple' in manufacturer:
|
||||||
@@ -215,6 +235,8 @@ class OPNsenseDeviceManager:
|
|||||||
for device in device_list:
|
for device in device_list:
|
||||||
report += f" MAC: {device['mac']}\n"
|
report += f" MAC: {device['mac']}\n"
|
||||||
report += f" IP: {device['ip']}\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 += f" Suggested Name: {device['suggested_name']}\n"
|
||||||
report += "\n"
|
report += "\n"
|
||||||
|
|
||||||
@@ -225,14 +247,16 @@ class OPNsenseDeviceManager:
|
|||||||
"""Export devices in a format easy to copy into Home Assistant"""
|
"""Export devices in a format easy to copy into Home Assistant"""
|
||||||
devices = self.discover_devices()
|
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("# 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:
|
for device in devices:
|
||||||
|
hostname = device['hostname'] if device['hostname'] else "N/A"
|
||||||
f.write(f"{device['suggested_name']:<40} | "
|
f.write(f"{device['suggested_name']:<40} | "
|
||||||
f"{device['mac']:<20} | "
|
f"{device['mac']:<20} | "
|
||||||
f"{device['ip']:<15} | "
|
f"{device['ip']:<15} | "
|
||||||
|
f"{hostname:<30} | "
|
||||||
f"{device['manufacturer']}\n")
|
f"{device['manufacturer']}\n")
|
||||||
|
|
||||||
print(f"Device list exported to: {output_file}")
|
print(f"Device list exported to: {output_file}")
|
||||||
@@ -272,6 +296,8 @@ def main():
|
|||||||
print(f"{idx}. {device['suggested_name']}")
|
print(f"{idx}. {device['suggested_name']}")
|
||||||
print(f" MAC: {device['mac']}")
|
print(f" MAC: {device['mac']}")
|
||||||
print(f" IP: {device['ip']}")
|
print(f" IP: {device['ip']}")
|
||||||
|
if device['hostname']:
|
||||||
|
print(f" Hostname: {device['hostname']}")
|
||||||
print(f" Manufacturer: {device['manufacturer']}")
|
print(f" Manufacturer: {device['manufacturer']}")
|
||||||
print()
|
print()
|
||||||
|
|
||||||
@@ -281,7 +307,7 @@ def main():
|
|||||||
|
|
||||||
save = input("Save report to file? (y/n): ").strip().lower()
|
save = input("Save report to file? (y/n): ").strip().lower()
|
||||||
if save == 'y':
|
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)
|
f.write(report)
|
||||||
print("Report saved to: device_report.txt")
|
print("Report saved to: device_report.txt")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user