Merge branch 'main' into Volume-Persistence-Across-Restarts
This commit is contained in:
13 files changed
+123
-12
No files matched your search
@@ -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,
|
||||
|
||||
@@ -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]"
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in new issue
Block a user