Add zeroconf discovery

This commit is contained in:
Michael Hansen
2025-09-18 10:52:56 -05:00
parent ed16301767
commit 7a4b3e661a
5 changed files with 77 additions and 7 deletions
+8 -6
View File
@@ -18,6 +18,7 @@ from .mpv_player import MpvMediaPlayer
from .openwakeword import OpenWakeWord, OpenWakeWordFeatures
from .satellite import VoiceSatelliteProtocol
from .util import get_mac, is_arm
from .zeroconf import HomeAssistantZeroconf
_LOGGER = logging.getLogger(__name__)
_MODULE_DIR = Path(__file__).parent
@@ -204,6 +205,10 @@ async def main() -> None:
lambda: VoiceSatelliteProtocol(state), host=args.host, port=args.port
)
# Auto discovery (zeroconf, mDNS)
discovery = HomeAssistantZeroconf(port=args.port, name=args.name)
await discovery.register_server()
try:
_LOGGER.debug("Opening audio input device: %s", args.audio_input_device)
with sd.RawInputStream(
@@ -254,7 +259,7 @@ def process_audio(state: ServerState):
if (not wake_words) or (state.wake_words_changed and state.wake_words):
# Update list of wake word models to process
state.wake_words_changed = False
wake_words = list(state.wake_words.values())
wake_words = [ww for ww in state.wake_words.values() if ww.is_active]
has_oww = False
for wake_word in wake_words:
@@ -286,13 +291,10 @@ def process_audio(state: ServerState):
oww_inputs.extend(oww_features.process_streaming(audio_chunk))
for wake_word in wake_words:
if not wake_word.is_active:
continue
activated = False
if isinstance(wake_word, MicroWakeWord):
for oww_input in oww_inputs:
if wake_word.process_streaming(oww_input):
for micro_input in micro_inputs:
if wake_word.process_streaming(micro_input):
activated = True
elif isinstance(wake_word, OpenWakeWord):
for oww_input in oww_inputs:
+3 -1
View File
@@ -176,7 +176,9 @@ class VoiceSatelliteProtocol(APIServer):
)
for ww in self.state.available_wake_words.values()
],
active_wake_words=list(self.state.wake_words.keys()),
active_wake_words=[
ww.id for ww in self.state.wake_words.values() if ww.is_active
],
max_active_wake_words=2,
)
_LOGGER.info("Connected to Home Assistant")
+64
View File
@@ -0,0 +1,64 @@
"""Runs mDNS zeroconf service for Home Assistant discovery."""
import logging
import socket
import uuid
from typing import Optional
_LOGGER = logging.getLogger(__name__)
try:
from zeroconf.asyncio import AsyncServiceInfo, AsyncZeroconf
except ImportError:
_LOGGER.fatal("pip install zeroconf")
raise
MDNS_TARGET_IP = "224.0.0.251"
class HomeAssistantZeroconf:
def __init__(
self, port: int, name: Optional[str] = None, host: Optional[str] = None
) -> None:
self.port = port
self.name = name or _get_mac_address()
if not host:
test_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
test_sock.setblocking(False)
test_sock.connect((MDNS_TARGET_IP, 1))
host = test_sock.getsockname()[0]
_LOGGER.debug("Detected IP: %s", host)
assert host
self.host = host
self._aiozc = AsyncZeroconf()
async def register_server(self) -> None:
service_info = AsyncServiceInfo(
"_esphomelib._tcp.local.",
f"{self.name}._esphomelib._tcp.local.",
addresses=[socket.inet_aton(self.host)],
port=self.port,
properties={
"version": "2025.9.0",
"mac": _get_mac_address(),
"board": "host",
"platform": "HOST",
"network": "ethernet", # or "wifi"
},
server=f"{self.name}.local.",
)
await self._aiozc.async_register_service(service_info)
_LOGGER.debug("Zeroconf discovery enabled: %s", service_info)
def _get_mac_address() -> str:
"""Return MAC address formatted as hex with no colons."""
return "".join(
# pylint: disable=consider-using-f-string
["{:02x}".format((uuid.getnode() >> ele) & 0xFF) for ele in range(0, 8 * 6, 8)][
::-1
]
)