mirror of
https://github.com/home-assistant/core.git
synced 2025-08-13 15:30:03 +00:00
.devcontainer
.github
.vscode
docs
homeassistant
machine
pylint
rootfs
script
tests
auth
backports
components
abode
accuweather
acmeda
adax
adguard
advantage_air
aemet
agent_dvr
air_quality
airly
airnow
airq
airthings
airthings_ble
airtouch4
airvisual
airvisual_pro
airzone
aladdin_connect
alarm_control_panel
alarmdecoder
alert
alexa
__init__.py
test_auth.py
test_capabilities.py
test_common.py
test_entities.py
test_flash_briefings.py
test_init.py
test_intent.py
test_smart_home.py
test_smart_home_http.py
test_state_report.py
amberelectric
ambiclimate
ambient_station
analytics
android_ip_webcam
androidtv
anthemav
apache_kafka
apcupsd
api
api_streams
apple_tv
application_credentials
apprise
aprs
aranet
arcam_fmj
aseko_pool_live
asuswrt
atag
august
aurora
aurora_abb_powerone
aussie_broadband
auth
automation
awair
aws
axis
azure_devops
azure_event_hub
backup
baf
balboa
bayesian
binary_sensor
blackbird
blebox
blink
bluemaestro
blueprint
bluetooth
bluetooth_adapters
bluetooth_le_tracker
bmw_connected_drive
bond
bosch_shc
braviatv
broadlink
brother
brunt
bsblan
bthome
buienradar
button
caldav
calendar
camera
canary
cast
cert_expiry
clicksend_tts
climate
cloud
cloudflare
co2signal
coinbase
color_extractor
comfoconnect
command_line
compensation
config
configurator
control4
conversation
coolmaster
coronavirus
counter
cover
cpuspeed
crownstone
daikin
darksky
datadog
debugpy
deconz
default_config
deluge
demo
denonavr
derivative
device_automation
device_sun_light_trigger
device_tracker
devolo_home_control
devolo_home_network
dexcom
dhcp
diagnostics
dialogflow
directv
discord
discovery
dlink
dlna_dmr
dlna_dms
dnsip
doorbird
dormakaba_dkey
dsmr
dsmr_reader
dte_energy_bridge
duckdns
dunehd
dynalite
eafm
easyenergy
ecobee
econet
ecowitt
edl21
efergy
eight_sleep
elgato
elkm1
elmax
emonitor
emulated_hue
emulated_kasa
emulated_roku
energy
energyzero
enocean
enphase_envoy
environment_canada
epson
escea
esphome
eufylife_ble
everlights
evil_genius_labs
ezviz
faa_delays
facebook
facebox
fail2ban
fan
feedreader
ffmpeg
fibaro
fido
file
file_upload
filesize
filter
fireservicerota
firmata
fivem
fjaraskupan
flic
flick_electric
flipr
flo
flume
flux
flux_led
folder
folder_watcher
foobot
forecast_solar
forked_daapd
foscam
freebox
freedns
freedompro
fritz
fritzbox
fritzbox_callmonitor
fronius
frontend
fully_kiosk
garages_amsterdam
gdacs
generic
generic_hygrostat
generic_thermostat
geo_json_events
geo_location
geo_rss_events
geocaching
geofency
geonetnz_quakes
geonetnz_volcano
gios
github
glances
goalzero
gogogate2
goodwe
google
google_assistant
google_assistant_sdk
google_domains
google_mail
google_pubsub
google_sheets
google_translate
google_travel_time
google_wifi
govee_ble
gpslogger
graphite
gree
greeneye_monitor
group
growatt_server
guardian
habitica
hardkernel
hardware
harmony
hassio
hddtemp
hdmi_cec
heos
here_travel_time
hisense_aehw4a1
history
history_stats
hive
hlk_sw16
home_connect
home_plus_control
homeassistant
homeassistant_alerts
homeassistant_hardware
homeassistant_sky_connect
homeassistant_yellow
homekit
homekit_controller
homematic
homematicip_cloud
homewizard
honeywell
html5
http
huawei_lte
hue
huisbaasje
humidifier
hunterdouglas_powerview
hvv_departures
hyperion
ialarm
iaqualink
ibeacon
icloud
ifttt
ign_sismologia
image_processing
image_upload
imap
imap_email_content
influxdb
inkbird
input_boolean
input_button
input_datetime
input_number
input_select
input_text
insteon
integration
intellifire
intent
intent_script
ios
iotawatt
ipma
ipp
iqvia
islamic_prayer_times
iss
isy994
izone
jellyfin
jewish_calendar
juicenet
justnimbus
kaleidescape
keenetic_ndms2
kegtron
keymitt_ble
kira
kitchen_sink
kmtronic
knx
kodi
konnected
kostal_plenticore
kraken
kulersky
lacrosse_view
lametric
landisgyr_heat_meter
lastfm
launch_library
laundrify
lcn
ld2410_ble
led_ble
lg_soundbar
lidarr
life360
lifx
light
litejet
litterrobot
livisi
local_calendar
local_file
local_ip
locative
lock
logbook
logentries
logger
logi_circle
london_air
lookin
lovelace
luftdaten
lutron_caseta
lyric
mailbox
mailgun
manual
manual_mqtt
marytts
matter
maxcube
mazda
meater
media_player
media_source
melcloud
melissa
melnor
meraki
met
met_eireann
meteo_france
meteoclimatic
metoffice
mfi
microsoft_face
microsoft_face_detect
microsoft_face_identify
mikrotik
mill
min_max
minecraft_server
minio
mjpeg
moat
mobile_app
mochad
modbus
modem_callerid
modern_forms
moehlenhoff_alpha2
mold_indicator
monoprice
moon
mopeka
motion_blinds
motioneye
mqtt
mqtt_eventstream
mqtt_json
mqtt_room
mqtt_statestream
mullvad
mutesync
my
myq
mysensors
mythicbeastsdns
nam
namecheapdns
nanoleaf
neato
ness_alarm
nest
netatmo
netgear
network
nexia
nextbus
nextdns
nfandroidtv
nibe_heatpump
nightscout
nina
nmap_tracker
no_ip
nobo_hub
notify
notify_events
notion
nsw_fuel_station
nsw_rural_fire_service_feed
nuheat
nuki
numato
number
nut
nws
nx584
nzbget
obihai
octoprint
omnilogic
onboarding
oncue
ondilo_ico
onewire
onvif
open_meteo
openai_conversation
openalpr_cloud
openerz
openexchangerates
opengarage
openhardwaremonitor
opentherm_gw
openuv
openweathermap
opnsense
oralb
otbr
overkiz
ovo_energy
owntracks
p1_monitor
panasonic_viera
panel_custom
panel_iframe
peco
persistent_notification
person
philips_js
pi_hole
picnic
pilight
ping
pjlink
plaato
plant
plex
plugwise
plum_lightpad
point
poolsense
powerwall
profiler
progettihwsw
prometheus
prosegur
proximity
prusalink
ps4
pure_energie
purpleair
push
pushbullet
pushover
pvoutput
pvpc_hourly_pricing
python_script
qingping
qld_bushfire
qnap_qsw
qwikswitch
rachio
radarr
radio_browser
radiotherm
rainbird
rainforest_eagle
rainmachine
random
raspberry_pi
rdw
recollect_waste
recorder
reddit
remember_the_milk
remote
renault
reolink
repairs
rest
rest_command
rflink
rfxtrx
rhasspy
ridwell
ring
risco
rituals_perfume_genie
rmvtransport
roku
roomba
roon
rpi_power
rss_feed_template
rtsp_to_webrtc
ruckus_unleashed
ruuvi_gateway
ruuvitag_ble
rympro
sabnzbd
safe_mode
samsungtv
scene
schedule
scrape
screenlogic
script
search
season
select
sense
senseme
sensibo
sensirion_ble
sensor
sensorpro
sensorpush
sentry
senz
seventeentrack
sfr_box
sharkiq
shell_command
shelly
shopping_list
sia
sigfox
sighthound
signal_messenger
simplepush
simplisafe
simulated
siren
skybell
slack
sleepiq
slimproto
sma
smappee
smart_meter_texas
smartthings
smarttub
smhi
smtp
snips
snmp
snooz
solaredge
solarlog
solax
soma
somfy_mylink
sonarr
songpal
sonos
soundtouch
spaceapi
spc
speedtestdotnet
spider
spotify
sql
squeezebox
srp_energy
ssdp
starline
starlink
startca
statistics
statsd
steam_online
steamist
stookalert
stookwijzer
stream
stt
subaru
sun
surepetcare
switch
switch_as_x
switchbee
switchbot
switcher_kis
syncthing
syncthru
synology_dsm
system_bridge
system_health
system_log
tado
tag
tailscale
tankerkoenig
tasmota
tautulli
tcp
telegram
telegram_bot
tellduslive
template
tesla_wall_connector
text
thermobeacon
thermopro
thread
threshold
tibber
tile
tilt_ble
time_date
timer
tod
todoist
tolo
tomato
tomorrowio
toon
totalconnect
tplink
tplink_omada
traccar
trace
tractive
tradfri
trafikverket_ferry
trafikverket_train
trafikverket_weatherstation
transmission
transport_nsw
trend
tts
tuya
twentemilieu
twilio
twinkly
twitch
uk_transport
ukraine_alarm
unifi
unifi_direct
unifiprotect
universal
upb
upcloud
update
upnp
uptime
uptimerobot
usb
usgs_earthquakes_feed
utility_meter
uvc
vacuum
vallox
velbus
venstar
vera
verisure
version
vesync
vicare
vilfo
vizio
vlc_telnet
voicerss
volumio
volvooncall
vulcan
vultr
wake_on_lan
wallbox
water_heater
watttime
waze_travel_time
weather
webhook
webostv
websocket_api
wemo
whirlpool
whois
wiffi
wilight
withings
wiz
wled
wolflink
workday
worldclock
ws66i
wsdot
xbox
xiaomi
xiaomi_aqara
xiaomi_ble
xiaomi_miio
yale_smart_alarm
yalexs_ble
yamaha
yamaha_musiccast
yandex_transport
yandextts
yeelight
yolink
youless
zamg
zeroconf
zerproc
zeversolar
zha
zodiac
zone
zwave_js
zwave_me
__init__.py
conftest.py
fixtures
hassfest
helpers
pylint
resources
scripts
test_util
testing_config
util
__init__.py
asyncio_legacy.py
bandit.yaml
common.py
conftest.py
ignore_uncaught_exceptions.py
ruff.toml
syrupy.py
test_bootstrap.py
test_config.py
test_config_entries.py
test_core.py
test_data_entry_flow.py
test_exceptions.py
test_loader.py
test_main.py
test_requirements.py
test_runner.py
test_setup.py
test_test_fixtures.py
typing.py
.core_files.yaml
.coveragerc
.dockerignore
.gitattributes
.gitignore
.hadolint.yaml
.pre-commit-config.yaml
.prettierignore
.readthedocs.yml
.strict-typing
.yamllint
CLA.md
CODEOWNERS
CODE_OF_CONDUCT.md
CONTRIBUTING.md
Dockerfile
Dockerfile.dev
LICENSE.md
MANIFEST.in
README.rst
build.yaml
codecov.yml
mypy.ini
pyproject.toml
requirements.txt
requirements_all.txt
requirements_docs.txt
requirements_test.txt
requirements_test_all.txt
requirements_test_pre_commit.txt
setup.cfg
540 lines
19 KiB
Python
540 lines
19 KiB
Python
"""The tests for the Alexa component."""
|
|
from http import HTTPStatus
|
|
import json
|
|
|
|
import pytest
|
|
|
|
from homeassistant.components import alexa
|
|
from homeassistant.components.alexa import intent
|
|
from homeassistant.const import CONTENT_TYPE_JSON
|
|
from homeassistant.core import HomeAssistant, callback
|
|
from homeassistant.setup import async_setup_component
|
|
|
|
SESSION_ID = "amzn1.echo-api.session.0000000-0000-0000-0000-00000000000"
|
|
APPLICATION_ID = "amzn1.echo-sdk-ams.app.000000-d0ed-0000-ad00-000000d00ebe"
|
|
APPLICATION_ID_SESSION_OPEN = (
|
|
"amzn1.echo-sdk-ams.app.000000-d0ed-0000-ad00-000000d00ebf"
|
|
)
|
|
REQUEST_ID = "amzn1.echo-api.request.0000000-0000-0000-0000-00000000000"
|
|
AUTHORITY_ID = "amzn1.er-authority.000000-d0ed-0000-ad00-000000d00ebe.ZODIAC"
|
|
BUILTIN_AUTH_ID = "amzn1.er-authority.000000-d0ed-0000-ad00-000000d00ebe.TEST"
|
|
|
|
# pylint: disable=invalid-name
|
|
calls = []
|
|
|
|
NPR_NEWS_MP3_URL = "https://pd.npr.org/anon.npr-mp3/npr/news/newscast.mp3"
|
|
|
|
|
|
@pytest.fixture
|
|
def alexa_client(event_loop, hass, hass_client):
|
|
"""Initialize a Home Assistant server for testing this module."""
|
|
loop = event_loop
|
|
|
|
@callback
|
|
def mock_service(call):
|
|
calls.append(call)
|
|
|
|
hass.services.async_register("test", "alexa", mock_service)
|
|
|
|
assert loop.run_until_complete(
|
|
async_setup_component(
|
|
hass,
|
|
alexa.DOMAIN,
|
|
{
|
|
# Key is here to verify we allow other keys in config too
|
|
"homeassistant": {},
|
|
"alexa": {},
|
|
},
|
|
)
|
|
)
|
|
assert loop.run_until_complete(
|
|
async_setup_component(
|
|
hass,
|
|
"intent_script",
|
|
{
|
|
"intent_script": {
|
|
"WhereAreWeIntent": {
|
|
"speech": {
|
|
"type": "plain",
|
|
"text": """
|
|
{%- if is_state("device_tracker.paulus", "home")
|
|
and is_state("device_tracker.anne_therese",
|
|
"home") -%}
|
|
You are both home, you silly
|
|
{%- else -%}
|
|
Anne Therese is at {{
|
|
states("device_tracker.anne_therese")
|
|
}} and Paulus is at {{
|
|
states("device_tracker.paulus")
|
|
}}
|
|
{% endif %}
|
|
""",
|
|
}
|
|
},
|
|
"GetZodiacHoroscopeIntent": {
|
|
"speech": {
|
|
"type": "plain",
|
|
"text": "You told us your sign is {{ ZodiacSign }}.",
|
|
}
|
|
},
|
|
"AMAZON.PlaybackAction<object@MusicCreativeWork>": {
|
|
"speech": {
|
|
"type": "plain",
|
|
"text": "Playing {{ object_byArtist_name }}.",
|
|
}
|
|
},
|
|
"CallServiceIntent": {
|
|
"speech": {
|
|
"type": "plain",
|
|
"text": "Service called for {{ ZodiacSign }}",
|
|
},
|
|
"card": {
|
|
"type": "simple",
|
|
"title": "Card title for {{ ZodiacSign }}",
|
|
"content": "Card content: {{ ZodiacSign }}",
|
|
},
|
|
"action": {
|
|
"service": "test.alexa",
|
|
"data_template": {"hello": "{{ ZodiacSign }}"},
|
|
"entity_id": "switch.test",
|
|
},
|
|
},
|
|
APPLICATION_ID: {
|
|
"speech": {
|
|
"type": "plain",
|
|
"text": "LaunchRequest has been received.",
|
|
}
|
|
},
|
|
APPLICATION_ID_SESSION_OPEN: {
|
|
"speech": {
|
|
"type": "plain",
|
|
"text": "LaunchRequest has been received.",
|
|
},
|
|
"reprompt": {
|
|
"type": "plain",
|
|
"text": "LaunchRequest has been received.",
|
|
},
|
|
},
|
|
}
|
|
},
|
|
)
|
|
)
|
|
return loop.run_until_complete(hass_client())
|
|
|
|
|
|
def _intent_req(client, data=None):
|
|
return client.post(
|
|
intent.INTENTS_API_ENDPOINT,
|
|
data=json.dumps(data or {}),
|
|
headers={"content-type": CONTENT_TYPE_JSON},
|
|
)
|
|
|
|
|
|
async def test_intent_launch_request(alexa_client) -> None:
|
|
"""Test the launch of a request."""
|
|
data = {
|
|
"version": "1.0",
|
|
"session": {
|
|
"new": True,
|
|
"sessionId": SESSION_ID,
|
|
"application": {"applicationId": APPLICATION_ID},
|
|
"attributes": {},
|
|
"user": {"userId": "amzn1.account.AM3B00000000000000000000000"},
|
|
},
|
|
"request": {
|
|
"type": "LaunchRequest",
|
|
"requestId": REQUEST_ID,
|
|
"timestamp": "2015-05-13T12:34:56Z",
|
|
},
|
|
}
|
|
req = await _intent_req(alexa_client, data)
|
|
assert req.status == HTTPStatus.OK
|
|
data = await req.json()
|
|
text = data.get("response", {}).get("outputSpeech", {}).get("text")
|
|
assert text == "LaunchRequest has been received."
|
|
assert data.get("response", {}).get("shouldEndSession")
|
|
|
|
|
|
async def test_intent_launch_request_with_session_open(alexa_client) -> None:
|
|
"""Test the launch of a request."""
|
|
data = {
|
|
"version": "1.0",
|
|
"session": {
|
|
"new": True,
|
|
"sessionId": SESSION_ID,
|
|
"application": {"applicationId": APPLICATION_ID_SESSION_OPEN},
|
|
"attributes": {},
|
|
"user": {"userId": "amzn1.account.AM3B00000000000000000000000"},
|
|
},
|
|
"request": {
|
|
"type": "LaunchRequest",
|
|
"requestId": REQUEST_ID,
|
|
"timestamp": "2015-05-13T12:34:56Z",
|
|
},
|
|
}
|
|
req = await _intent_req(alexa_client, data)
|
|
assert req.status == HTTPStatus.OK
|
|
data = await req.json()
|
|
text = data.get("response", {}).get("outputSpeech", {}).get("text")
|
|
assert text == "LaunchRequest has been received."
|
|
text = (
|
|
data.get("response", {}).get("reprompt", {}).get("outputSpeech", {}).get("text")
|
|
)
|
|
assert text == "LaunchRequest has been received."
|
|
assert not data.get("response", {}).get("shouldEndSession")
|
|
|
|
|
|
async def test_intent_launch_request_not_configured(alexa_client) -> None:
|
|
"""Test the launch of a request."""
|
|
data = {
|
|
"version": "1.0",
|
|
"session": {
|
|
"new": True,
|
|
"sessionId": SESSION_ID,
|
|
"application": {
|
|
"applicationId": (
|
|
"amzn1.echo-sdk-ams.app.000000-d0ed-0000-ad00-000000d00000"
|
|
),
|
|
},
|
|
"attributes": {},
|
|
"user": {"userId": "amzn1.account.AM3B00000000000000000000000"},
|
|
},
|
|
"request": {
|
|
"type": "LaunchRequest",
|
|
"requestId": REQUEST_ID,
|
|
"timestamp": "2015-05-13T12:34:56Z",
|
|
},
|
|
}
|
|
req = await _intent_req(alexa_client, data)
|
|
assert req.status == HTTPStatus.OK
|
|
data = await req.json()
|
|
text = data.get("response", {}).get("outputSpeech", {}).get("text")
|
|
assert text == "This intent is not yet configured within Home Assistant."
|
|
|
|
|
|
async def test_intent_request_with_slots(alexa_client) -> None:
|
|
"""Test a request with slots."""
|
|
data = {
|
|
"version": "1.0",
|
|
"session": {
|
|
"new": False,
|
|
"sessionId": SESSION_ID,
|
|
"application": {"applicationId": APPLICATION_ID},
|
|
"attributes": {
|
|
"supportedHoroscopePeriods": {
|
|
"daily": True,
|
|
"weekly": False,
|
|
"monthly": False,
|
|
}
|
|
},
|
|
"user": {"userId": "amzn1.account.AM3B00000000000000000000000"},
|
|
},
|
|
"request": {
|
|
"type": "IntentRequest",
|
|
"requestId": REQUEST_ID,
|
|
"timestamp": "2015-05-13T12:34:56Z",
|
|
"intent": {
|
|
"name": "GetZodiacHoroscopeIntent",
|
|
"slots": {"ZodiacSign": {"name": "ZodiacSign", "value": "virgo"}},
|
|
},
|
|
},
|
|
}
|
|
req = await _intent_req(alexa_client, data)
|
|
assert req.status == HTTPStatus.OK
|
|
data = await req.json()
|
|
text = data.get("response", {}).get("outputSpeech", {}).get("text")
|
|
assert text == "You told us your sign is virgo."
|
|
|
|
|
|
async def test_intent_request_with_slots_and_synonym_resolution(alexa_client) -> None:
|
|
"""Test a request with slots and a name synonym."""
|
|
data = {
|
|
"version": "1.0",
|
|
"session": {
|
|
"new": False,
|
|
"sessionId": SESSION_ID,
|
|
"application": {"applicationId": APPLICATION_ID},
|
|
"attributes": {
|
|
"supportedHoroscopePeriods": {
|
|
"daily": True,
|
|
"weekly": False,
|
|
"monthly": False,
|
|
}
|
|
},
|
|
"user": {"userId": "amzn1.account.AM3B00000000000000000000000"},
|
|
},
|
|
"request": {
|
|
"type": "IntentRequest",
|
|
"requestId": REQUEST_ID,
|
|
"timestamp": "2015-05-13T12:34:56Z",
|
|
"intent": {
|
|
"name": "GetZodiacHoroscopeIntent",
|
|
"slots": {
|
|
"ZodiacSign": {
|
|
"name": "ZodiacSign",
|
|
"value": "V zodiac",
|
|
"resolutions": {
|
|
"resolutionsPerAuthority": [
|
|
{
|
|
"authority": AUTHORITY_ID,
|
|
"status": {"code": "ER_SUCCESS_MATCH"},
|
|
"values": [{"value": {"name": "Virgo"}}],
|
|
},
|
|
{
|
|
"authority": BUILTIN_AUTH_ID,
|
|
"status": {"code": "ER_SUCCESS_NO_MATCH"},
|
|
"values": [{"value": {"name": "Test"}}],
|
|
},
|
|
]
|
|
},
|
|
}
|
|
},
|
|
},
|
|
},
|
|
}
|
|
req = await _intent_req(alexa_client, data)
|
|
assert req.status == HTTPStatus.OK
|
|
data = await req.json()
|
|
text = data.get("response", {}).get("outputSpeech", {}).get("text")
|
|
assert text == "You told us your sign is Virgo."
|
|
|
|
|
|
async def test_intent_request_with_slots_and_multi_synonym_resolution(
|
|
alexa_client,
|
|
) -> None:
|
|
"""Test a request with slots and multiple name synonyms."""
|
|
data = {
|
|
"version": "1.0",
|
|
"session": {
|
|
"new": False,
|
|
"sessionId": SESSION_ID,
|
|
"application": {"applicationId": APPLICATION_ID},
|
|
"attributes": {
|
|
"supportedHoroscopePeriods": {
|
|
"daily": True,
|
|
"weekly": False,
|
|
"monthly": False,
|
|
}
|
|
},
|
|
"user": {"userId": "amzn1.account.AM3B00000000000000000000000"},
|
|
},
|
|
"request": {
|
|
"type": "IntentRequest",
|
|
"requestId": REQUEST_ID,
|
|
"timestamp": "2015-05-13T12:34:56Z",
|
|
"intent": {
|
|
"name": "GetZodiacHoroscopeIntent",
|
|
"slots": {
|
|
"ZodiacSign": {
|
|
"name": "ZodiacSign",
|
|
"value": "V zodiac",
|
|
"resolutions": {
|
|
"resolutionsPerAuthority": [
|
|
{
|
|
"authority": AUTHORITY_ID,
|
|
"status": {"code": "ER_SUCCESS_MATCH"},
|
|
"values": [{"value": {"name": "Virgo"}}],
|
|
},
|
|
{
|
|
"authority": BUILTIN_AUTH_ID,
|
|
"status": {"code": "ER_SUCCESS_MATCH"},
|
|
"values": [{"value": {"name": "Test"}}],
|
|
},
|
|
]
|
|
},
|
|
}
|
|
},
|
|
},
|
|
},
|
|
}
|
|
req = await _intent_req(alexa_client, data)
|
|
assert req.status == HTTPStatus.OK
|
|
data = await req.json()
|
|
text = data.get("response", {}).get("outputSpeech", {}).get("text")
|
|
assert text == "You told us your sign is V zodiac."
|
|
|
|
|
|
async def test_intent_request_with_slots_but_no_value(alexa_client) -> None:
|
|
"""Test a request with slots but no value."""
|
|
data = {
|
|
"version": "1.0",
|
|
"session": {
|
|
"new": False,
|
|
"sessionId": SESSION_ID,
|
|
"application": {"applicationId": APPLICATION_ID},
|
|
"attributes": {
|
|
"supportedHoroscopePeriods": {
|
|
"daily": True,
|
|
"weekly": False,
|
|
"monthly": False,
|
|
}
|
|
},
|
|
"user": {"userId": "amzn1.account.AM3B00000000000000000000000"},
|
|
},
|
|
"request": {
|
|
"type": "IntentRequest",
|
|
"requestId": REQUEST_ID,
|
|
"timestamp": "2015-05-13T12:34:56Z",
|
|
"intent": {
|
|
"name": "GetZodiacHoroscopeIntent",
|
|
"slots": {"ZodiacSign": {"name": "ZodiacSign"}},
|
|
},
|
|
},
|
|
}
|
|
req = await _intent_req(alexa_client, data)
|
|
assert req.status == HTTPStatus.OK
|
|
data = await req.json()
|
|
text = data.get("response", {}).get("outputSpeech", {}).get("text")
|
|
assert text == "You told us your sign is ."
|
|
|
|
|
|
async def test_intent_request_without_slots(hass: HomeAssistant, alexa_client) -> None:
|
|
"""Test a request without slots."""
|
|
data = {
|
|
"version": "1.0",
|
|
"session": {
|
|
"new": False,
|
|
"sessionId": SESSION_ID,
|
|
"application": {"applicationId": APPLICATION_ID},
|
|
"attributes": {
|
|
"supportedHoroscopePeriods": {
|
|
"daily": True,
|
|
"weekly": False,
|
|
"monthly": False,
|
|
}
|
|
},
|
|
"user": {"userId": "amzn1.account.AM3B00000000000000000000000"},
|
|
},
|
|
"request": {
|
|
"type": "IntentRequest",
|
|
"requestId": REQUEST_ID,
|
|
"timestamp": "2015-05-13T12:34:56Z",
|
|
"intent": {"name": "WhereAreWeIntent"},
|
|
},
|
|
}
|
|
req = await _intent_req(alexa_client, data)
|
|
assert req.status == HTTPStatus.OK
|
|
json = await req.json()
|
|
text = json.get("response", {}).get("outputSpeech", {}).get("text")
|
|
|
|
assert text == "Anne Therese is at unknown and Paulus is at unknown"
|
|
|
|
hass.states.async_set("device_tracker.paulus", "home")
|
|
hass.states.async_set("device_tracker.anne_therese", "home")
|
|
|
|
req = await _intent_req(alexa_client, data)
|
|
assert req.status == HTTPStatus.OK
|
|
json = await req.json()
|
|
text = json.get("response", {}).get("outputSpeech", {}).get("text")
|
|
assert text == "You are both home, you silly"
|
|
|
|
|
|
async def test_intent_request_calling_service(alexa_client) -> None:
|
|
"""Test a request for calling a service."""
|
|
data = {
|
|
"version": "1.0",
|
|
"session": {
|
|
"new": False,
|
|
"sessionId": SESSION_ID,
|
|
"application": {"applicationId": APPLICATION_ID},
|
|
"attributes": {},
|
|
"user": {"userId": "amzn1.account.AM3B00000000000000000000000"},
|
|
},
|
|
"request": {
|
|
"type": "IntentRequest",
|
|
"requestId": REQUEST_ID,
|
|
"timestamp": "2015-05-13T12:34:56Z",
|
|
"intent": {
|
|
"name": "CallServiceIntent",
|
|
"slots": {"ZodiacSign": {"name": "ZodiacSign", "value": "virgo"}},
|
|
},
|
|
},
|
|
}
|
|
call_count = len(calls)
|
|
req = await _intent_req(alexa_client, data)
|
|
assert req.status == HTTPStatus.OK
|
|
assert call_count + 1 == len(calls)
|
|
call = calls[-1]
|
|
assert call.domain == "test"
|
|
assert call.service == "alexa"
|
|
assert call.data.get("entity_id") == ["switch.test"]
|
|
assert call.data.get("hello") == "virgo"
|
|
|
|
data = await req.json()
|
|
assert data["response"]["card"]["title"] == "Card title for virgo"
|
|
assert data["response"]["card"]["content"] == "Card content: virgo"
|
|
assert data["response"]["outputSpeech"]["type"] == "PlainText"
|
|
assert data["response"]["outputSpeech"]["text"] == "Service called for virgo"
|
|
|
|
|
|
async def test_intent_session_ended_request(alexa_client) -> None:
|
|
"""Test the request for ending the session."""
|
|
data = {
|
|
"version": "1.0",
|
|
"session": {
|
|
"new": False,
|
|
"sessionId": SESSION_ID,
|
|
"application": {"applicationId": APPLICATION_ID},
|
|
"attributes": {
|
|
"supportedHoroscopePeriods": {
|
|
"daily": True,
|
|
"weekly": False,
|
|
"monthly": False,
|
|
}
|
|
},
|
|
"user": {"userId": "amzn1.account.AM3B00000000000000000000000"},
|
|
},
|
|
"request": {
|
|
"type": "SessionEndedRequest",
|
|
"requestId": REQUEST_ID,
|
|
"timestamp": "2015-05-13T12:34:56Z",
|
|
"reason": "USER_INITIATED",
|
|
},
|
|
}
|
|
|
|
req = await _intent_req(alexa_client, data)
|
|
assert req.status == HTTPStatus.OK
|
|
data = await req.json()
|
|
assert (
|
|
data["response"]["outputSpeech"]["text"]
|
|
== "This intent is not yet configured within Home Assistant."
|
|
)
|
|
|
|
|
|
async def test_intent_from_built_in_intent_library(alexa_client) -> None:
|
|
"""Test intents from the Built-in Intent Library."""
|
|
data = {
|
|
"request": {
|
|
"intent": {
|
|
"name": "AMAZON.PlaybackAction<object@MusicCreativeWork>",
|
|
"slots": {
|
|
"object.byArtist.name": {
|
|
"name": "object.byArtist.name",
|
|
"value": "the shins",
|
|
},
|
|
"object.composer.name": {"name": "object.composer.name"},
|
|
"object.contentSource": {"name": "object.contentSource"},
|
|
"object.era": {"name": "object.era"},
|
|
"object.genre": {"name": "object.genre"},
|
|
"object.name": {"name": "object.name"},
|
|
"object.owner.name": {"name": "object.owner.name"},
|
|
"object.select": {"name": "object.select"},
|
|
"object.sort": {"name": "object.sort"},
|
|
"object.type": {"name": "object.type", "value": "music"},
|
|
},
|
|
},
|
|
"timestamp": "2016-12-14T23:23:37Z",
|
|
"type": "IntentRequest",
|
|
"requestId": REQUEST_ID,
|
|
},
|
|
"session": {
|
|
"sessionId": SESSION_ID,
|
|
"application": {"applicationId": APPLICATION_ID},
|
|
},
|
|
}
|
|
req = await _intent_req(alexa_client, data)
|
|
assert req.status == HTTPStatus.OK
|
|
data = await req.json()
|
|
text = data.get("response", {}).get("outputSpeech", {}).get("text")
|
|
assert text == "Playing the shins."
|