Merge branch 'main' into Volume-Persistence-Across-Restarts

This commit is contained in:
Omar Amin authored and GitHub committed 2026-02-21 22:19:05 +02:00
commit 981a786a46
13 files changed
+123 -12

No files matched your search

+23 -6
View File
@@ -22,6 +22,8 @@ from getmac import get_mac_address
from .zeroconf import HomeAssistantZeroconf
from .util import get_default_interface
from .util import get_default_ipv4
from .util import get_version
from .util import get_esphome_version
_LOGGER = logging.getLogger(__name__)
_MODULE_DIR = Path(__file__).parent
@@ -183,13 +185,25 @@ async def main() -> None:
# Resolve name
if not args.name:
print("No name specified, try to autogenerate name")
device_name = f"lva-{mac_address_clean}"
print(f"Name autogenerated: {device_name}")
print("No friendly name specified, try to autogenerate name")
friendly_name = f"LVA - {mac_address_clean}"
print(f"Friendly name autogenerated: {friendly_name}")
else:
print("Name specified")
print(f"Using name: {args.name}")
device_name = args.name
print("Friendly name specified")
print(f"Using friendly name: {args.name}")
friendly_name = args.name
mac_no_colon = mac_address.replace(":", "").lower()
device_name = f"lva-{mac_no_colon}"
print(f"Device name: {device_name}")
# Resolve version
version = get_version()
print(f"Version: {version}")
# Resolve esphome version
esphome_version = get_esphome_version()
print(f"ESPHome api version: {esphome_version}")
# Resolve download dir
args.download_dir = Path(args.download_dir)
@@ -293,9 +307,12 @@ async def main() -> None:
state = ServerState(
name=device_name,
friendly_name=friendly_name,
network_interface=network_interface,
mac_address=get_mac_address(interface=network_interface),
ip_address=host_ip_address,
version=version,
esphome_version=esphome_version,
audio_queue=Queue(),
entities=[],
available_wake_words=available_wake_words,
+3
View File
@@ -64,9 +64,12 @@ class Preferences:
@dataclass
class ServerState:
name: str
friendly_name: str
mac_address: str
ip_address: str
network_interface: str
version: str
esphome_version: str
audio_queue: "Queue[Optional[bytes]]"
entities: "List[ESPHomeEntity]"
available_wake_words: "Dict[str, AvailableWakeWord]"
+6 -6
View File
@@ -294,15 +294,15 @@ class VoiceSatelliteProtocol(APIServer):
elif isinstance(msg, VoiceAssistantTimerEventResponse):
self.handle_timer_event(VoiceAssistantTimerEventType(msg.event_type), msg)
elif isinstance(msg, DeviceInfoRequest):
# Compute dynamic device name
base_name = re.sub(r"[\s-]+", "-", self.state.name.lower()).strip("-")
mac_no_colon = self.state.mac_address.replace(":", "").lower()
mac_last6 = mac_no_colon[-6:]
device_name = f"{base_name}-{mac_last6}"
_LOGGER.debug("Device info request")
yield DeviceInfoResponse(
uses_password=False,
name=device_name,
name=self.state.name,
friendly_name=self.state.friendly_name,
project_name="Open Home Foundation.Linux Voice Assistant",
project_version=self.state.version,
esphome_version=self.state.esphome_version,
mac_address=self.state.mac_address,
manufacturer="Open Home Foundation",
model="Linux Voice Assistant",
+60
View File
@@ -4,8 +4,68 @@ import uuid
# netifaces lib is from netifaces2
import netifaces
from collections.abc import Callable
from importlib.metadata import version, PackageNotFoundError
from pathlib import Path
from typing import Optional
# Cache for version to avoid repeated file reading
_version_cache: Optional[str] = None
_esphome_version_cache: Optional[str] = None
def get_version() -> str:
"""
Read the version from version.txt file.
This function reads the content safely without risk of code injection,
as it only reads raw text and performs no evaluation.
Returns:
str: The version from version.txt or 'unknown' if the file
does not exist or cannot be read.
"""
global _version_cache
if _version_cache is not None:
return _version_cache
version_file = Path(__file__).parent.parent / "version.txt"
try:
# Sicher lesen: nur Rohtext, keine Evaluierung
version = version_file.read_text(encoding="utf-8").strip()
_version_cache = version if version else "unknown"
except (FileNotFoundError, PermissionError, OSError):
_version_cache = "unknown"
return _version_cache
def get_esphome_version() -> str:
"""
Read the version of the installed aioesphomeapi package.
This function uses importlib.metadata to safely retrieve the version
of an installed Python package without executing any code from the
package itself.
Returns:
str: The version of aioesphomeapi (e.g., '42.7.0'), or 'unknown'
if the package is not installed or the version cannot be read.
"""
global _esphome_version_cache
if _esphome_version_cache is not None:
return _esphome_version_cache
try:
_esphome_version_cache = version("aioesphomeapi")
except PackageNotFoundError:
_esphome_version_cache = "unknown"
return _esphome_version_cache
def call_all(*callables: Optional[Callable[[], None]]) -> None:
for item in filter(None, callables):
item()