Skip to content

Commit 9c8a15c

Browse files
Add go2rtc debug_ui yaml key to enable go2rtc ui (#129587)
* Add go2rtc debug_ui yaml key to enable go2rtc ui * Apply suggestions from code review Co-authored-by: Martin Hjelmare <[email protected]> * Order imports --------- Co-authored-by: Martin Hjelmare <[email protected]>
1 parent b09e54c commit 9c8a15c

File tree

5 files changed

+77
-25
lines changed

5 files changed

+77
-25
lines changed

homeassistant/components/go2rtc/__init__.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
from homeassistant.util.hass_dict import HassKey
3838
from homeassistant.util.package import is_docker_env
3939

40-
from .const import DOMAIN
40+
from .const import CONF_DEBUG_UI, DEBUG_UI_URL_MESSAGE, DOMAIN
4141
from .server import Server
4242

4343
_LOGGER = logging.getLogger(__name__)
@@ -72,9 +72,15 @@
7272
)
7373
)
7474

75-
7675
CONFIG_SCHEMA = vol.Schema(
77-
{DOMAIN: vol.Schema({vol.Optional(CONF_URL): cv.url})},
76+
{
77+
DOMAIN: vol.Schema(
78+
{
79+
vol.Exclusive(CONF_URL, DOMAIN, DEBUG_UI_URL_MESSAGE): cv.url,
80+
vol.Exclusive(CONF_DEBUG_UI, DOMAIN, DEBUG_UI_URL_MESSAGE): cv.boolean,
81+
}
82+
)
83+
},
7884
extra=vol.ALLOW_EXTRA,
7985
)
8086

@@ -104,7 +110,9 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
104110
return False
105111

106112
# HA will manage the binary
107-
server = Server(hass, binary)
113+
server = Server(
114+
hass, binary, enable_ui=config.get(DOMAIN, {}).get(CONF_DEBUG_UI, False)
115+
)
108116
await server.start()
109117

110118
async def on_stop(event: Event) -> None:

homeassistant/components/go2rtc/const.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22

33
DOMAIN = "go2rtc"
44

5-
CONF_BINARY = "binary"
5+
CONF_DEBUG_UI = "debug_ui"
6+
DEBUG_UI_URL_MESSAGE = "Url and debug_ui cannot be set at the same time."

