"""SMTP email sending -- password resets and battery-threshold alerts. Config lives in the server_settings singleton row (admin-configured via /admin, see routers/pages.py), not env vars -- it's operator infrastructure a household admin sets up once through the UI, same spirit as the rest of this project's "no separate config file" stance post-redesign. Uses stdlib smtplib; no new dependency. send_email() never raises -- a broken mail server shouldn't 500 a password-reset request or a battery report; callers get a bool and log a warning on failure.""" from __future__ import annotations import logging import smtplib from email.mime.text import MIMEText from .models import ServerSettings logger = logging.getLogger(__name__) SMTP_TIMEOUT_S = 10 def send_email(settings: ServerSettings, to_address: str, subject: str, body: str) -> bool: if not settings.smtp_host or not to_address: return False msg = MIMEText(body) msg["Subject"] = subject msg["From"] = settings.smtp_from_address or settings.smtp_username or "noreply@localhost" msg["To"] = to_address try: with smtplib.SMTP(settings.smtp_host, settings.smtp_port, timeout=SMTP_TIMEOUT_S) as smtp: if settings.smtp_use_tls: smtp.starttls() if settings.smtp_username: smtp.login(settings.smtp_username, settings.smtp_password) smtp.send_message(msg) return True except (OSError, smtplib.SMTPException) as e: logger.warning("Failed to send email to %s: %s", to_address, e) return False