"""Publish this device on the LAN: hostname + optional services. Config example (config/services.example.yaml or JSON): hostname: movil services: - {type: _ssh._tcp, port: 8022, name: "Termux SSH"} - {type: _smb._tcp, port: 4450, name: "Termux SMB"} """ import socket import time from pathlib import Path from zeroconf import Zeroconf, ServiceInfo def _local_ipv4() -> str: s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) try: s.connect(("8.8.8.8", 80)) return s.getsockname()[0] except OSError: return "127.0.0.1" finally: s.close() def _load_config(path: str | None) -> dict: if not path: return {"hostname": "movil", "services": []} p = Path(path).expanduser() if not p.exists(): return {"hostname": "movil", "services": []} text = p.read_text() if p.suffix in (".yml", ".yaml"): try: import yaml # type: ignore return yaml.safe_load(text) or {} except ImportError: print("warning: PyYAML not installed, using minimal parser") import json try: return json.loads(text) except Exception: return {"hostname": "movil", "services": []} def run(config_path: str | None = None, hostname: str | None = None, verbose: bool = True) -> None: """Publish hostname + services and block forever.""" cfg = _load_config(config_path) host = hostname or cfg.get("hostname", "movil") ip = _local_ipv4() zc = Zeroconf() infos: list[ServiceInfo] = [] # Register hostname via _workstation._tcp so the device shows up in browsers ws = ServiceInfo( type_="_workstation._tcp.local.", name=f"{host} [{ip.replace('.', '-')}]._workstation._tcp.local.", addresses=[socket.inet_aton(ip)], port=9, server=f"{host}.local.", ) zc.register_service(ws) infos.append(ws) for svc in (cfg.get("services") or []): try: t = svc["type"].rstrip(".") + ".local." n = f'{svc.get("name", host)}.{t}' info = ServiceInfo( type_=t, name=n, addresses=[socket.inet_aton(ip)], port=int(svc["port"]), server=f"{host}.local.", properties=svc.get("txt", {}), ) zc.register_service(info) infos.append(info) except Exception as e: print(f"warning: could not register {svc}: {e}") if verbose: print(f"[mdns] published {host}.local ({ip}), {len(infos)} record(s). Ctrl+C to stop.") try: while True: time.sleep(60) except KeyboardInterrupt: pass finally: for i in infos: try: zc.unregister_service(i) except Exception: pass zc.close() if verbose: print("[mdns] stopped, goodbye packets sent.")