mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +00:00
[TTS] options support for service calls (#5436)
* [TTS] Support now options like voice and age on service. * Add unittest
This commit is contained in:
parent
074f9315d7
commit
2fff8a5a11
@ -5,6 +5,8 @@ For more details about this component, please refer to the documentation at
|
|||||||
https://home-assistant.io/components/tts/
|
https://home-assistant.io/components/tts/
|
||||||
"""
|
"""
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import ctypes
|
||||||
|
import functools as ft
|
||||||
import hashlib
|
import hashlib
|
||||||
import logging
|
import logging
|
||||||
import mimetypes
|
import mimetypes
|
||||||
@ -49,9 +51,11 @@ SERVICE_CLEAR_CACHE = 'clear_cache'
|
|||||||
ATTR_MESSAGE = 'message'
|
ATTR_MESSAGE = 'message'
|
||||||
ATTR_CACHE = 'cache'
|
ATTR_CACHE = 'cache'
|
||||||
ATTR_LANGUAGE = 'language'
|
ATTR_LANGUAGE = 'language'
|
||||||
|
ATTR_OPTIONS = 'options'
|
||||||
|
|
||||||
_RE_VOICE_FILE = re.compile(r"([a-f0-9]{40})_([^_]+)_([a-z]+)\.[a-z0-9]{3,4}")
|
_RE_VOICE_FILE = re.compile(
|
||||||
KEY_PATTERN = '{}_{}_{}'
|
r"([a-f0-9]{40})_([^_]+)_([^_]+)_([a-z]+)\.[a-z0-9]{3,4}")
|
||||||
|
KEY_PATTERN = '{0}_{1}_{2}_{3}'
|
||||||
|
|
||||||
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
|
PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
|
||||||
vol.Optional(CONF_CACHE, default=DEFAULT_CACHE): cv.boolean,
|
vol.Optional(CONF_CACHE, default=DEFAULT_CACHE): cv.boolean,
|
||||||
@ -60,12 +64,12 @@ PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({
|
|||||||
vol.All(vol.Coerce(int), vol.Range(min=60, max=57600)),
|
vol.All(vol.Coerce(int), vol.Range(min=60, max=57600)),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
SCHEMA_SERVICE_SAY = vol.Schema({
|
SCHEMA_SERVICE_SAY = vol.Schema({
|
||||||
vol.Required(ATTR_MESSAGE): cv.string,
|
vol.Required(ATTR_MESSAGE): cv.string,
|
||||||
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
|
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
|
||||||
vol.Optional(ATTR_CACHE): cv.boolean,
|
vol.Optional(ATTR_CACHE): cv.boolean,
|
||||||
vol.Optional(ATTR_LANGUAGE): cv.string
|
vol.Optional(ATTR_LANGUAGE): cv.string,
|
||||||
|
vol.Optional(ATTR_OPTIONS): dict,
|
||||||
})
|
})
|
||||||
|
|
||||||
SCHEMA_SERVICE_CLEAR_CACHE = vol.Schema({})
|
SCHEMA_SERVICE_CLEAR_CACHE = vol.Schema({})
|
||||||
@ -125,10 +129,13 @@ def async_setup(hass, config):
|
|||||||
message = service.data.get(ATTR_MESSAGE)
|
message = service.data.get(ATTR_MESSAGE)
|
||||||
cache = service.data.get(ATTR_CACHE)
|
cache = service.data.get(ATTR_CACHE)
|
||||||
language = service.data.get(ATTR_LANGUAGE)
|
language = service.data.get(ATTR_LANGUAGE)
|
||||||
|
options = service.data.get(ATTR_OPTIONS)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
url = yield from tts.async_get_url(
|
url = yield from tts.async_get_url(
|
||||||
p_type, message, cache=cache, language=language)
|
p_type, message, cache=cache, language=language,
|
||||||
|
options=options
|
||||||
|
)
|
||||||
except HomeAssistantError as err:
|
except HomeAssistantError as err:
|
||||||
_LOGGER.error("Error on init tts: %s", err)
|
_LOGGER.error("Error on init tts: %s", err)
|
||||||
return
|
return
|
||||||
@ -212,7 +219,9 @@ class SpeechManager(object):
|
|||||||
record = _RE_VOICE_FILE.match(file_data)
|
record = _RE_VOICE_FILE.match(file_data)
|
||||||
if record:
|
if record:
|
||||||
key = KEY_PATTERN.format(
|
key = KEY_PATTERN.format(
|
||||||
record.group(1), record.group(2), record.group(3))
|
record.group(1), record.group(2), record.group(3),
|
||||||
|
record.group(4)
|
||||||
|
)
|
||||||
cache[key.lower()] = file_data.lower()
|
cache[key.lower()] = file_data.lower()
|
||||||
return cache
|
return cache
|
||||||
|
|
||||||
@ -249,22 +258,37 @@ class SpeechManager(object):
|
|||||||
self.providers[engine] = provider
|
self.providers[engine] = provider
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def async_get_url(self, engine, message, cache=None, language=None):
|
def async_get_url(self, engine, message, cache=None, language=None,
|
||||||
|
options=None):
|
||||||
"""Get URL for play message.
|
"""Get URL for play message.
|
||||||
|
|
||||||
This method is a coroutine.
|
This method is a coroutine.
|
||||||
"""
|
"""
|
||||||
provider = self.providers[engine]
|
provider = self.providers[engine]
|
||||||
|
msg_hash = hashlib.sha1(bytes(message, 'utf-8')).hexdigest()
|
||||||
|
use_cache = cache if cache is not None else self.use_cache
|
||||||
|
|
||||||
|
# languages
|
||||||
language = language or provider.default_language
|
language = language or provider.default_language
|
||||||
if language is None or \
|
if language is None or \
|
||||||
language not in provider.supported_languages:
|
language not in provider.supported_languages:
|
||||||
raise HomeAssistantError("Not supported language {0}".format(
|
raise HomeAssistantError("Not supported language {0}".format(
|
||||||
language))
|
language))
|
||||||
|
|
||||||
msg_hash = hashlib.sha1(bytes(message, 'utf-8')).hexdigest()
|
# options
|
||||||
key = KEY_PATTERN.format(msg_hash, language, engine).lower()
|
options = options or provider.default_options
|
||||||
use_cache = cache if cache is not None else self.use_cache
|
if options is not None:
|
||||||
|
invalid_opts = [opt_name for opt_name in options.keys()
|
||||||
|
if opt_name not in provider.supported_options]
|
||||||
|
if invalid_opts:
|
||||||
|
raise HomeAssistantError(
|
||||||
|
"Invalid options found: %s", invalid_opts)
|
||||||
|
options_key = ctypes.c_size_t(hash(frozenset(options))).value
|
||||||
|
else:
|
||||||
|
options_key = '-'
|
||||||
|
|
||||||
|
key = KEY_PATTERN.format(
|
||||||
|
msg_hash, language, options_key, engine).lower()
|
||||||
|
|
||||||
# is speech allready in memory
|
# is speech allready in memory
|
||||||
if key in self.mem_cache:
|
if key in self.mem_cache:
|
||||||
@ -276,20 +300,21 @@ class SpeechManager(object):
|
|||||||
# load speech from provider into memory
|
# load speech from provider into memory
|
||||||
else:
|
else:
|
||||||
filename = yield from self.async_get_tts_audio(
|
filename = yield from self.async_get_tts_audio(
|
||||||
engine, key, message, use_cache, language)
|
engine, key, message, use_cache, language, options)
|
||||||
|
|
||||||
return "{}/api/tts_proxy/{}".format(
|
return "{}/api/tts_proxy/{}".format(
|
||||||
self.hass.config.api.base_url, filename)
|
self.hass.config.api.base_url, filename)
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def async_get_tts_audio(self, engine, key, message, cache, language):
|
def async_get_tts_audio(self, engine, key, message, cache, language,
|
||||||
|
options):
|
||||||
"""Receive TTS and store for view in cache.
|
"""Receive TTS and store for view in cache.
|
||||||
|
|
||||||
This method is a coroutine.
|
This method is a coroutine.
|
||||||
"""
|
"""
|
||||||
provider = self.providers[engine]
|
provider = self.providers[engine]
|
||||||
extension, data = yield from provider.async_get_tts_audio(
|
extension, data = yield from provider.async_get_tts_audio(
|
||||||
message, language)
|
message, language, options)
|
||||||
|
|
||||||
if data is None or extension is None:
|
if data is None or extension is None:
|
||||||
raise HomeAssistantError(
|
raise HomeAssistantError(
|
||||||
@ -377,7 +402,7 @@ class SpeechManager(object):
|
|||||||
raise HomeAssistantError("Wrong tts file format!")
|
raise HomeAssistantError("Wrong tts file format!")
|
||||||
|
|
||||||
key = KEY_PATTERN.format(
|
key = KEY_PATTERN.format(
|
||||||
record.group(1), record.group(2), record.group(3))
|
record.group(1), record.group(2), record.group(3), record.group(4))
|
||||||
|
|
||||||
if key not in self.mem_cache:
|
if key not in self.mem_cache:
|
||||||
if key not in self.file_cache:
|
if key not in self.file_cache:
|
||||||
@ -403,11 +428,21 @@ class Provider(object):
|
|||||||
"""List of supported languages."""
|
"""List of supported languages."""
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_tts_audio(self, message, language):
|
@property
|
||||||
|
def supported_options(self):
|
||||||
|
"""List of supported options like voice, emotionen."""
|
||||||
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def default_options(self):
|
||||||
|
"""Dict include default options."""
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_tts_audio(self, message, language, options=None):
|
||||||
"""Load tts audio file from provider."""
|
"""Load tts audio file from provider."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def async_get_tts_audio(self, message, language):
|
def async_get_tts_audio(self, message, language, options=None):
|
||||||
"""Load tts audio file from provider.
|
"""Load tts audio file from provider.
|
||||||
|
|
||||||
Return a tuple of file extension and data as bytes.
|
Return a tuple of file extension and data as bytes.
|
||||||
@ -415,7 +450,8 @@ class Provider(object):
|
|||||||
This method must be run in the event loop and returns a coroutine.
|
This method must be run in the event loop and returns a coroutine.
|
||||||
"""
|
"""
|
||||||
return self.hass.loop.run_in_executor(
|
return self.hass.loop.run_in_executor(
|
||||||
None, self.get_tts_audio, message, language)
|
None, ft.partial(
|
||||||
|
self.get_tts_audio, message, language, options=options))
|
||||||
|
|
||||||
|
|
||||||
class TextToSpeechView(HomeAssistantView):
|
class TextToSpeechView(HomeAssistantView):
|
||||||
|
@ -43,7 +43,12 @@ class DemoProvider(Provider):
|
|||||||
"""List of supported languages."""
|
"""List of supported languages."""
|
||||||
return SUPPORT_LANGUAGES
|
return SUPPORT_LANGUAGES
|
||||||
|
|
||||||
def get_tts_audio(self, message, language):
|
@property
|
||||||
|
def supported_options(self):
|
||||||
|
"""List of supported options like voice, emotionen."""
|
||||||
|
return ['voice', 'age']
|
||||||
|
|
||||||
|
def get_tts_audio(self, message, language, options=None):
|
||||||
"""Load TTS from demo."""
|
"""Load TTS from demo."""
|
||||||
filename = os.path.join(os.path.dirname(__file__), "demo.mp3")
|
filename = os.path.join(os.path.dirname(__file__), "demo.mp3")
|
||||||
try:
|
try:
|
||||||
|
@ -70,7 +70,7 @@ class GoogleProvider(Provider):
|
|||||||
return SUPPORT_LANGUAGES
|
return SUPPORT_LANGUAGES
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def async_get_tts_audio(self, message, language):
|
def async_get_tts_audio(self, message, language, options=None):
|
||||||
"""Load TTS from google."""
|
"""Load TTS from google."""
|
||||||
from gtts_token import gtts_token
|
from gtts_token import gtts_token
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ class PicoProvider(Provider):
|
|||||||
"""List of supported languages."""
|
"""List of supported languages."""
|
||||||
return SUPPORT_LANGUAGES
|
return SUPPORT_LANGUAGES
|
||||||
|
|
||||||
def get_tts_audio(self, message, language):
|
def get_tts_audio(self, message, language, options=None):
|
||||||
"""Load TTS using pico2wave."""
|
"""Load TTS using pico2wave."""
|
||||||
with tempfile.NamedTemporaryFile(suffix='.wav', delete=False) as tmpf:
|
with tempfile.NamedTemporaryFile(suffix='.wav', delete=False) as tmpf:
|
||||||
fname = tmpf.name
|
fname = tmpf.name
|
||||||
|
@ -114,7 +114,7 @@ class VoiceRSSProvider(Provider):
|
|||||||
return SUPPORT_LANGUAGES
|
return SUPPORT_LANGUAGES
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def async_get_tts_audio(self, message, language):
|
def async_get_tts_audio(self, message, language, options=None):
|
||||||
"""Load TTS from VoiceRSS."""
|
"""Load TTS from VoiceRSS."""
|
||||||
websession = async_get_clientsession(self.hass)
|
websession = async_get_clientsession(self.hass)
|
||||||
form_data = self._form_data.copy()
|
form_data = self._form_data.copy()
|
||||||
|
@ -77,7 +77,7 @@ class YandexSpeechKitProvider(Provider):
|
|||||||
return SUPPORT_LANGUAGES
|
return SUPPORT_LANGUAGES
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def async_get_tts_audio(self, message, language):
|
def async_get_tts_audio(self, message, language, options=None):
|
||||||
"""Load TTS from yandex."""
|
"""Load TTS from yandex."""
|
||||||
websession = async_get_clientsession(self.hass)
|
websession = async_get_clientsession(self.hass)
|
||||||
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
"""The tests for the TTS component."""
|
"""The tests for the TTS component."""
|
||||||
|
import ctypes
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch, PropertyMock
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
@ -82,11 +83,11 @@ class TestTTS(object):
|
|||||||
assert calls[0].data[ATTR_MEDIA_CONTENT_TYPE] == MEDIA_TYPE_MUSIC
|
assert calls[0].data[ATTR_MEDIA_CONTENT_TYPE] == MEDIA_TYPE_MUSIC
|
||||||
assert calls[0].data[ATTR_MEDIA_CONTENT_ID].find(
|
assert calls[0].data[ATTR_MEDIA_CONTENT_ID].find(
|
||||||
"/api/tts_proxy/265944c108cbb00b2a621be5930513e03a0bb2cd"
|
"/api/tts_proxy/265944c108cbb00b2a621be5930513e03a0bb2cd"
|
||||||
"_en_demo.mp3") \
|
"_en_-_demo.mp3") \
|
||||||
!= -1
|
!= -1
|
||||||
assert os.path.isfile(os.path.join(
|
assert os.path.isfile(os.path.join(
|
||||||
self.default_tts_cache,
|
self.default_tts_cache,
|
||||||
"265944c108cbb00b2a621be5930513e03a0bb2cd_en_demo.mp3"))
|
"265944c108cbb00b2a621be5930513e03a0bb2cd_en_-_demo.mp3"))
|
||||||
|
|
||||||
def test_setup_component_and_test_service_with_config_language(self):
|
def test_setup_component_and_test_service_with_config_language(self):
|
||||||
"""Setup the demo platform and call service."""
|
"""Setup the demo platform and call service."""
|
||||||
@ -111,11 +112,11 @@ class TestTTS(object):
|
|||||||
assert calls[0].data[ATTR_MEDIA_CONTENT_TYPE] == MEDIA_TYPE_MUSIC
|
assert calls[0].data[ATTR_MEDIA_CONTENT_TYPE] == MEDIA_TYPE_MUSIC
|
||||||
assert calls[0].data[ATTR_MEDIA_CONTENT_ID].find(
|
assert calls[0].data[ATTR_MEDIA_CONTENT_ID].find(
|
||||||
"/api/tts_proxy/265944c108cbb00b2a621be5930513e03a0bb2cd"
|
"/api/tts_proxy/265944c108cbb00b2a621be5930513e03a0bb2cd"
|
||||||
"_de_demo.mp3") \
|
"_de_-_demo.mp3") \
|
||||||
!= -1
|
!= -1
|
||||||
assert os.path.isfile(os.path.join(
|
assert os.path.isfile(os.path.join(
|
||||||
self.default_tts_cache,
|
self.default_tts_cache,
|
||||||
"265944c108cbb00b2a621be5930513e03a0bb2cd_de_demo.mp3"))
|
"265944c108cbb00b2a621be5930513e03a0bb2cd_de_-_demo.mp3"))
|
||||||
|
|
||||||
def test_setup_component_and_test_service_with_wrong_conf_language(self):
|
def test_setup_component_and_test_service_with_wrong_conf_language(self):
|
||||||
"""Setup the demo platform and call service with wrong config."""
|
"""Setup the demo platform and call service with wrong config."""
|
||||||
@ -152,11 +153,11 @@ class TestTTS(object):
|
|||||||
assert calls[0].data[ATTR_MEDIA_CONTENT_TYPE] == MEDIA_TYPE_MUSIC
|
assert calls[0].data[ATTR_MEDIA_CONTENT_TYPE] == MEDIA_TYPE_MUSIC
|
||||||
assert calls[0].data[ATTR_MEDIA_CONTENT_ID].find(
|
assert calls[0].data[ATTR_MEDIA_CONTENT_ID].find(
|
||||||
"/api/tts_proxy/265944c108cbb00b2a621be5930513e03a0bb2cd"
|
"/api/tts_proxy/265944c108cbb00b2a621be5930513e03a0bb2cd"
|
||||||
"_de_demo.mp3") \
|
"_de_-_demo.mp3") \
|
||||||
!= -1
|
!= -1
|
||||||
assert os.path.isfile(os.path.join(
|
assert os.path.isfile(os.path.join(
|
||||||
self.default_tts_cache,
|
self.default_tts_cache,
|
||||||
"265944c108cbb00b2a621be5930513e03a0bb2cd_de_demo.mp3"))
|
"265944c108cbb00b2a621be5930513e03a0bb2cd_de_-_demo.mp3"))
|
||||||
|
|
||||||
def test_setup_component_test_service_with_wrong_service_language(self):
|
def test_setup_component_test_service_with_wrong_service_language(self):
|
||||||
"""Setup the demo platform and call service."""
|
"""Setup the demo platform and call service."""
|
||||||
@ -180,7 +181,106 @@ class TestTTS(object):
|
|||||||
assert len(calls) == 0
|
assert len(calls) == 0
|
||||||
assert not os.path.isfile(os.path.join(
|
assert not os.path.isfile(os.path.join(
|
||||||
self.default_tts_cache,
|
self.default_tts_cache,
|
||||||
"265944c108cbb00b2a621be5930513e03a0bb2cd_lang_demo.mp3"))
|
"265944c108cbb00b2a621be5930513e03a0bb2cd_lang_-_demo.mp3"))
|
||||||
|
|
||||||
|
def test_setup_component_and_test_service_with_service_options(self):
|
||||||
|
"""Setup the demo platform and call service with options."""
|
||||||
|
calls = mock_service(self.hass, DOMAIN_MP, SERVICE_PLAY_MEDIA)
|
||||||
|
|
||||||
|
config = {
|
||||||
|
tts.DOMAIN: {
|
||||||
|
'platform': 'demo',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
with assert_setup_component(1, tts.DOMAIN):
|
||||||
|
setup_component(self.hass, tts.DOMAIN, config)
|
||||||
|
|
||||||
|
self.hass.services.call(tts.DOMAIN, 'demo_say', {
|
||||||
|
tts.ATTR_MESSAGE: "I person is on front of your door.",
|
||||||
|
tts.ATTR_LANGUAGE: "de",
|
||||||
|
tts.ATTR_OPTIONS: {
|
||||||
|
'voice': 'alex'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
self.hass.block_till_done()
|
||||||
|
|
||||||
|
opt_hash = ctypes.c_size_t(hash(frozenset({'voice': 'alex'}))).value
|
||||||
|
|
||||||
|
assert len(calls) == 1
|
||||||
|
assert calls[0].data[ATTR_MEDIA_CONTENT_TYPE] == MEDIA_TYPE_MUSIC
|
||||||
|
assert calls[0].data[ATTR_MEDIA_CONTENT_ID].find(
|
||||||
|
"/api/tts_proxy/265944c108cbb00b2a621be5930513e03a0bb2cd"
|
||||||
|
"_de_{0}_demo.mp3".format(opt_hash)) \
|
||||||
|
!= -1
|
||||||
|
assert os.path.isfile(os.path.join(
|
||||||
|
self.default_tts_cache,
|
||||||
|
"265944c108cbb00b2a621be5930513e03a0bb2cd_de_{0}_demo.mp3".format(
|
||||||
|
opt_hash)))
|
||||||
|
|
||||||
|
@patch('homeassistant.components.tts.demo.DemoProvider.default_options',
|
||||||
|
new_callable=PropertyMock(return_value={'voice': 'alex'}))
|
||||||
|
def test_setup_component_and_test_with_service_options_def(self, def_mock):
|
||||||
|
"""Setup the demo platform and call service with default options."""
|
||||||
|
calls = mock_service(self.hass, DOMAIN_MP, SERVICE_PLAY_MEDIA)
|
||||||
|
|
||||||
|
config = {
|
||||||
|
tts.DOMAIN: {
|
||||||
|
'platform': 'demo',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
with assert_setup_component(1, tts.DOMAIN):
|
||||||
|
setup_component(self.hass, tts.DOMAIN, config)
|
||||||
|
|
||||||
|
self.hass.services.call(tts.DOMAIN, 'demo_say', {
|
||||||
|
tts.ATTR_MESSAGE: "I person is on front of your door.",
|
||||||
|
tts.ATTR_LANGUAGE: "de",
|
||||||
|
})
|
||||||
|
self.hass.block_till_done()
|
||||||
|
|
||||||
|
opt_hash = ctypes.c_size_t(hash(frozenset({'voice': 'alex'}))).value
|
||||||
|
|
||||||
|
assert len(calls) == 1
|
||||||
|
assert calls[0].data[ATTR_MEDIA_CONTENT_TYPE] == MEDIA_TYPE_MUSIC
|
||||||
|
assert calls[0].data[ATTR_MEDIA_CONTENT_ID].find(
|
||||||
|
"/api/tts_proxy/265944c108cbb00b2a621be5930513e03a0bb2cd"
|
||||||
|
"_de_{0}_demo.mp3".format(opt_hash)) \
|
||||||
|
!= -1
|
||||||
|
assert os.path.isfile(os.path.join(
|
||||||
|
self.default_tts_cache,
|
||||||
|
"265944c108cbb00b2a621be5930513e03a0bb2cd_de_{0}_demo.mp3".format(
|
||||||
|
opt_hash)))
|
||||||
|
|
||||||
|
def test_setup_component_and_test_service_with_service_options_wrong(self):
|
||||||
|
"""Setup the demo platform and call service with wrong options."""
|
||||||
|
calls = mock_service(self.hass, DOMAIN_MP, SERVICE_PLAY_MEDIA)
|
||||||
|
|
||||||
|
config = {
|
||||||
|
tts.DOMAIN: {
|
||||||
|
'platform': 'demo',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
with assert_setup_component(1, tts.DOMAIN):
|
||||||
|
setup_component(self.hass, tts.DOMAIN, config)
|
||||||
|
|
||||||
|
self.hass.services.call(tts.DOMAIN, 'demo_say', {
|
||||||
|
tts.ATTR_MESSAGE: "I person is on front of your door.",
|
||||||
|
tts.ATTR_LANGUAGE: "de",
|
||||||
|
tts.ATTR_OPTIONS: {
|
||||||
|
'speed': 1
|
||||||
|
}
|
||||||
|
})
|
||||||
|
self.hass.block_till_done()
|
||||||
|
|
||||||
|
opt_hash = ctypes.c_size_t(hash(frozenset({'speed': 1}))).value
|
||||||
|
|
||||||
|
assert len(calls) == 0
|
||||||
|
assert not os.path.isfile(os.path.join(
|
||||||
|
self.default_tts_cache,
|
||||||
|
"265944c108cbb00b2a621be5930513e03a0bb2cd_de_{0}_demo.mp3".format(
|
||||||
|
opt_hash)))
|
||||||
|
|
||||||
def test_setup_component_and_test_service_clear_cache(self):
|
def test_setup_component_and_test_service_clear_cache(self):
|
||||||
"""Setup the demo platform and call service clear cache."""
|
"""Setup the demo platform and call service clear cache."""
|
||||||
@ -203,14 +303,14 @@ class TestTTS(object):
|
|||||||
assert len(calls) == 1
|
assert len(calls) == 1
|
||||||
assert os.path.isfile(os.path.join(
|
assert os.path.isfile(os.path.join(
|
||||||
self.default_tts_cache,
|
self.default_tts_cache,
|
||||||
"265944c108cbb00b2a621be5930513e03a0bb2cd_en_demo.mp3"))
|
"265944c108cbb00b2a621be5930513e03a0bb2cd_en_-_demo.mp3"))
|
||||||
|
|
||||||
self.hass.services.call(tts.DOMAIN, tts.SERVICE_CLEAR_CACHE, {})
|
self.hass.services.call(tts.DOMAIN, tts.SERVICE_CLEAR_CACHE, {})
|
||||||
self.hass.block_till_done()
|
self.hass.block_till_done()
|
||||||
|
|
||||||
assert not os.path.isfile(os.path.join(
|
assert not os.path.isfile(os.path.join(
|
||||||
self.default_tts_cache,
|
self.default_tts_cache,
|
||||||
"265944c108cbb00b2a621be5930513e03a0bb2cd_en_demo.mp3"))
|
"265944c108cbb00b2a621be5930513e03a0bb2cd_en_-_demo.mp3"))
|
||||||
|
|
||||||
def test_setup_component_and_test_service_with_receive_voice(self):
|
def test_setup_component_and_test_service_with_receive_voice(self):
|
||||||
"""Setup the demo platform and call service and receive voice."""
|
"""Setup the demo platform and call service and receive voice."""
|
||||||
@ -278,7 +378,7 @@ class TestTTS(object):
|
|||||||
self.hass.start()
|
self.hass.start()
|
||||||
|
|
||||||
url = ("{}/api/tts_proxy/265944c108cbb00b2a621be5930513e03a0bb2cd"
|
url = ("{}/api/tts_proxy/265944c108cbb00b2a621be5930513e03a0bb2cd"
|
||||||
"_en_demo.mp3").format(self.hass.config.api.base_url)
|
"_en_-_demo.mp3").format(self.hass.config.api.base_url)
|
||||||
|
|
||||||
req = requests.get(url)
|
req = requests.get(url)
|
||||||
assert req.status_code == 404
|
assert req.status_code == 404
|
||||||
@ -297,7 +397,7 @@ class TestTTS(object):
|
|||||||
self.hass.start()
|
self.hass.start()
|
||||||
|
|
||||||
url = ("{}/api/tts_proxy/265944dsk32c1b2a621be5930510bb2cd"
|
url = ("{}/api/tts_proxy/265944dsk32c1b2a621be5930510bb2cd"
|
||||||
"_en_demo.mp3").format(self.hass.config.api.base_url)
|
"_en_-_demo.mp3").format(self.hass.config.api.base_url)
|
||||||
|
|
||||||
req = requests.get(url)
|
req = requests.get(url)
|
||||||
assert req.status_code == 404
|
assert req.status_code == 404
|
||||||
@ -324,7 +424,7 @@ class TestTTS(object):
|
|||||||
assert len(calls) == 1
|
assert len(calls) == 1
|
||||||
assert not os.path.isfile(os.path.join(
|
assert not os.path.isfile(os.path.join(
|
||||||
self.default_tts_cache,
|
self.default_tts_cache,
|
||||||
"265944c108cbb00b2a621be5930513e03a0bb2cd_en_demo.mp3"))
|
"265944c108cbb00b2a621be5930513e03a0bb2cd_en_-_demo.mp3"))
|
||||||
|
|
||||||
def test_setup_component_test_with_cache_call_service_without_cache(self):
|
def test_setup_component_test_with_cache_call_service_without_cache(self):
|
||||||
"""Setup demo platform with cache and call service without cache."""
|
"""Setup demo platform with cache and call service without cache."""
|
||||||
@ -349,7 +449,7 @@ class TestTTS(object):
|
|||||||
assert len(calls) == 1
|
assert len(calls) == 1
|
||||||
assert not os.path.isfile(os.path.join(
|
assert not os.path.isfile(os.path.join(
|
||||||
self.default_tts_cache,
|
self.default_tts_cache,
|
||||||
"265944c108cbb00b2a621be5930513e03a0bb2cd_en_demo.mp3"))
|
"265944c108cbb00b2a621be5930513e03a0bb2cd_en_-_demo.mp3"))
|
||||||
|
|
||||||
def test_setup_component_test_with_cache_dir(self):
|
def test_setup_component_test_with_cache_dir(self):
|
||||||
"""Setup demo platform with cache and call service without cache."""
|
"""Setup demo platform with cache and call service without cache."""
|
||||||
@ -358,7 +458,7 @@ class TestTTS(object):
|
|||||||
_, demo_data = self.demo_provider.get_tts_audio("bla", 'en')
|
_, demo_data = self.demo_provider.get_tts_audio("bla", 'en')
|
||||||
cache_file = os.path.join(
|
cache_file = os.path.join(
|
||||||
self.default_tts_cache,
|
self.default_tts_cache,
|
||||||
"265944c108cbb00b2a621be5930513e03a0bb2cd_en_demo.mp3")
|
"265944c108cbb00b2a621be5930513e03a0bb2cd_en_-_demo.mp3")
|
||||||
|
|
||||||
os.mkdir(self.default_tts_cache)
|
os.mkdir(self.default_tts_cache)
|
||||||
with open(cache_file, "wb") as voice_file:
|
with open(cache_file, "wb") as voice_file:
|
||||||
@ -384,7 +484,7 @@ class TestTTS(object):
|
|||||||
assert len(calls) == 1
|
assert len(calls) == 1
|
||||||
assert calls[0].data[ATTR_MEDIA_CONTENT_ID].find(
|
assert calls[0].data[ATTR_MEDIA_CONTENT_ID].find(
|
||||||
"/api/tts_proxy/265944c108cbb00b2a621be5930513e03a0bb2cd"
|
"/api/tts_proxy/265944c108cbb00b2a621be5930513e03a0bb2cd"
|
||||||
"_en_demo.mp3") \
|
"_en_-_demo.mp3") \
|
||||||
!= -1
|
!= -1
|
||||||
|
|
||||||
@patch('homeassistant.components.tts.demo.DemoProvider.get_tts_audio',
|
@patch('homeassistant.components.tts.demo.DemoProvider.get_tts_audio',
|
||||||
@ -414,7 +514,7 @@ class TestTTS(object):
|
|||||||
_, demo_data = self.demo_provider.get_tts_audio("bla", 'en')
|
_, demo_data = self.demo_provider.get_tts_audio("bla", 'en')
|
||||||
cache_file = os.path.join(
|
cache_file = os.path.join(
|
||||||
self.default_tts_cache,
|
self.default_tts_cache,
|
||||||
"265944c108cbb00b2a621be5930513e03a0bb2cd_en_demo.mp3")
|
"265944c108cbb00b2a621be5930513e03a0bb2cd_en_-_demo.mp3")
|
||||||
|
|
||||||
os.mkdir(self.default_tts_cache)
|
os.mkdir(self.default_tts_cache)
|
||||||
with open(cache_file, "wb") as voice_file:
|
with open(cache_file, "wb") as voice_file:
|
||||||
@ -433,7 +533,7 @@ class TestTTS(object):
|
|||||||
self.hass.start()
|
self.hass.start()
|
||||||
|
|
||||||
url = ("{}/api/tts_proxy/265944c108cbb00b2a621be5930513e03a0bb2cd"
|
url = ("{}/api/tts_proxy/265944c108cbb00b2a621be5930513e03a0bb2cd"
|
||||||
"_en_demo.mp3").format(self.hass.config.api.base_url)
|
"_en_-_demo.mp3").format(self.hass.config.api.base_url)
|
||||||
|
|
||||||
req = requests.get(url)
|
req = requests.get(url)
|
||||||
assert req.status_code == 200
|
assert req.status_code == 200
|
||||||
|
Loading…
x
Reference in New Issue
Block a user