Fix loglevel & discovery-subnet

This commit is contained in:
Артём Кокос
2026-02-24 21:21:20 +07:00
parent fd0b9d4d81
commit 450fd54b80
2 changed files with 54 additions and 41 deletions

View File

@@ -2,7 +2,7 @@ import asyncio
import json
import socket
import logging
import subprocess
import os
import ipaddress
from typing import List, Dict
@@ -14,21 +14,33 @@ class DiscoveryService:
self.port = port
self.discover_msg = {"method": "getPilot", "params": {}}
def _get_local_subnet(self) -> str:
def _get_target_subnets(self) -> List[str]:
"""
Определяет список подсетей для сканирования.
Приоритет:
1. Переменная окружения SCAN_NETWORK (можно через запятую: "192.168.0.0/24,192.168.1.0/24")
2. Автоопределение по дефолтному шлюзу
"""
env_network = os.getenv("SCAN_NETWORK")
if env_network:
return [s.strip() for s in env_network.split(",")]
# Автоопределение (твой старый метод)
try:
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
# Коннект не создает трафика, но заставляет ОС выбрать нужный интерфейс
s.connect(("8.8.8.8", 80))
local_ip = s.getsockname()[0]
network = ipaddress.IPv4Network(f"{local_ip}/24", strict=False)
return str(network)
network = ipaddress.IPv4Network(f"{local_ip}/24", strict=False)
return [str(network)]
except Exception as e:
logger.error(f"Linux Discovery Error: Не удалось определить подсеть: {e}")
return "192.168.1.0/24"
logger.error(
f"Discovery Error: Не удалось определить подсеть автоматически: {e}"
)
return ["192.168.1.0/24"]
async def scan_network(self, timeout: float = 1.5) -> List[Dict]:
subnet = self._get_local_subnet()
network = ipaddress.IPv4Network(subnet)
async def scan_network(self, timeout: float = 2.0) -> List[Dict]:
subnets = self._get_target_subnets()
found_devices = []
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
@@ -37,55 +49,56 @@ class DiscoveryService:
loop = asyncio.get_running_loop()
message = json.dumps(self.discover_msg).encode()
logger.info(f"🚀 Начинаю сканирование сети {subnet}...")
logger.debug(f"🚀 Начинаю сканирование сетей: {', '.join(subnets)}...")
# Рассылаем запросы
for ip in network.hosts():
# Рассылаем запросы по всем целевым сетям
for subnet in subnets:
try:
sock.sendto(message, (str(ip), self.port))
except Exception:
continue
network = ipaddress.IPv4Network(subnet)
for ip in network.hosts():
try:
sock.sendto(message, (str(ip), self.port))
except Exception:
continue
except ValueError as e:
logger.error(f"❌ Неверный формат подсети {subnet}: {e}")
# Собираем ответы
start_time = loop.time()
while (loop.time() - start_time) < timeout:
try:
# Используем небольшой таймаут на чтение, чтобы успевать выходить из цикла
data, addr = await asyncio.wait_for(
loop.run_in_executor(None, sock.recvfrom, 1024), timeout=0.1
loop.run_in_executor(None, sock.recvfrom, 1024), timeout=0.2
)
resp = json.loads(data.decode())
if "result" in resp:
res = resp["result"]
mac = res.get("mac")
ip_from_socket = addr[0]
if mac:
device_info = {
"mac": mac,
"ip": ip_from_socket,
"state": {
"on": res.get("state"),
"dimming": res.get("dimming"),
"temp": res.get("temp"),
},
}
found_devices.append(device_info)
# Красивый лог с IP и MAC
logger.info(
f" [+] Найдена лампа: {ip_from_socket} | MAC: {mac}"
found_devices.append(
{
"mac": mac,
"ip": addr[0],
"state": {
"on": res.get("state"),
"dimming": res.get("dimming"),
"temp": res.get("temp"),
},
}
)
logger.info(f" [+] Найдена лампа: {addr[0]} | MAC: {mac}")
except (asyncio.TimeoutError, json.JSONDecodeError):
continue
except Exception as e:
# На Linux Errno 11 (EAGAIN) тут может выскочить, если буфер пуст
except Exception:
await asyncio.sleep(0.01)
continue
sock.close()
# Фильтруем дубликаты (если лампа ответила несколько раз)
unique_devices = list({d["mac"]: d for d in found_devices}.values())
return unique_devices
# Фильтруем дубликаты
return list({d["mac"]: d for d in found_devices}.values())
async def start_background_discovery(self, state_manager, interval=600):
"""Запускает бесконечный цикл сканирования."""
@@ -98,5 +111,5 @@ class DiscoveryService:
f"📡 Discovery: онлайн {len(state_manager.devices)} устройств"
)
except Exception as e:
print(f"❌ Discovery error: {e}")
logger.error(f"❌ Discovery background error: {e}")
await asyncio.sleep(interval)

View File

@@ -1,5 +1,6 @@
import logging
import asyncio
import os
from contextlib import asynccontextmanager
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
@@ -11,10 +12,9 @@ from sqlalchemy import select
from app.models.device import GroupModel
from app.api.routes import devices, control, schedules
LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO").upper()
logging.basicConfig(
level=logging.INFO, format="%(asctime)s | %(levelname)s | %(message)s"
)
logging.basicConfig(level=LOG_LEVEL, format="%(asctime)s | %(levelname)s | %(message)s")
logger = logging.getLogger(__name__)