mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 11:17:21 +00:00
Add HassGetWeather intent (#102613)
* Add HassGetWeather intent * Use async_match_states * Extend test coverage * Use get_entity * Update homeassistant/components/weather/intent.py * Fix state --------- Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
This commit is contained in:
parent
c92945ecd6
commit
45f1d50f03
85
homeassistant/components/weather/intent.py
Normal file
85
homeassistant/components/weather/intent.py
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
"""Intents for the weather integration."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from homeassistant.core import HomeAssistant, State
|
||||||
|
from homeassistant.helpers import intent
|
||||||
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
from homeassistant.helpers.entity_component import EntityComponent
|
||||||
|
|
||||||
|
from . import DOMAIN, WeatherEntity
|
||||||
|
|
||||||
|
INTENT_GET_WEATHER = "HassGetWeather"
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_intents(hass: HomeAssistant) -> None:
|
||||||
|
"""Set up the weather intents."""
|
||||||
|
intent.async_register(hass, GetWeatherIntent())
|
||||||
|
|
||||||
|
|
||||||
|
class GetWeatherIntent(intent.IntentHandler):
|
||||||
|
"""Handle GetWeather intents."""
|
||||||
|
|
||||||
|
intent_type = INTENT_GET_WEATHER
|
||||||
|
slot_schema = {vol.Optional("name"): cv.string}
|
||||||
|
|
||||||
|
async def async_handle(self, intent_obj: intent.Intent) -> intent.IntentResponse:
|
||||||
|
"""Handle the intent."""
|
||||||
|
hass = intent_obj.hass
|
||||||
|
slots = self.async_validate_slots(intent_obj.slots)
|
||||||
|
|
||||||
|
weather: WeatherEntity | None = None
|
||||||
|
weather_state: State | None = None
|
||||||
|
component: EntityComponent[WeatherEntity] = hass.data[DOMAIN]
|
||||||
|
entities = list(component.entities)
|
||||||
|
|
||||||
|
if "name" in slots:
|
||||||
|
# Named weather entity
|
||||||
|
weather_name = slots["name"]["value"]
|
||||||
|
|
||||||
|
# Find matching weather entity
|
||||||
|
matching_states = intent.async_match_states(
|
||||||
|
hass, name=weather_name, domains=[DOMAIN]
|
||||||
|
)
|
||||||
|
for maybe_weather_state in matching_states:
|
||||||
|
weather = component.get_entity(maybe_weather_state.entity_id)
|
||||||
|
if weather is not None:
|
||||||
|
weather_state = maybe_weather_state
|
||||||
|
break
|
||||||
|
|
||||||
|
if weather is None:
|
||||||
|
raise intent.IntentHandleError(
|
||||||
|
f"No weather entity named {weather_name}"
|
||||||
|
)
|
||||||
|
elif entities:
|
||||||
|
# First weather entity
|
||||||
|
weather = entities[0]
|
||||||
|
weather_name = weather.name
|
||||||
|
weather_state = hass.states.get(weather.entity_id)
|
||||||
|
|
||||||
|
if weather is None:
|
||||||
|
raise intent.IntentHandleError("No weather entity")
|
||||||
|
|
||||||
|
if weather_state is None:
|
||||||
|
raise intent.IntentHandleError(f"No state for weather: {weather.name}")
|
||||||
|
|
||||||
|
assert weather is not None
|
||||||
|
assert weather_state is not None
|
||||||
|
|
||||||
|
# Create response
|
||||||
|
response = intent_obj.create_response()
|
||||||
|
response.response_type = intent.IntentResponseType.QUERY_ANSWER
|
||||||
|
response.async_set_results(
|
||||||
|
success_results=[
|
||||||
|
intent.IntentResponseTarget(
|
||||||
|
type=intent.IntentResponseTargetType.ENTITY,
|
||||||
|
name=weather_name,
|
||||||
|
id=weather.entity_id,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
response.async_set_states(matched_states=[weather_state])
|
||||||
|
|
||||||
|
return response
|
108
tests/components/weather/test_intent.py
Normal file
108
tests/components/weather/test_intent.py
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
"""Test weather intents."""
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from homeassistant.components.weather import (
|
||||||
|
DOMAIN,
|
||||||
|
WeatherEntity,
|
||||||
|
intent as weather_intent,
|
||||||
|
)
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers import intent
|
||||||
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
|
|
||||||
|
async def test_get_weather(hass: HomeAssistant) -> None:
|
||||||
|
"""Test get weather for first entity and by name."""
|
||||||
|
assert await async_setup_component(hass, "weather", {"weather": {}})
|
||||||
|
|
||||||
|
entity1 = WeatherEntity()
|
||||||
|
entity1._attr_name = "Weather 1"
|
||||||
|
entity1.entity_id = "weather.test_1"
|
||||||
|
|
||||||
|
entity2 = WeatherEntity()
|
||||||
|
entity2._attr_name = "Weather 2"
|
||||||
|
entity2.entity_id = "weather.test_2"
|
||||||
|
|
||||||
|
await hass.data[DOMAIN].async_add_entities([entity1, entity2])
|
||||||
|
|
||||||
|
await weather_intent.async_setup_intents(hass)
|
||||||
|
|
||||||
|
# First entity will be chosen
|
||||||
|
response = await intent.async_handle(
|
||||||
|
hass, "test", weather_intent.INTENT_GET_WEATHER, {}
|
||||||
|
)
|
||||||
|
assert response.response_type == intent.IntentResponseType.QUERY_ANSWER
|
||||||
|
assert len(response.matched_states) == 1
|
||||||
|
state = response.matched_states[0]
|
||||||
|
assert state.entity_id == entity1.entity_id
|
||||||
|
|
||||||
|
# Named entity will be chosen
|
||||||
|
response = await intent.async_handle(
|
||||||
|
hass,
|
||||||
|
"test",
|
||||||
|
weather_intent.INTENT_GET_WEATHER,
|
||||||
|
{"name": {"value": "Weather 2"}},
|
||||||
|
)
|
||||||
|
assert response.response_type == intent.IntentResponseType.QUERY_ANSWER
|
||||||
|
assert len(response.matched_states) == 1
|
||||||
|
state = response.matched_states[0]
|
||||||
|
assert state.entity_id == entity2.entity_id
|
||||||
|
|
||||||
|
|
||||||
|
async def test_get_weather_wrong_name(hass: HomeAssistant) -> None:
|
||||||
|
"""Test get weather with the wrong name."""
|
||||||
|
assert await async_setup_component(hass, "weather", {"weather": {}})
|
||||||
|
|
||||||
|
entity1 = WeatherEntity()
|
||||||
|
entity1._attr_name = "Weather 1"
|
||||||
|
entity1.entity_id = "weather.test_1"
|
||||||
|
|
||||||
|
await hass.data[DOMAIN].async_add_entities([entity1])
|
||||||
|
|
||||||
|
await weather_intent.async_setup_intents(hass)
|
||||||
|
|
||||||
|
# Incorrect name
|
||||||
|
with pytest.raises(intent.IntentHandleError):
|
||||||
|
await intent.async_handle(
|
||||||
|
hass,
|
||||||
|
"test",
|
||||||
|
weather_intent.INTENT_GET_WEATHER,
|
||||||
|
{"name": {"value": "not the right name"}},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_get_weather_no_entities(hass: HomeAssistant) -> None:
|
||||||
|
"""Test get weather with no weather entities."""
|
||||||
|
assert await async_setup_component(hass, "weather", {"weather": {}})
|
||||||
|
await weather_intent.async_setup_intents(hass)
|
||||||
|
|
||||||
|
# No weather entities
|
||||||
|
with pytest.raises(intent.IntentHandleError):
|
||||||
|
await intent.async_handle(hass, "test", weather_intent.INTENT_GET_WEATHER, {})
|
||||||
|
|
||||||
|
|
||||||
|
async def test_get_weather_no_state(hass: HomeAssistant) -> None:
|
||||||
|
"""Test get weather when state is not returned."""
|
||||||
|
assert await async_setup_component(hass, "weather", {"weather": {}})
|
||||||
|
|
||||||
|
entity1 = WeatherEntity()
|
||||||
|
entity1._attr_name = "Weather 1"
|
||||||
|
entity1.entity_id = "weather.test_1"
|
||||||
|
|
||||||
|
await hass.data[DOMAIN].async_add_entities([entity1])
|
||||||
|
|
||||||
|
await weather_intent.async_setup_intents(hass)
|
||||||
|
|
||||||
|
# Success with state
|
||||||
|
response = await intent.async_handle(
|
||||||
|
hass, "test", weather_intent.INTENT_GET_WEATHER, {}
|
||||||
|
)
|
||||||
|
assert response.response_type == intent.IntentResponseType.QUERY_ANSWER
|
||||||
|
|
||||||
|
# Failure without state
|
||||||
|
with patch("homeassistant.core.StateMachine.get", return_value=None), pytest.raises(
|
||||||
|
intent.IntentHandleError
|
||||||
|
):
|
||||||
|
await intent.async_handle(hass, "test", weather_intent.INTENT_GET_WEATHER, {})
|
Loading…
x
Reference in New Issue
Block a user