1
0
mirror of https://github.com/esphome/esphome.git synced 2025-03-13 14:18:14 +00:00

Merge a1ba64ad9bcfe52b0ab19df4a43eef9219f69690 into 755b0bbfc7d404d9ce1a4a02ee8c99c4c838e63f

This commit is contained in:
Mischa Siekmann 2025-02-23 17:42:17 +01:00 committed by GitHub
commit 14d7590ebb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 63 additions and 3 deletions

View File

@ -50,6 +50,7 @@ from esphome.util import (
run_external_process,
safe_print,
)
from esphome.zeroconf import get_mac_suffix_nodes
_LOGGER = logging.getLogger(__name__)
@ -95,9 +96,13 @@ def choose_upload_log_host(
if default == "SERIAL":
return choose_prompt(options, purpose=purpose)
if (show_ota and "ota" in CORE.config) or (show_api and "api" in CORE.config):
options.append((f"Over The Air ({CORE.address})", CORE.address))
if default == "OTA":
return CORE.address
if CORE.config["esphome"]["name_add_mac_suffix"]:
for addr in get_mac_suffix_nodes(CORE.config["esphome"]["name"]):
options.append((f"Over The Air ({addr})", addr))
else:
options.append((f"Over The Air ({CORE.address})", CORE.address))
if default == "OTA":
return CORE.address
if (
show_mqtt
and (mqtt_config := CORE.config.get(CONF_MQTT))

View File

@ -3,6 +3,7 @@ from __future__ import annotations
import asyncio
from dataclasses import dataclass
import logging
import time
from typing import Callable
from zeroconf import IPVersion, ServiceInfo, ServiceStateChange, Zeroconf
@ -199,3 +200,57 @@ class AsyncEsphomeZeroconf(AsyncZeroconf):
) and (addresses := info.parsed_scoped_addresses(IPVersion.All)):
return addresses
return None
NODE_SCAN_TIME_SEC = 2
class NodeScanner:
def __init__(self, node_raw_name: str) -> None:
self.node_raw_name = node_raw_name
self.aiobrowser: AsyncServiceBrowser | None = None
self.aiozc: AsyncZeroconf | None = None
self.found_nodes = []
def async_on_service_state_change(
self,
zeroconf: Zeroconf,
service_type: str,
name: str,
state_change: ServiceStateChange,
) -> None:
if state_change is not ServiceStateChange.Added:
return
if name.startswith(self.node_raw_name):
self.found_nodes.append(name.split(".")[0] + ".local")
async def async_run(self) -> None:
self.aiozc = AsyncZeroconf(ip_version=IPVersion.V4Only)
services = ["_esphomelib._tcp.local."]
self.aiobrowser = AsyncServiceBrowser(
self.aiozc.zeroconf, services, handlers=[self.async_on_service_state_change]
)
start_time = time.time()
while time.time() - start_time < NODE_SCAN_TIME_SEC:
await asyncio.sleep(1)
await self.async_close()
async def async_close(self) -> None:
assert self.aiozc is not None
assert self.aiobrowser is not None
await self.aiobrowser.async_cancel()
await self.aiozc.async_close()
def get_mac_suffix_nodes(node_name: str) -> list[str]:
loop = asyncio.get_event_loop()
runner = NodeScanner(node_name)
try:
loop.run_until_complete(runner.async_run())
except KeyboardInterrupt:
loop.run_until_complete(runner.async_close())
return sorted(runner.found_nodes)