from __future__ import annotations import hmac import os from pathlib import Path from typing import Any from esphome.core import CORE from esphome.helpers import get_bool_env from .util.password import password_hash class DashboardSettings: """Settings for the dashboard.""" __slots__ = ( "config_dir", "password_hash", "username", "using_password", "on_ha_addon", "cookie_secret", "absolute_config_dir", "verbose", ) def __init__(self) -> None: """Initialize the dashboard settings.""" self.config_dir: str = "" self.password_hash: str = "" self.username: str = "" self.using_password: bool = False self.on_ha_addon: bool = False self.cookie_secret: str | None = None self.absolute_config_dir: Path | None = None self.verbose: bool = False def parse_args(self, args: Any) -> None: """Parse the arguments.""" self.on_ha_addon: bool = args.ha_addon password = args.password or os.getenv("PASSWORD") or "" if not self.on_ha_addon: self.username = args.username or os.getenv("USERNAME") or "" self.using_password = bool(password) if self.using_password: self.password_hash = password_hash(password) self.config_dir = args.configuration self.absolute_config_dir = Path(self.config_dir).resolve() self.verbose = args.verbose CORE.config_path = os.path.join(self.config_dir, ".") @property def relative_url(self) -> str: return os.getenv("ESPHOME_DASHBOARD_RELATIVE_URL") or "/" @property def status_use_mqtt(self) -> bool: return get_bool_env("ESPHOME_DASHBOARD_USE_MQTT") @property def using_ha_addon_auth(self) -> bool: if not self.on_ha_addon: return False return not get_bool_env("DISABLE_HA_AUTHENTICATION") @property def using_auth(self) -> bool: return self.using_password or self.using_ha_addon_auth @property def streamer_mode(self) -> bool: return get_bool_env("ESPHOME_STREAMER_MODE") def check_password(self, username: str, password: str) -> bool: if not self.using_auth: return True if username != self.username: return False # Compare password in constant running time (to prevent timing attacks) return hmac.compare_digest(self.password_hash, password_hash(password)) def rel_path(self, *args: Any) -> str: """Return a path relative to the ESPHome config folder.""" joined_path = os.path.join(self.config_dir, *args) # Raises ValueError if not relative to ESPHome config folder Path(joined_path).resolve().relative_to(self.absolute_config_dir) return joined_path