Coverage for custom_components/dev_shell_server/__init__.py: 93%
25 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-10-03 09:02 +0000
« prev ^ index » next coverage.py v7.15.4, created at 2026-10-03 09:02 +0000
1"""Dev Shell: a live Python REPL inside Home Assistant, served over the websocket API.
3Set up from Settings > Devices & services > Add integration, or with `dev_shell_server:` in
4configuration.yaml (imported as a config entry). Executes arbitrary code as admin; never
5enable it on an instance where admin accounts are not fully trusted.
6"""
8from __future__ import annotations
10import logging
12from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
13from homeassistant.core import HomeAssistant
14from homeassistant.helpers import config_validation as cv
15from homeassistant.helpers.typing import ConfigType
17from . import websocket_api
18from .browser import open_target
19from .const import DOMAIN
20from .objtree import ObjTree
21from .session import SessionManager
23_LOGGER = logging.getLogger(__name__)
25CONFIG_SCHEMA = cv.empty_config_schema(DOMAIN)
28async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
29 # Commands stay registered for the life of the process (HA has no unregister);
30 # they report an error while no config entry is loaded.
31 websocket_api.async_register(hass)
32 if DOMAIN in config: 32 ↛ 33line 32 didn't jump to line 33 because the condition on line 32 was never true
33 hass.async_create_task(
34 hass.config_entries.flow.async_init(
35 DOMAIN, context={"source": SOURCE_IMPORT}, data={}
36 )
37 )
38 return True
41async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
42 hass.data[DOMAIN] = SessionManager({
43 "hass": hass,
44 "obj": ObjTree(hass),
45 "open": open_target,
46 })
47 _LOGGER.warning(
48 "Dev Shell is enabled: admin users can execute arbitrary Python in this instance"
49 )
50 return True
53async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
54 hass.data.pop(DOMAIN, None)
55 return True