homeassistant/components/go2rtc/server.py

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@
1010
_LOGGER = logging.getLogger(__name__)
1111
_TERMINATE_TIMEOUT = 5
1212
_SETUP_TIMEOUT = 30
13-
_SUCCESSFUL_BOOT_MESSAGE = "INF [api] listen addr=127.0.0.1:1984"
14-
13+
_SUCCESSFUL_BOOT_MESSAGE = "INF [api] listen addr="
14+
_LOCALHOST_IP = "127.0.0.1"
1515
# Default configuration for HA
1616
# - Api is listening only on localhost
1717
# - Disable rtsp listener
1818
# - Clear default ice servers
19-
_GO2RTC_CONFIG = """
19+
_GO2RTC_CONFIG_FORMAT = r"""
2020
api:
21-
listen: "127.0.0.1:1984"
21+
listen: "{api_ip}:1984"
2222
2323
rtsp:
2424
# ffmpeg needs rtsp for opus audio transcoding
@@ -29,29 +29,37 @@
2929
"""
3030

3131

32-
def _create_temp_file() -> str:
32+
def _create_temp_file(api_ip: str) -> str:
3333
"""Create temporary config file."""
3434
# Set delete=False to prevent the file from being deleted when the file is closed
3535
# Linux is clearing tmp folder on reboot, so no need to delete it manually
3636
with NamedTemporaryFile(prefix="go2rtc_", suffix=".yaml", delete=False) as file:
37-
file.write(_GO2RTC_CONFIG.encode())
37+
file.write(_GO2RTC_CONFIG_FORMAT.format(api_ip=api_ip).encode())
3838
return file.name
3939

4040

4141
class Server:
4242
"""Go2rtc server."""
4343

44-
def __init__(self, hass: HomeAssistant, binary: str) -> None:
44+
def __init__(
45+
self, hass: HomeAssistant, binary: str, *, enable_ui: bool = False
46+
) -> None:
4547
"""Initialize the server."""
4648
self._hass = hass
4749
self._binary = binary
4850
self._process: asyncio.subprocess.Process | None = None
4951
self._startup_complete = asyncio.Event()
52+
self._api_ip = _LOCALHOST_IP
53+
if enable_ui:
54+
# Listen on all interfaces for allowing access from all ips
55+
self._api_ip = ""
5056

5157
async def start(self) -> None:
5258
"""Start the server."""
5359
_LOGGER.debug("Starting go2rtc server")
54-
config_file = await self._hass.async_add_executor_job(_create_temp_file)
60+
config_file = await self._hass.async_add_executor_job(
61+
_create_temp_file, self._api_ip
62+
)
5563

5664
self._startup_complete.clear()
5765

@@ -84,9 +92,7 @@ async def _log_output(self, process: asyncio.subprocess.Process) -> None:
8492
async for line in process.stdout:
8593
msg = line[:-1].decode().strip()
8694
_LOGGER.debug(msg)
87-
if not self._startup_complete.is_set() and msg.endswith(
88-
_SUCCESSFUL_BOOT_MESSAGE
89-
):
95+
if not self._startup_complete.is_set() and _SUCCESSFUL_BOOT_MESSAGE in msg:
9096
self._startup_complete.set()
9197

9298
async def stop(self) -> None:

tests/components/go2rtc/test_init.py

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,11 @@
3131
)
3232
from homeassistant.components.default_config import DOMAIN as DEFAULT_CONFIG_DOMAIN
3333
from homeassistant.components.go2rtc import WebRTCProvider
34-
from homeassistant.components.go2rtc.const import DOMAIN
34+
from homeassistant.components.go2rtc.const import (
35+
CONF_DEBUG_UI,
36+
DEBUG_UI_URL_MESSAGE,
37+
DOMAIN,
38+
)
3539
from homeassistant.config_entries import ConfigEntry, ConfigEntryState, ConfigFlow
3640
from homeassistant.const import CONF_URL
3741
from homeassistant.core import HomeAssistant
@@ -265,7 +269,15 @@ async def test() -> None:
265269
"mock_is_docker_env",
266270
"mock_go2rtc_entry",
267271
)
268-
@pytest.mark.parametrize("config", [{DOMAIN: {}}, {DEFAULT_CONFIG_DOMAIN: {}}])
272+
@pytest.mark.parametrize(
273+
("config", "ui_enabled"),
274+
[
275+
({DOMAIN: {}}, False),
276+
({DOMAIN: {CONF_DEBUG_UI: True}}, True),
277+
({DEFAULT_CONFIG_DOMAIN: {}}, False),
278+
({DEFAULT_CONFIG_DOMAIN: {}, DOMAIN: {CONF_DEBUG_UI: True}}, True),
279+
],
280+
)
269281
@pytest.mark.parametrize("has_go2rtc_entry", [True, False])
270282
async def test_setup_go_binary(
271283
hass: HomeAssistant,
@@ -277,12 +289,13 @@ async def test_setup_go_binary(
277289
init_test_integration: MockCamera,
278290
has_go2rtc_entry: bool,
279291
config: ConfigType,
292+
ui_enabled: bool,
280293
) -> None:
281294
"""Test the go2rtc config entry with binary."""
282295
assert (len(hass.config_entries.async_entries(DOMAIN)) == 1) == has_go2rtc_entry
283296

284297
def after_setup() -> None:
285-
server.assert_called_once_with(hass, "/usr/bin/go2rtc")
298+
server.assert_called_once_with(hass, "/usr/bin/go2rtc", enable_ui=ui_enabled)
286299
server_start.assert_called_once()
287300

288301
await _test_setup_and_signaling(
@@ -468,7 +481,9 @@ async def test_close_session(
468481
ERR_CONNECT_RETRY = (
469482
"Could not connect to go2rtc instance on http://localhost:1984/; Retrying"
470483
)
471-
ERR_INVALID_URL = "Invalid config for 'go2rtc': invalid url"
484+
_INVALID_CONFIG = "Invalid config for 'go2rtc': "
485+
ERR_INVALID_URL = _INVALID_CONFIG + "invalid url"
486+
ERR_EXCLUSIVE = _INVALID_CONFIG + DEBUG_UI_URL_MESSAGE
472487
ERR_URL_REQUIRED = "Go2rtc URL required in non-docker installs"
473488

474489

@@ -501,6 +516,12 @@ async def test_non_user_setup_with_error(
501516
({DOMAIN: {}}, None, False, ERR_URL_REQUIRED),
502517
({DOMAIN: {}}, None, True, ERR_BINARY_NOT_FOUND),
503518
({DOMAIN: {CONF_URL: "invalid"}}, None, True, ERR_INVALID_URL),
519+
(
520+
{DOMAIN: {CONF_URL: "http://localhost:1984", CONF_DEBUG_UI: True}},
521+
None,
522+
True,
523+
ERR_EXCLUSIVE,
524+
),
504525
],
505526
)
506527
@pytest.mark.parametrize("has_go2rtc_entry", [True, False])

tests/components/go2rtc/test_server.py

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,15 @@
1616

1717

1818
@pytest.fixture
19-
def server(hass: HomeAssistant) -> Server:
19+
def enable_ui() -> bool:
20+
"""Fixture to enable the UI."""
21+
return False
22+
23+
24+
@pytest.fixture
25+
def server(hass: HomeAssistant, enable_ui: bool) -> Server:
2026
"""Fixture to initialize the Server."""
21-
return Server(hass, binary=TEST_BINARY)
27+
return Server(hass, binary=TEST_BINARY, enable_ui=enable_ui)
2228

2329

2430
@pytest.fixture
@@ -32,12 +38,20 @@ def mock_tempfile() -> Generator[Mock]:
3238
yield file
3339

3440

41+
@pytest.mark.parametrize(
42+
("enable_ui", "api_ip"),
43+
[
44+
(True, ""),
45+
(False, "127.0.0.1"),
46+
],
47+
)
3548
async def test_server_run_success(
3649
mock_create_subprocess: AsyncMock,
3750
server_stdout: list[str],
3851
server: Server,
3952
caplog: pytest.LogCaptureFixture,
4053
mock_tempfile: Mock,
54+
api_ip: str,
4155
) -> None:
4256
"""Test that the server runs successfully."""
4357
await server.start()
@@ -53,17 +67,19 @@ async def test_server_run_success(
5367
)
5468

5569
# Verify that the config file was written
56-
mock_tempfile.write.assert_called_once_with(b"""
70+
mock_tempfile.write.assert_called_once_with(
71+
f"""
5772
api:
58-
listen: "127.0.0.1:1984"
73+
listen: "{api_ip}:1984"
5974
6075
rtsp:
6176
# ffmpeg needs rtsp for opus audio transcoding
6277
listen: "127.0.0.1:8554"
6378
6479
webrtc:
6580
ice_servers: []
66-
""")
81+
""".encode()
82+
)
6783

6884
# Check that server read the log lines
6985
for entry in server_stdout:

0 commit comments

Comments
 (0)