From 74c6ac08188facf296878191027c1de97f535365 Mon Sep 17 00:00:00 2001 From: Filip Gdovin Date: Wed, 27 May 2026 22:14:11 +0200 Subject: [PATCH] allow MQTT basic auth --- config.example.toml | 2 ++ .../mqtt_broker_server/topic_bridge.py | 14 +++++++++++++- src/roborock_local_server/config.py | 6 ++++++ src/roborock_local_server/server.py | 4 ++++ 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/config.example.toml b/config.example.toml index 3216359..bc50d3b 100644 --- a/config.example.toml +++ b/config.example.toml @@ -12,6 +12,8 @@ region = "us" mode = "embedded" host = "127.0.0.1" port = 18830 +username = "roborock" +password = "replace-with-a-secure-password" mosquitto_binary = "mosquitto" enable_topic_bridge = true diff --git a/src/roborock_local_server/bundled_backend/mqtt_broker_server/topic_bridge.py b/src/roborock_local_server/bundled_backend/mqtt_broker_server/topic_bridge.py index ee7e027..1d33d67 100644 --- a/src/roborock_local_server/bundled_backend/mqtt_broker_server/topic_bridge.py +++ b/src/roborock_local_server/bundled_backend/mqtt_broker_server/topic_bridge.py @@ -67,6 +67,8 @@ def __init__( *, host: str, port: int, + username: str = "", + password: str = "", logger: logging.Logger, fixed_device_did: str = "", fixed_device_duid: str = "", @@ -76,6 +78,8 @@ def __init__( ) -> None: self._host = host self._port = port + self._username = username + self._password = password self._logger = logger self._stop_event = asyncio.Event() self._task: asyncio.Task[None] | None = None @@ -440,10 +444,18 @@ async def _handle_device_message( ) async def _run(self) -> None: + auth: dict[str, str] = {} + if self._username: + auth["username"] = self._username + auth["password"] = self._password + self._logger.info( + "MQTT topic bridge auth attempt with username:password (%s:REDACTED)", + self._username + ) retry_delay_seconds = 2.0 while not self._stop_event.is_set(): try: - async with aiomqtt.Client(hostname=self._host, port=self._port) as client: + async with aiomqtt.Client(hostname=self._host, port=self._port, **auth) as client: await client.subscribe("rr/m/i/#") await client.subscribe("rr/d/i/#") self._logger.info( diff --git a/src/roborock_local_server/config.py b/src/roborock_local_server/config.py index 7206b2d..7f0f6c3 100644 --- a/src/roborock_local_server/config.py +++ b/src/roborock_local_server/config.py @@ -28,6 +28,8 @@ class BrokerConfig: mode: str host: str port: int + username: str + password: str mosquitto_binary: str enable_topic_bridge: bool @@ -200,6 +202,8 @@ def load_config(path: str | Path) -> AppConfig: if broker_mode == "embedded" and not broker_host: broker_host = "127.0.0.1" broker_port_default = 18830 if broker_mode == "embedded" else 1883 + broker_username = str(broker.get("username")) or "" + broker_password = str(broker.get("password")) or "" config = AppConfig( network=NetworkConfig( @@ -218,6 +222,8 @@ def load_config(path: str | Path) -> AppConfig: mode=broker_mode, host=broker_host, port=_as_port(broker.get("port"), "broker.port", broker_port_default), + username=broker_username, + password=broker_password, mosquitto_binary=str(broker.get("mosquitto_binary", "mosquitto")).strip() or "mosquitto", enable_topic_bridge=_as_bool(broker.get("enable_topic_bridge"), True), ), diff --git a/src/roborock_local_server/server.py b/src/roborock_local_server/server.py index 644b116..88796d6 100644 --- a/src/roborock_local_server/server.py +++ b/src/roborock_local_server/server.py @@ -1650,6 +1650,8 @@ async def start(self) -> None: self._topic_bridge = MqttTopicBridge( host=self.config.broker.host, port=self.config.broker.port, + username=self.config.broker.username, + password=self.config.broker.password, logger=self.loggers["mqtt"], runtime_state=self.runtime_state, inventory_path=self.paths.inventory_path, @@ -1677,6 +1679,8 @@ async def start(self) -> None: self.config.broker.mode, self.config.broker.host, self.config.broker.port, + self.config.broker.username, + self.config.broker.password, ) if self.config.tls.mode == "cloudflare_acme":