mirror of
https://github.com/home-assistant/core.git
synced 2025-07-24 21:57:51 +00:00
Check area temperature sensors in get temperature intent (#139221)
* Check area temperature sensors in get temperature intent * Fix candidate check * Add new code back in * Remove cruft from climate
This commit is contained in:
parent
e9bb4625d8
commit
49c27ae7bc
@ -2,13 +2,14 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections.abc import Collection
|
||||||
import logging
|
import logging
|
||||||
from typing import Any, Protocol
|
from typing import Any, Protocol
|
||||||
|
|
||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components import http
|
from homeassistant.components import http, sensor
|
||||||
from homeassistant.components.climate import DOMAIN as CLIMATE_DOMAIN
|
from homeassistant.components.climate import DOMAIN as CLIMATE_DOMAIN
|
||||||
from homeassistant.components.cover import (
|
from homeassistant.components.cover import (
|
||||||
ATTR_POSITION,
|
ATTR_POSITION,
|
||||||
@ -40,7 +41,12 @@ from homeassistant.const import (
|
|||||||
SERVICE_TURN_ON,
|
SERVICE_TURN_ON,
|
||||||
)
|
)
|
||||||
from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN, HomeAssistant, State
|
from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN, HomeAssistant, State
|
||||||
from homeassistant.helpers import config_validation as cv, integration_platform, intent
|
from homeassistant.helpers import (
|
||||||
|
area_registry as ar,
|
||||||
|
config_validation as cv,
|
||||||
|
integration_platform,
|
||||||
|
intent,
|
||||||
|
)
|
||||||
from homeassistant.helpers.typing import ConfigType
|
from homeassistant.helpers.typing import ConfigType
|
||||||
from homeassistant.util import dt as dt_util
|
from homeassistant.util import dt as dt_util
|
||||||
|
|
||||||
@ -454,6 +460,9 @@ class GetTemperatureIntent(intent.IntentHandler):
|
|||||||
slot_schema = {
|
slot_schema = {
|
||||||
vol.Optional("area"): intent.non_empty_string,
|
vol.Optional("area"): intent.non_empty_string,
|
||||||
vol.Optional("name"): intent.non_empty_string,
|
vol.Optional("name"): intent.non_empty_string,
|
||||||
|
vol.Optional("floor"): intent.non_empty_string,
|
||||||
|
vol.Optional("preferred_area_id"): cv.string,
|
||||||
|
vol.Optional("preferred_floor_id"): cv.string,
|
||||||
}
|
}
|
||||||
platforms = {CLIMATE_DOMAIN}
|
platforms = {CLIMATE_DOMAIN}
|
||||||
|
|
||||||
@ -470,13 +479,71 @@ class GetTemperatureIntent(intent.IntentHandler):
|
|||||||
if "area" in slots:
|
if "area" in slots:
|
||||||
area = slots["area"]["value"]
|
area = slots["area"]["value"]
|
||||||
|
|
||||||
|
floor_name: str | None = None
|
||||||
|
if "floor" in slots:
|
||||||
|
floor_name = slots["floor"]["value"]
|
||||||
|
|
||||||
|
match_preferences = intent.MatchTargetsPreferences(
|
||||||
|
area_id=slots.get("preferred_area_id", {}).get("value"),
|
||||||
|
floor_id=slots.get("preferred_floor_id", {}).get("value"),
|
||||||
|
)
|
||||||
|
|
||||||
|
if (not name) and (area or match_preferences.area_id):
|
||||||
|
# Look for temperature sensors assigned to an area
|
||||||
|
area_registry = ar.async_get(hass)
|
||||||
|
area_temperature_ids: dict[str, str] = {}
|
||||||
|
|
||||||
|
# Keep candidates that are registered as area temperature sensors
|
||||||
|
def area_candidate_filter(
|
||||||
|
candidate: intent.MatchTargetsCandidate,
|
||||||
|
possible_area_ids: Collection[str],
|
||||||
|
) -> bool:
|
||||||
|
for area_id in possible_area_ids:
|
||||||
|
temperature_id = area_temperature_ids.get(area_id)
|
||||||
|
if (temperature_id is None) and (
|
||||||
|
area_entry := area_registry.async_get_area(area_id)
|
||||||
|
):
|
||||||
|
temperature_id = area_entry.temperature_entity_id or ""
|
||||||
|
area_temperature_ids[area_id] = temperature_id
|
||||||
|
|
||||||
|
if candidate.state.entity_id == temperature_id:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
match_constraints = intent.MatchTargetsConstraints(
|
||||||
|
area_name=area,
|
||||||
|
floor_name=floor_name,
|
||||||
|
domains=[sensor.DOMAIN],
|
||||||
|
device_classes=[sensor.SensorDeviceClass.TEMPERATURE],
|
||||||
|
assistant=intent_obj.assistant,
|
||||||
|
single_target=True,
|
||||||
|
)
|
||||||
|
match_result = intent.async_match_targets(
|
||||||
|
hass,
|
||||||
|
match_constraints,
|
||||||
|
match_preferences,
|
||||||
|
area_candidate_filter=area_candidate_filter,
|
||||||
|
)
|
||||||
|
if match_result.is_match:
|
||||||
|
# Found temperature sensor
|
||||||
|
response = intent_obj.create_response()
|
||||||
|
response.response_type = intent.IntentResponseType.QUERY_ANSWER
|
||||||
|
response.async_set_states(matched_states=match_result.states)
|
||||||
|
return response
|
||||||
|
|
||||||
|
# Look for climate devices
|
||||||
match_constraints = intent.MatchTargetsConstraints(
|
match_constraints = intent.MatchTargetsConstraints(
|
||||||
name=name,
|
name=name,
|
||||||
area_name=area,
|
area_name=area,
|
||||||
|
floor_name=floor_name,
|
||||||
domains=[CLIMATE_DOMAIN],
|
domains=[CLIMATE_DOMAIN],
|
||||||
assistant=intent_obj.assistant,
|
assistant=intent_obj.assistant,
|
||||||
|
single_target=True,
|
||||||
|
)
|
||||||
|
match_result = intent.async_match_targets(
|
||||||
|
hass, match_constraints, match_preferences
|
||||||
)
|
)
|
||||||
match_result = intent.async_match_targets(hass, match_constraints)
|
|
||||||
if not match_result.is_match:
|
if not match_result.is_match:
|
||||||
raise intent.MatchFailedError(
|
raise intent.MatchFailedError(
|
||||||
result=match_result, constraints=match_constraints
|
result=match_result, constraints=match_constraints
|
||||||
|
@ -507,12 +507,22 @@ def _add_areas(
|
|||||||
candidate.area = areas.async_get_area(candidate.device.area_id)
|
candidate.area = areas.async_get_area(candidate.device.area_id)
|
||||||
|
|
||||||
|
|
||||||
|
def _default_area_candidate_filter(
|
||||||
|
candidate: MatchTargetsCandidate, possible_area_ids: Collection[str]
|
||||||
|
) -> bool:
|
||||||
|
"""Keep candidates in the possible areas."""
|
||||||
|
return (candidate.area is not None) and (candidate.area.id in possible_area_ids)
|
||||||
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_match_targets( # noqa: C901
|
def async_match_targets( # noqa: C901
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
constraints: MatchTargetsConstraints,
|
constraints: MatchTargetsConstraints,
|
||||||
preferences: MatchTargetsPreferences | None = None,
|
preferences: MatchTargetsPreferences | None = None,
|
||||||
states: list[State] | None = None,
|
states: list[State] | None = None,
|
||||||
|
area_candidate_filter: Callable[
|
||||||
|
[MatchTargetsCandidate, Collection[str]], bool
|
||||||
|
] = _default_area_candidate_filter,
|
||||||
) -> MatchTargetsResult:
|
) -> MatchTargetsResult:
|
||||||
"""Match entities based on constraints in order to handle an intent."""
|
"""Match entities based on constraints in order to handle an intent."""
|
||||||
preferences = preferences or MatchTargetsPreferences()
|
preferences = preferences or MatchTargetsPreferences()
|
||||||
@ -623,9 +633,7 @@ def async_match_targets( # noqa: C901
|
|||||||
}
|
}
|
||||||
|
|
||||||
candidates = [
|
candidates = [
|
||||||
c
|
c for c in candidates if area_candidate_filter(c, possible_area_ids)
|
||||||
for c in candidates
|
|
||||||
if (c.area is not None) and (c.area.id in possible_area_ids)
|
|
||||||
]
|
]
|
||||||
if not candidates:
|
if not candidates:
|
||||||
return MatchTargetsResult(
|
return MatchTargetsResult(
|
||||||
@ -649,9 +657,7 @@ def async_match_targets( # noqa: C901
|
|||||||
# May be constrained by floors above
|
# May be constrained by floors above
|
||||||
possible_area_ids.intersection_update(matching_area_ids)
|
possible_area_ids.intersection_update(matching_area_ids)
|
||||||
candidates = [
|
candidates = [
|
||||||
c
|
c for c in candidates if area_candidate_filter(c, possible_area_ids)
|
||||||
for c in candidates
|
|
||||||
if (c.area is not None) and (c.area.id in possible_area_ids)
|
|
||||||
]
|
]
|
||||||
if not candidates:
|
if not candidates:
|
||||||
return MatchTargetsResult(
|
return MatchTargetsResult(
|
||||||
@ -701,7 +707,7 @@ def async_match_targets( # noqa: C901
|
|||||||
group_candidates = [
|
group_candidates = [
|
||||||
c
|
c
|
||||||
for c in group_candidates
|
for c in group_candidates
|
||||||
if (c.area is not None) and (c.area.id == preferences.area_id)
|
if area_candidate_filter(c, {preferences.area_id})
|
||||||
]
|
]
|
||||||
if len(group_candidates) < 2:
|
if len(group_candidates) < 2:
|
||||||
# Disambiguated by area
|
# Disambiguated by area
|
||||||
@ -747,7 +753,7 @@ def async_match_targets( # noqa: C901
|
|||||||
if preferences.area_id:
|
if preferences.area_id:
|
||||||
# Filter by area
|
# Filter by area
|
||||||
filtered_candidates = [
|
filtered_candidates = [
|
||||||
c for c in candidates if c.area and (c.area.id == preferences.area_id)
|
c for c in candidates if area_candidate_filter(c, {preferences.area_id})
|
||||||
]
|
]
|
||||||
|
|
||||||
if (len(filtered_candidates) > 1) and preferences.floor_id:
|
if (len(filtered_candidates) > 1) and preferences.floor_id:
|
||||||
|
@ -14,10 +14,16 @@ from homeassistant.components.climate import (
|
|||||||
HVACMode,
|
HVACMode,
|
||||||
)
|
)
|
||||||
from homeassistant.components.homeassistant.exposed_entities import async_expose_entity
|
from homeassistant.components.homeassistant.exposed_entities import async_expose_entity
|
||||||
|
from homeassistant.components.sensor import SensorDeviceClass
|
||||||
from homeassistant.config_entries import ConfigEntry, ConfigFlow
|
from homeassistant.config_entries import ConfigEntry, ConfigFlow
|
||||||
from homeassistant.const import Platform, UnitOfTemperature
|
from homeassistant.const import ATTR_DEVICE_CLASS, Platform, UnitOfTemperature
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers import area_registry as ar, entity_registry as er, intent
|
from homeassistant.helpers import (
|
||||||
|
area_registry as ar,
|
||||||
|
entity_registry as er,
|
||||||
|
floor_registry as fr,
|
||||||
|
intent,
|
||||||
|
)
|
||||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
@ -131,6 +137,7 @@ async def test_get_temperature(
|
|||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
area_registry: ar.AreaRegistry,
|
area_registry: ar.AreaRegistry,
|
||||||
entity_registry: er.EntityRegistry,
|
entity_registry: er.EntityRegistry,
|
||||||
|
floor_registry: fr.FloorRegistry,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test HassClimateGetTemperature intent."""
|
"""Test HassClimateGetTemperature intent."""
|
||||||
assert await async_setup_component(hass, "homeassistant", {})
|
assert await async_setup_component(hass, "homeassistant", {})
|
||||||
@ -157,29 +164,133 @@ async def test_get_temperature(
|
|||||||
# Add climate entities to different areas:
|
# Add climate entities to different areas:
|
||||||
# climate_1 => living room
|
# climate_1 => living room
|
||||||
# climate_2 => bedroom
|
# climate_2 => bedroom
|
||||||
# nothing in office
|
# nothing in bathroom
|
||||||
|
# nothing in office yet
|
||||||
|
# nothing in attic yet
|
||||||
living_room_area = area_registry.async_create(name="Living Room")
|
living_room_area = area_registry.async_create(name="Living Room")
|
||||||
bedroom_area = area_registry.async_create(name="Bedroom")
|
bedroom_area = area_registry.async_create(name="Bedroom")
|
||||||
office_area = area_registry.async_create(name="Office")
|
office_area = area_registry.async_create(name="Office")
|
||||||
|
attic_area = area_registry.async_create(name="Attic")
|
||||||
|
bathroom_area = area_registry.async_create(name="Bathroom")
|
||||||
|
|
||||||
entity_registry.async_update_entity(
|
entity_registry.async_update_entity(
|
||||||
climate_1.entity_id, area_id=living_room_area.id
|
climate_1.entity_id, area_id=living_room_area.id
|
||||||
)
|
)
|
||||||
entity_registry.async_update_entity(climate_2.entity_id, area_id=bedroom_area.id)
|
entity_registry.async_update_entity(climate_2.entity_id, area_id=bedroom_area.id)
|
||||||
|
|
||||||
# First climate entity will be selected (no area)
|
# Put areas on different floors:
|
||||||
response = await intent.async_handle(
|
# first floor => living room and office
|
||||||
|
# 2nd floor => bedroom
|
||||||
|
# 3rd floor => attic
|
||||||
|
floor_registry = fr.async_get(hass)
|
||||||
|
first_floor = floor_registry.async_create("First floor")
|
||||||
|
living_room_area = area_registry.async_update(
|
||||||
|
living_room_area.id, floor_id=first_floor.floor_id
|
||||||
|
)
|
||||||
|
office_area = area_registry.async_update(
|
||||||
|
office_area.id, floor_id=first_floor.floor_id
|
||||||
|
)
|
||||||
|
|
||||||
|
second_floor = floor_registry.async_create("Second floor")
|
||||||
|
bedroom_area = area_registry.async_update(
|
||||||
|
bedroom_area.id, floor_id=second_floor.floor_id
|
||||||
|
)
|
||||||
|
bathroom_area = area_registry.async_update(
|
||||||
|
bathroom_area.id, floor_id=second_floor.floor_id
|
||||||
|
)
|
||||||
|
|
||||||
|
third_floor = floor_registry.async_create("Third floor")
|
||||||
|
attic_area = area_registry.async_update(
|
||||||
|
attic_area.id, floor_id=third_floor.floor_id
|
||||||
|
)
|
||||||
|
|
||||||
|
# Add temperature sensors to each area that should *not* be selected
|
||||||
|
for area in (living_room_area, office_area, bedroom_area, attic_area):
|
||||||
|
wrong_temperature_entry = entity_registry.async_get_or_create(
|
||||||
|
"sensor", "test", f"wrong_temperature_{area.id}"
|
||||||
|
)
|
||||||
|
hass.states.async_set(
|
||||||
|
wrong_temperature_entry.entity_id,
|
||||||
|
"10.0",
|
||||||
|
{
|
||||||
|
ATTR_TEMPERATURE: "Temperature",
|
||||||
|
ATTR_DEVICE_CLASS: SensorDeviceClass.TEMPERATURE,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
entity_registry.async_update_entity(
|
||||||
|
wrong_temperature_entry.entity_id, area_id=area.id
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create temperature sensor and assign them to the office/attic
|
||||||
|
office_temperature_id = "sensor.office_temperature"
|
||||||
|
attic_temperature_id = "sensor.attic_temperature"
|
||||||
|
hass.states.async_set(
|
||||||
|
office_temperature_id,
|
||||||
|
"15.5",
|
||||||
|
{
|
||||||
|
ATTR_TEMPERATURE: "Temperature",
|
||||||
|
ATTR_DEVICE_CLASS: SensorDeviceClass.TEMPERATURE,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
office_area = area_registry.async_update(
|
||||||
|
office_area.id, temperature_entity_id=office_temperature_id
|
||||||
|
)
|
||||||
|
|
||||||
|
hass.states.async_set(
|
||||||
|
attic_temperature_id,
|
||||||
|
"18.1",
|
||||||
|
{
|
||||||
|
ATTR_TEMPERATURE: "Temperature",
|
||||||
|
ATTR_DEVICE_CLASS: SensorDeviceClass.TEMPERATURE,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
attic_area = area_registry.async_update(
|
||||||
|
attic_area.id, temperature_entity_id=attic_temperature_id
|
||||||
|
)
|
||||||
|
|
||||||
|
# Multiple climate entities match (error)
|
||||||
|
with pytest.raises(intent.MatchFailedError) as error:
|
||||||
|
await intent.async_handle(
|
||||||
hass,
|
hass,
|
||||||
"test",
|
"test",
|
||||||
intent.INTENT_GET_TEMPERATURE,
|
intent.INTENT_GET_TEMPERATURE,
|
||||||
{},
|
{},
|
||||||
assistant=conversation.DOMAIN,
|
assistant=conversation.DOMAIN,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Exception should contain details of what we tried to match
|
||||||
|
assert isinstance(error.value, intent.MatchFailedError)
|
||||||
|
assert (
|
||||||
|
error.value.result.no_match_reason == intent.MatchFailedReason.MULTIPLE_TARGETS
|
||||||
|
)
|
||||||
|
|
||||||
|
# Select by area (office_temperature)
|
||||||
|
response = await intent.async_handle(
|
||||||
|
hass,
|
||||||
|
"test",
|
||||||
|
intent.INTENT_GET_TEMPERATURE,
|
||||||
|
{"area": {"value": office_area.name}},
|
||||||
|
assistant=conversation.DOMAIN,
|
||||||
|
)
|
||||||
assert response.response_type == intent.IntentResponseType.QUERY_ANSWER
|
assert response.response_type == intent.IntentResponseType.QUERY_ANSWER
|
||||||
assert response.matched_states
|
assert len(response.matched_states) == 1
|
||||||
assert response.matched_states[0].entity_id == climate_1.entity_id
|
assert response.matched_states[0].entity_id == office_temperature_id
|
||||||
state = response.matched_states[0]
|
state = response.matched_states[0]
|
||||||
assert state.attributes["current_temperature"] == 10.0
|
assert state.state == "15.5"
|
||||||
|
|
||||||
|
# Select by preferred area (attic_temperature)
|
||||||
|
response = await intent.async_handle(
|
||||||
|
hass,
|
||||||
|
"test",
|
||||||
|
intent.INTENT_GET_TEMPERATURE,
|
||||||
|
{"preferred_area_id": {"value": attic_area.id}},
|
||||||
|
assistant=conversation.DOMAIN,
|
||||||
|
)
|
||||||
|
assert response.response_type == intent.IntentResponseType.QUERY_ANSWER
|
||||||
|
assert len(response.matched_states) == 1
|
||||||
|
assert response.matched_states[0].entity_id == attic_temperature_id
|
||||||
|
state = response.matched_states[0]
|
||||||
|
assert state.state == "18.1"
|
||||||
|
|
||||||
# Select by area (climate_2)
|
# Select by area (climate_2)
|
||||||
response = await intent.async_handle(
|
response = await intent.async_handle(
|
||||||
@ -215,7 +326,7 @@ async def test_get_temperature(
|
|||||||
hass,
|
hass,
|
||||||
"test",
|
"test",
|
||||||
intent.INTENT_GET_TEMPERATURE,
|
intent.INTENT_GET_TEMPERATURE,
|
||||||
{"area": {"value": office_area.name}},
|
{"area": {"value": bathroom_area.name}},
|
||||||
assistant=conversation.DOMAIN,
|
assistant=conversation.DOMAIN,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -224,7 +335,7 @@ async def test_get_temperature(
|
|||||||
assert error.value.result.no_match_reason == intent.MatchFailedReason.AREA
|
assert error.value.result.no_match_reason == intent.MatchFailedReason.AREA
|
||||||
constraints = error.value.constraints
|
constraints = error.value.constraints
|
||||||
assert constraints.name is None
|
assert constraints.name is None
|
||||||
assert constraints.area_name == office_area.name
|
assert constraints.area_name == bathroom_area.name
|
||||||
assert constraints.domains and (set(constraints.domains) == {CLIMATE_DOMAIN})
|
assert constraints.domains and (set(constraints.domains) == {CLIMATE_DOMAIN})
|
||||||
assert constraints.device_classes is None
|
assert constraints.device_classes is None
|
||||||
|
|
||||||
@ -262,6 +373,48 @@ async def test_get_temperature(
|
|||||||
assert constraints.domains and (set(constraints.domains) == {CLIMATE_DOMAIN})
|
assert constraints.domains and (set(constraints.domains) == {CLIMATE_DOMAIN})
|
||||||
assert constraints.device_classes is None
|
assert constraints.device_classes is None
|
||||||
|
|
||||||
|
# Select by floor (climate_1)
|
||||||
|
response = await intent.async_handle(
|
||||||
|
hass,
|
||||||
|
"test",
|
||||||
|
intent.INTENT_GET_TEMPERATURE,
|
||||||
|
{"floor": {"value": first_floor.name}},
|
||||||
|
assistant=conversation.DOMAIN,
|
||||||
|
)
|
||||||
|
assert response.response_type == intent.IntentResponseType.QUERY_ANSWER
|
||||||
|
assert len(response.matched_states) == 1
|
||||||
|
assert response.matched_states[0].entity_id == climate_1.entity_id
|
||||||
|
state = response.matched_states[0]
|
||||||
|
assert state.attributes["current_temperature"] == 10.0
|
||||||
|
|
||||||
|
# Select by preferred area (climate_2)
|
||||||
|
response = await intent.async_handle(
|
||||||
|
hass,
|
||||||
|
"test",
|
||||||
|
intent.INTENT_GET_TEMPERATURE,
|
||||||
|
{"preferred_area_id": {"value": bedroom_area.id}},
|
||||||
|
assistant=conversation.DOMAIN,
|
||||||
|
)
|
||||||
|
assert response.response_type == intent.IntentResponseType.QUERY_ANSWER
|
||||||
|
assert len(response.matched_states) == 1
|
||||||
|
assert response.matched_states[0].entity_id == climate_2.entity_id
|
||||||
|
state = response.matched_states[0]
|
||||||
|
assert state.attributes["current_temperature"] == 22.0
|
||||||
|
|
||||||
|
# Select by preferred floor (climate_1)
|
||||||
|
response = await intent.async_handle(
|
||||||
|
hass,
|
||||||
|
"test",
|
||||||
|
intent.INTENT_GET_TEMPERATURE,
|
||||||
|
{"preferred_floor_id": {"value": first_floor.floor_id}},
|
||||||
|
assistant=conversation.DOMAIN,
|
||||||
|
)
|
||||||
|
assert response.response_type == intent.IntentResponseType.QUERY_ANSWER
|
||||||
|
assert len(response.matched_states) == 1
|
||||||
|
assert response.matched_states[0].entity_id == climate_1.entity_id
|
||||||
|
state = response.matched_states[0]
|
||||||
|
assert state.attributes["current_temperature"] == 10.0
|
||||||
|
|
||||||
|
|
||||||
async def test_get_temperature_no_entities(
|
async def test_get_temperature_no_entities(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user