Allow exposing any entity to the default conversation agent (#92398)

* Allow exposing any entity to the default conversation agent

* Tweak

* Fix race, update tests

* Update tests
This commit is contained in:
Erik Montnemery 2023-05-03 15:45:54 +02:00 committed by GitHub
parent 6a8668effc
commit 0126cfa9d9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 195 additions and 128 deletions

View file

@ -1,4 +1,5 @@
"""Const for conversation integration."""
DOMAIN = "conversation"
DEFAULT_EXPOSED_ATTRIBUTES = {"device_class"}
HOME_ASSISTANT_AGENT = "homeassistant"

View file

@ -21,19 +21,21 @@ from homeassistant.components.homeassistant.exposed_entities import (
async_listen_entity_updates,
async_should_expose,
)
from homeassistant.const import ATTR_DEVICE_CLASS
from homeassistant.const import MATCH_ALL
from homeassistant.helpers import (
area_registry as ar,
device_registry as dr,
entity_registry as er,
intent,
start,
template,
translation,
)
from homeassistant.helpers.event import async_track_state_change
from homeassistant.util.json import JsonObjectType, json_loads_object
from .agent import AbstractConversationAgent, ConversationInput, ConversationResult
from .const import DOMAIN
from .const import DEFAULT_EXPOSED_ATTRIBUTES, DOMAIN
_LOGGER = logging.getLogger(__name__)
_DEFAULT_ERROR_TEXT = "Sorry, I couldn't understand that"
@ -81,16 +83,24 @@ def async_setup(hass: core.HomeAssistant) -> None:
async_should_expose(hass, DOMAIN, entity_id)
@core.callback
def async_handle_entity_registry_changed(event: core.Event) -> None:
"""Set expose flag on newly created entities."""
if event.data["action"] == "create":
async_should_expose(hass, DOMAIN, event.data["entity_id"])
def async_entity_state_listener(
changed_entity: str,
old_state: core.State | None,
new_state: core.State | None,
):
"""Set expose flag on new entities."""
if old_state is not None or new_state is None:
return
async_should_expose(hass, DOMAIN, changed_entity)
hass.bus.async_listen(
er.EVENT_ENTITY_REGISTRY_UPDATED,
async_handle_entity_registry_changed,
run_immediately=True,
)
@core.callback
def async_hass_started(hass: core.HomeAssistant) -> None:
"""Set expose flag on all entities."""
for state in hass.states.async_all():
async_should_expose(hass, DOMAIN, state.entity_id)
async_track_state_change(hass, MATCH_ALL, async_entity_state_listener)
start.async_at_started(hass, async_hass_started)
class DefaultAgent(AbstractConversationAgent):
@ -130,6 +140,11 @@ class DefaultAgent(AbstractConversationAgent):
self._async_handle_entity_registry_changed,
run_immediately=True,
)
self.hass.bus.async_listen(
core.EVENT_STATE_CHANGED,
self._async_handle_state_changed,
run_immediately=True,
)
async_listen_entity_updates(
self.hass, DOMAIN, self._async_exposed_entities_updated
)
@ -475,12 +490,19 @@ class DefaultAgent(AbstractConversationAgent):
@core.callback
def _async_handle_entity_registry_changed(self, event: core.Event) -> None:
"""Clear names list cache when an entity registry entry has changed."""
if event.data["action"] == "update" and not any(
if event.data["action"] != "update" or not any(
field in event.data["changes"] for field in _ENTITY_REGISTRY_UPDATE_FIELDS
):
return
self._slot_lists = None
@core.callback
def _async_handle_state_changed(self, event: core.Event) -> None:
"""Clear names list cache when a state is added or removed from the state machine."""
if event.data.get("old_state") and event.data.get("new_state"):
return
self._slot_lists = None
@core.callback
def _async_exposed_entities_updated(self) -> None:
"""Handle updated preferences."""
@ -493,30 +515,38 @@ class DefaultAgent(AbstractConversationAgent):
area_ids_with_entities: set[str] = set()
entity_registry = er.async_get(self.hass)
entities = [
entity
for entity in entity_registry.entities.values()
if async_should_expose(self.hass, DOMAIN, entity.entity_id)
states = [
state
for state in self.hass.states.async_all()
if async_should_expose(self.hass, DOMAIN, state.entity_id)
]
devices = dr.async_get(self.hass)
# Gather exposed entity names
entity_names = []
for entity in entities:
for state in states:
# Checked against "requires_context" and "excludes_context" in hassil
context = {"domain": entity.domain}
if entity.device_class:
context[ATTR_DEVICE_CLASS] = entity.device_class
context = {"domain": state.domain}
if state.attributes:
# Include some attributes
for attr in DEFAULT_EXPOSED_ATTRIBUTES:
if attr not in state.attributes:
continue
context[attr] = state.attributes[attr]
entity = entity_registry.async_get(state.entity_id)
if not entity:
# Default name
entity_names.append((state.name, state.name, context))
continue
if entity.aliases:
for alias in entity.aliases:
entity_names.append((alias, alias, context))
# Default name
name = entity.async_friendly_name(self.hass) or entity.entity_id.replace(
"_", " "
)
entity_names.append((name, name, context))
entity_names.append((state.name, state.name, context))
if entity.area_id:
# Expose area too

View file

@ -305,7 +305,11 @@ class ExposedEntities:
if domain in DEFAULT_EXPOSED_DOMAINS:
return True
device_class = get_device_class(self._hass, entity_id)
try:
device_class = get_device_class(self._hass, entity_id)
except HomeAssistantError:
# The entity no longer exists
return False
if (
domain == "binary_sensor"
and device_class in DEFAULT_EXPOSED_BINARY_SENSOR_DEVICE_CLASSES

View file

@ -307,26 +307,6 @@ class RegistryEntry:
hass.states.async_set(self.entity_id, STATE_UNAVAILABLE, attrs)
def async_friendly_name(self, hass: HomeAssistant) -> str | None:
"""Return the friendly name.
If self.name is not None, this returns self.name
If has_entity_name is False, self.original_name
If has_entity_name is True, this returns device.name + self.original_name
"""
if not self.has_entity_name or self.name is not None:
return self.name or self.original_name
device_registry = dr.async_get(hass)
if not (device_id := self.device_id) or not (
device_entry := device_registry.async_get(device_id)
):
return self.original_name
if not (original_name := self.original_name):
return device_entry.name_by_user or device_entry.name
return f"{device_entry.name_by_user or device_entry.name} {original_name}"
class EntityRegistryStore(storage.Store[dict[str, list[dict[str, Any]]]]):
"""Store entity registry data."""

View file

@ -1,4 +1,6 @@
"""The tests for the Air Quality component."""
import pytest
from homeassistant.components.air_quality import ATTR_N2O, ATTR_OZONE, ATTR_PM_10
from homeassistant.const import (
ATTR_ATTRIBUTION,
@ -9,6 +11,12 @@ from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component
@pytest.fixture(autouse=True)
async def setup_homeassistant(hass: HomeAssistant):
"""Set up the homeassistant integration."""
await async_setup_component(hass, "homeassistant", {})
async def test_state(hass: HomeAssistant) -> None:
"""Test Air Quality state."""
config = {"air_quality": {"platform": "demo"}}

View file

@ -39,6 +39,7 @@ def events(hass: HomeAssistant) -> list[Event]:
@pytest.fixture
async def mock_camera(hass: HomeAssistant) -> None:
"""Initialize a demo camera platform."""
assert await async_setup_component(hass, "homeassistant", {})
assert await async_setup_component(
hass, "camera", {camera.DOMAIN: {"platform": "demo"}}
)

View file

@ -1539,6 +1539,7 @@ async def test_automation_restore_last_triggered_with_initial_state(
async def test_extraction_functions(hass: HomeAssistant) -> None:
"""Test extraction functions."""
await async_setup_component(hass, "homeassistant", {})
await async_setup_component(hass, "calendar", {"calendar": {"platform": "demo"}})
assert await async_setup_component(
hass,

View file

@ -0,0 +1,11 @@
"""Test fixtures for calendar sensor platforms."""
import pytest
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component
@pytest.fixture(autouse=True)
async def setup_homeassistant(hass: HomeAssistant):
"""Set up the homeassistant integration."""
await async_setup_component(hass, "homeassistant", {})

View file

@ -1,6 +1,8 @@
"""The tests for calendar recorder."""
from datetime import timedelta
import pytest
from homeassistant.components.recorder import Recorder
from homeassistant.components.recorder.history import get_significant_states
from homeassistant.const import ATTR_FRIENDLY_NAME
@ -12,9 +14,15 @@ from tests.common import async_fire_time_changed
from tests.components.recorder.common import async_wait_recording_done
@pytest.fixture(autouse=True)
async def setup_homeassistant():
"""Override the fixture in calendar.conftest."""
async def test_exclude_attributes(recorder_mock: Recorder, hass: HomeAssistant) -> None:
"""Test sensor attributes to be excluded."""
now = dt_util.utcnow()
await async_setup_component(hass, "homeassistant", {})
await async_setup_component(hass, "calendar", {"calendar": {"platform": "demo"}})
await hass.async_block_till_done()

View file

@ -5,11 +5,18 @@ import pytest
from homeassistant.components import camera
from homeassistant.components.camera.const import StreamType
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component
from .common import WEBRTC_ANSWER
@pytest.fixture(autouse=True)
async def setup_homeassistant(hass: HomeAssistant):
"""Set up the homeassistant integration."""
await async_setup_component(hass, "homeassistant", {})
@pytest.fixture(name="mock_camera")
async def mock_camera_fixture(hass):
"""Initialize a demo camera platform."""

View file

@ -3,6 +3,8 @@ from __future__ import annotations
from datetime import timedelta
import pytest
from homeassistant.components import camera
from homeassistant.components.recorder import Recorder
from homeassistant.components.recorder.history import get_significant_states
@ -20,9 +22,15 @@ from tests.common import async_fire_time_changed
from tests.components.recorder.common import async_wait_recording_done
@pytest.fixture(autouse=True)
async def setup_homeassistant():
"""Override the fixture in calendar.conftest."""
async def test_exclude_attributes(recorder_mock: Recorder, hass: HomeAssistant) -> None:
"""Test camera registered attributes to be excluded."""
now = dt_util.utcnow()
await async_setup_component(hass, "homeassistant", {})
await async_setup_component(
hass, camera.DOMAIN, {camera.DOMAIN: {"platform": "demo"}}
)

View file

@ -91,21 +91,21 @@ async def test_exposed_areas(
)
device_registry.async_update_device(kitchen_device.id, area_id=area_kitchen.id)
kitchen_light = entity_registry.async_get_or_create(
"light", "demo", "1234", original_name="kitchen light"
)
kitchen_light = entity_registry.async_get_or_create("light", "demo", "1234")
entity_registry.async_update_entity(
kitchen_light.entity_id, device_id=kitchen_device.id
)
hass.states.async_set(kitchen_light.entity_id, "on")
bedroom_light = entity_registry.async_get_or_create(
"light", "demo", "5678", original_name="bedroom light"
hass.states.async_set(
kitchen_light.entity_id, "on", attributes={ATTR_FRIENDLY_NAME: "kitchen light"}
)
bedroom_light = entity_registry.async_get_or_create("light", "demo", "5678")
entity_registry.async_update_entity(
bedroom_light.entity_id, area_id=area_bedroom.id
)
hass.states.async_set(bedroom_light.entity_id, "on")
hass.states.async_set(
bedroom_light.entity_id, "on", attributes={ATTR_FRIENDLY_NAME: "bedroom light"}
)
# Hide the bedroom light
expose_entity(hass, bedroom_light.entity_id, False)
@ -156,6 +156,8 @@ async def test_expose_flag_automatically_set(
assert await async_setup_component(hass, "conversation", {})
await hass.async_block_till_done()
with patch("homeassistant.components.http.start_http_server_and_save_config"):
await hass.async_start()
# After setting up conversation, the expose flag should now be set on all entities
assert async_get_assistant_settings(hass, conversation.DOMAIN) == {
@ -164,10 +166,11 @@ async def test_expose_flag_automatically_set(
}
# New entities will automatically have the expose flag set
new_light = entity_registry.async_get_or_create("light", "demo", "2345")
new_light = "light.demo_2345"
hass.states.async_set(new_light, "test")
await hass.async_block_till_done()
assert async_get_assistant_settings(hass, conversation.DOMAIN) == {
light.entity_id: {"should_expose": True},
new_light.entity_id: {"should_expose": True},
new_light: {"should_expose": True},
test.entity_id: {"should_expose": False},
}

View file

@ -202,11 +202,7 @@ async def test_http_processing_intent_entity_added_removed(
# Add an entity
entity_registry.async_get_or_create(
"light",
"demo",
"5678",
suggested_object_id="late",
original_name="friendly light",
"light", "demo", "5678", suggested_object_id="late"
)
hass.states.async_set("light.late", "off", {"friendly_name": "friendly light"})
@ -274,7 +270,7 @@ async def test_http_processing_intent_entity_added_removed(
}
# Now delete the entity
entity_registry.async_remove("light.late")
hass.states.async_remove("light.late")
client = await hass_client()
resp = await client.post(
@ -313,11 +309,7 @@ async def test_http_processing_intent_alias_added_removed(
so that the new alias is available.
"""
entity_registry.async_get_or_create(
"light",
"demo",
"1234",
suggested_object_id="kitchen",
original_name="kitchen light",
"light", "demo", "1234", suggested_object_id="kitchen"
)
hass.states.async_set("light.kitchen", "off", {"friendly_name": "kitchen light"})
@ -438,7 +430,6 @@ async def test_http_processing_intent_entity_renamed(
LIGHT_DOMAIN,
{LIGHT_DOMAIN: [{"platform": "test"}]},
)
await hass.async_block_till_done()
calls = async_mock_service(hass, LIGHT_DOMAIN, "turn_on")
client = await hass_client()
@ -882,20 +873,9 @@ async def test_http_processing_intent_conversion_not_expose_new(
@pytest.mark.parametrize("agent_id", AGENT_ID_OPTIONS)
@pytest.mark.parametrize("sentence", ("turn on kitchen", "turn kitchen on"))
async def test_turn_on_intent(
hass: HomeAssistant,
init_components,
entity_registry: er.EntityRegistry,
sentence,
agent_id,
hass: HomeAssistant, init_components, sentence, agent_id
) -> None:
"""Test calling the turn on intent."""
entity_registry.async_get_or_create(
"light",
"demo",
"1234",
suggested_object_id="kitchen",
original_name="kitchen",
)
hass.states.async_set("light.kitchen", "off")
calls = async_mock_service(hass, LIGHT_DOMAIN, "turn_on")
@ -913,17 +893,8 @@ async def test_turn_on_intent(
@pytest.mark.parametrize("sentence", ("turn off kitchen", "turn kitchen off"))
async def test_turn_off_intent(
hass: HomeAssistant, init_components, entity_registry: er.EntityRegistry, sentence
) -> None:
async def test_turn_off_intent(hass: HomeAssistant, init_components, sentence) -> None:
"""Test calling the turn on intent."""
entity_registry.async_get_or_create(
"light",
"demo",
"1234",
suggested_object_id="kitchen",
original_name="kitchen",
)
hass.states.async_set("light.kitchen", "on")
calls = async_mock_service(hass, LIGHT_DOMAIN, "turn_off")
@ -969,21 +940,11 @@ async def test_http_api_no_match(
async def test_http_api_handle_failure(
hass: HomeAssistant,
init_components,
entity_registry: er.EntityRegistry,
hass_client: ClientSessionGenerator,
hass: HomeAssistant, init_components, hass_client: ClientSessionGenerator
) -> None:
"""Test the HTTP conversation API with an error during handling."""
client = await hass_client()
entity_registry.async_get_or_create(
"light",
"demo",
"1234",
suggested_object_id="kitchen",
original_name="kitchen",
)
hass.states.async_set("light.kitchen", "off")
# Raise an error during intent handling
@ -1020,19 +981,11 @@ async def test_http_api_handle_failure(
async def test_http_api_unexpected_failure(
hass: HomeAssistant,
init_components,
entity_registry: er.EntityRegistry,
hass_client: ClientSessionGenerator,
) -> None:
"""Test the HTTP conversation API with an unexpected error during handling."""
client = await hass_client()
entity_registry.async_get_or_create(
"light",
"demo",
"1234",
suggested_object_id="kitchen",
original_name="kitchen",
)
hass.states.async_set("light.kitchen", "off")
# Raise an "unexpected" error during intent handling
@ -1355,17 +1308,8 @@ async def test_prepare_fail(hass: HomeAssistant) -> None:
assert not agent._lang_intents.get("not-a-language")
async def test_language_region(
hass: HomeAssistant, init_components, entity_registry: er.EntityRegistry
) -> None:
async def test_language_region(hass: HomeAssistant, init_components) -> None:
"""Test calling the turn on intent."""
entity_registry.async_get_or_create(
"light",
"demo",
"1234",
suggested_object_id="kitchen",
original_name="kitchen",
)
hass.states.async_set("light.kitchen", "off")
calls = async_mock_service(hass, LIGHT_DOMAIN, "turn_on")
@ -1414,17 +1358,8 @@ async def test_reload_on_new_component(hass: HomeAssistant) -> None:
assert {"light"} == (lang_intents.loaded_components - loaded_components)
async def test_non_default_response(
hass: HomeAssistant, init_components, entity_registry: er.EntityRegistry
) -> None:
async def test_non_default_response(hass: HomeAssistant, init_components) -> None:
"""Test intent response that is not the default."""
entity_registry.async_get_or_create(
"cover",
"demo",
"1234",
suggested_object_id="front_door",
original_name="front door",
)
hass.states.async_set("cover.front_door", "closed")
calls = async_mock_service(hass, "cover", SERVICE_OPEN_COVER)

View file

@ -218,6 +218,7 @@ async def test_discover_platform(
mock_demo_setup_scanner, mock_see, hass: HomeAssistant
) -> None:
"""Test discovery of device_tracker demo platform."""
await async_setup_component(hass, "homeassistant", {})
with patch("homeassistant.components.device_tracker.legacy.update_config"):
await discovery.async_load_platform(
hass, device_tracker.DOMAIN, "demo", {"test_key": "test_val"}, {"bla": {}}

View file

@ -75,6 +75,12 @@ VALID_CONFIG = {
}
@pytest.fixture(autouse=True)
async def setup_homeassistant(hass: HomeAssistant):
"""Set up the homeassistant integration."""
await async_setup_component(hass, "homeassistant", {})
@pytest.fixture
def mock_healthybox():
"""Mock fb.check_box_health."""

View file

@ -209,6 +209,7 @@ async def test_send_text_command_expired_token_refresh_failure(
requires_reauth: ConfigEntryState,
) -> None:
"""Test failure refreshing token in send_text_command."""
await async_setup_component(hass, "homeassistant", {})
await setup_integration()
entries = hass.config_entries.async_entries(DOMAIN)

View file

@ -43,6 +43,12 @@ MOCK_START_STREAM_SESSION_UUID = UUID("3303d503-17cc-469a-b672-92436a71a2f6")
PID_THAT_WILL_NEVER_BE_ALIVE = 2147483647
@pytest.fixture(autouse=True)
async def setup_homeassistant(hass: HomeAssistant):
"""Set up the homeassistant integration."""
await async_setup_component(hass, "homeassistant", {})
async def _async_start_streaming(hass, acc):
"""Start streaming a camera."""
acc.set_selected_stream_configuration(MOCK_START_STREAM_TLV)

View file

@ -16,6 +16,12 @@ from tests.common import assert_setup_component, async_capture_events
from tests.test_util.aiohttp import AiohttpClientMocker
@pytest.fixture(autouse=True)
async def setup_homeassistant(hass: HomeAssistant):
"""Set up the homeassistant integration."""
await async_setup_component(hass, "homeassistant", {})
@pytest.fixture
def aiohttp_unused_port(event_loop, aiohttp_unused_port, socket_enabled):
"""Return aiohttp_unused_port and allow opening sockets."""

View file

@ -21,6 +21,12 @@ from tests.test_util.aiohttp import AiohttpClientMocker
from tests.typing import ClientSessionGenerator, WebSocketGenerator
@pytest.fixture(autouse=True)
async def setup_homeassistant(hass: HomeAssistant):
"""Set up the homeassistant integration."""
await async_setup_component(hass, "homeassistant", {})
async def test_get_image_http(
hass: HomeAssistant, hass_client_no_auth: ClientSessionGenerator
) -> None:

View file

@ -25,6 +25,7 @@ from tests.components.recorder.common import async_wait_recording_done
async def test_exclude_attributes(recorder_mock: Recorder, hass: HomeAssistant) -> None:
"""Test media_player registered attributes to be excluded."""
now = dt_util.utcnow()
await async_setup_component(hass, "homeassistant", {})
await async_setup_component(
hass, media_player.DOMAIN, {media_player.DOMAIN: {"platform": "demo"}}
)

View file

@ -25,6 +25,12 @@ from tests.common import assert_setup_component, load_fixture
from tests.test_util.aiohttp import AiohttpClientMocker
@pytest.fixture(autouse=True)
async def setup_homeassistant(hass: HomeAssistant):
"""Set up the homeassistant integration."""
await async_setup_component(hass, "homeassistant", {})
def create_group(hass, name):
"""Create a new person group.

View file

@ -26,6 +26,12 @@ CONFIG = {
ENDPOINT_URL = f"https://westus.{mf.FACE_API_URL}"
@pytest.fixture(autouse=True)
async def setup_homeassistant(hass: HomeAssistant):
"""Set up the homeassistant integration."""
await async_setup_component(hass, "homeassistant", {})
@pytest.fixture
def store_mock():
"""Mock update store."""

View file

@ -14,6 +14,12 @@ from tests.components.image_processing import common
from tests.test_util.aiohttp import AiohttpClientMocker
@pytest.fixture(autouse=True)
async def setup_homeassistant(hass: HomeAssistant):
"""Set up the homeassistant integration."""
await async_setup_component(hass, "homeassistant", {})
@pytest.fixture
def store_mock():
"""Mock update store."""

View file

@ -14,6 +14,12 @@ from tests.components.image_processing import common
from tests.test_util.aiohttp import AiohttpClientMocker
@pytest.fixture(autouse=True)
async def setup_homeassistant(hass: HomeAssistant):
"""Set up the homeassistant integration."""
await async_setup_component(hass, "homeassistant", {})
@pytest.fixture
async def setup_openalpr_cloud(hass):
"""Set up openalpr cloud."""

View file

@ -14,6 +14,7 @@ from homeassistant.components.rtsp_to_webrtc import CONF_STUN_SERVER, DOMAIN
from homeassistant.components.websocket_api.const import TYPE_RESULT
from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component
from .conftest import SERVER_URL, STREAM_SOURCE, ComponentSetup
@ -27,6 +28,12 @@ OFFER_SDP = "v=0\r\no=carol 28908764872 28908764872 IN IP4 100.3.6.6\r\n..."
ANSWER_SDP = "v=0\r\no=bob 2890844730 2890844730 IN IP4 host.example.com\r\n..."
@pytest.fixture(autouse=True)
async def setup_homeassistant(hass: HomeAssistant):
"""Set up the homeassistant integration."""
await async_setup_component(hass, "homeassistant", {})
async def test_setup_success(
hass: HomeAssistant, rtsp_to_webrtc_client: Any, setup_integration: ComponentSetup
) -> None:

View file

@ -1373,6 +1373,7 @@ def test_compile_hourly_sum_statistics_negative_state(
mocksensor._attr_should_poll = False
platform.ENTITIES["custom_sensor"] = mocksensor
setup_component(hass, "homeassistant", {})
setup_component(
hass, "sensor", {"sensor": [{"platform": "demo"}, {"platform": "test"}]}
)

View file

@ -46,6 +46,12 @@ MOCK_DETECTIONS = {
MOCK_NOW = datetime.datetime(2020, 2, 20, 10, 5, 3)
@pytest.fixture(autouse=True)
async def setup_homeassistant(hass: HomeAssistant):
"""Set up the homeassistant integration."""
await async_setup_component(hass, "homeassistant", {})
@pytest.fixture
def mock_detections():
"""Return a mock detection."""

View file

@ -72,6 +72,8 @@ async def test_setup_legacy_service(hass: HomeAssistant) -> None:
},
}
await async_setup_component(hass, "homeassistant", {})
with assert_setup_component(1, tts.DOMAIN):
assert await async_setup_component(hass, tts.DOMAIN, config)

View file

@ -1105,6 +1105,7 @@ async def test_state_template(hass: HomeAssistant) -> None:
async def test_browse_media(hass: HomeAssistant) -> None:
"""Test browse media."""
await async_setup_component(hass, "homeassistant", {})
await async_setup_component(
hass, "media_player", {"media_player": {"platform": "demo"}}
)
@ -1135,6 +1136,7 @@ async def test_browse_media(hass: HomeAssistant) -> None:
async def test_browse_media_override(hass: HomeAssistant) -> None:
"""Test browse media override."""
await async_setup_component(hass, "homeassistant", {})
await async_setup_component(
hass, "media_player", {"media_player": {"platform": "demo"}}
)

View file

@ -18,6 +18,7 @@ from tests.components.recorder.common import async_wait_recording_done
async def test_exclude_attributes(recorder_mock: Recorder, hass: HomeAssistant) -> None:
"""Test weather attributes to be excluded."""
now = dt_util.utcnow()
await async_setup_component(hass, "homeassistant", {})
await async_setup_component(hass, DOMAIN, {DOMAIN: {"platform": "demo"}})
hass.config.units = METRIC_SYSTEM
await hass.async_block_till_done()