Add "Mein Zimmer" room-control page (Home Assistant, proxied through the backend)
Implements the room-control page from the design mockup: a scenes row above cards for shutters, color lamps, and brightness-only lamps, all driven by a new `general.ha` config section (server URL, token, ordered device/scene lists). The backend proxies every Home Assistant call server-side (GET/POST /api/ha/...) rather than the browser calling Home Assistant directly, so the long-lived token never leaves the LAN device and Home Assistant's own CORS settings don't need to know about musicmouse at all. Card kind (shutter/color/brightness-only) is inferred at runtime from what Home Assistant reports about each entity, not configured explicitly. Also stops tracking python-backend/config.yml, which had drifted into the repo despite its own header saying it shouldn't be - it now carries real credentials locally and needs to stay untracked. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -36,6 +36,8 @@ __all__ = [
|
||||
"FigureColors",
|
||||
"FigureConfig",
|
||||
"GeneralConfig",
|
||||
"HaConfig",
|
||||
"HaDeviceConfig",
|
||||
"LibraryConfig",
|
||||
"MqttConfig",
|
||||
"WebConfig",
|
||||
@@ -173,6 +175,36 @@ class WebConfig(_Strict):
|
||||
return None if folder is None else _resolve_folder(folder, info, must_exist=False)
|
||||
|
||||
|
||||
class HaDeviceConfig(_Strict):
|
||||
"""One Home Assistant entity to expose to the room-control page ("Mein Zimmer")."""
|
||||
|
||||
entity_id: str
|
||||
name: str | None = None
|
||||
|
||||
|
||||
class HaConfig(_Strict):
|
||||
"""Home Assistant integration for the room-control page ("Mein Zimmer").
|
||||
|
||||
The backend never calls Home Assistant itself - it only hands the browser the
|
||||
server URL, the token, and these two ordered lists. Control happens directly from
|
||||
the browser to Home Assistant's own REST API, so this token grants full HA control
|
||||
to anything on the LAN that can reach musicmouse. See config.yml.example.
|
||||
"""
|
||||
|
||||
url: str
|
||||
token: str
|
||||
#: Order is preserved and drives the device card grid on the room page.
|
||||
devices: list[HaDeviceConfig] = Field(default_factory=list)
|
||||
#: Order is preserved and drives the scene pill row on the room page.
|
||||
scenes: list[HaDeviceConfig] = Field(default_factory=list)
|
||||
|
||||
@model_validator(mode="after")
|
||||
def _check_something_configured(self) -> Self:
|
||||
if not self.devices and not self.scenes:
|
||||
raise ValueError("configure at least one device or scene, or omit the ha section")
|
||||
return self
|
||||
|
||||
|
||||
class GeneralConfig(_Strict):
|
||||
library: LibraryConfig
|
||||
|
||||
@@ -188,6 +220,7 @@ class GeneralConfig(_Strict):
|
||||
|
||||
mqtt: MqttConfig | None = None
|
||||
web: WebConfig | None = None
|
||||
ha: HaConfig | None = None
|
||||
|
||||
min_volume: int = Field(default=0, ge=0, le=200)
|
||||
max_volume: int = Field(default=100, ge=0, le=200)
|
||||
|
||||
Reference in New Issue
Block a user