Add _attr
class attributes to TextToSpeechEntity (#115684)
This commit is contained in:
parent
790d22dc46
commit
e2141dc208
2 changed files with 128 additions and 24 deletions
|
@ -2,11 +2,10 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
from abc import abstractmethod
|
||||
import asyncio
|
||||
from collections.abc import Mapping
|
||||
from datetime import datetime
|
||||
from functools import partial
|
||||
from functools import cached_property, partial
|
||||
import hashlib
|
||||
from http import HTTPStatus
|
||||
import io
|
||||
|
@ -373,12 +372,25 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||
return await component.async_unload_entry(entry)
|
||||
|
||||
|
||||
class TextToSpeechEntity(RestoreEntity):
|
||||
CACHED_PROPERTIES_WITH_ATTR_ = {
|
||||
"default_language",
|
||||
"default_options",
|
||||
"supported_languages",
|
||||
"supported_options",
|
||||
}
|
||||
|
||||
|
||||
class TextToSpeechEntity(RestoreEntity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||
"""Represent a single TTS engine."""
|
||||
|
||||
_attr_should_poll = False
|
||||
__last_tts_loaded: str | None = None
|
||||
|
||||
_attr_default_language: str
|
||||
_attr_default_options: Mapping[str, Any] | None = None
|
||||
_attr_supported_languages: list[str]
|
||||
_attr_supported_options: list[str] | None = None
|
||||
|
||||
@property
|
||||
@final
|
||||
def state(self) -> str | None:
|
||||
|
@ -387,25 +399,25 @@ class TextToSpeechEntity(RestoreEntity):
|
|||
return None
|
||||
return self.__last_tts_loaded
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
@cached_property
|
||||
def supported_languages(self) -> list[str]:
|
||||
"""Return a list of supported languages."""
|
||||
return self._attr_supported_languages
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
@cached_property
|
||||
def default_language(self) -> str:
|
||||
"""Return the default language."""
|
||||
return self._attr_default_language
|
||||
|
||||
@property
|
||||
@cached_property
|
||||
def supported_options(self) -> list[str] | None:
|
||||
"""Return a list of supported options like voice, emotions."""
|
||||
return None
|
||||
return self._attr_supported_options
|
||||
|
||||
@property
|
||||
@cached_property
|
||||
def default_options(self) -> Mapping[str, Any] | None:
|
||||
"""Return a mapping with the default options."""
|
||||
return None
|
||||
return self._attr_default_options
|
||||
|
||||
@callback
|
||||
def async_get_supported_voices(self, language: str) -> list[Voice] | None:
|
||||
|
@ -415,6 +427,18 @@ class TextToSpeechEntity(RestoreEntity):
|
|||
async def async_internal_added_to_hass(self) -> None:
|
||||
"""Call when the entity is added to hass."""
|
||||
await super().async_internal_added_to_hass()
|
||||
try:
|
||||
_ = self.default_language
|
||||
except AttributeError as err:
|
||||
raise AttributeError(
|
||||
"TTS entities must either set the '_attr_default_language' attribute or override the 'default_language' property"
|
||||
) from err
|
||||
try:
|
||||
_ = self.supported_languages
|
||||
except AttributeError as err:
|
||||
raise AttributeError(
|
||||
"TTS entities must either set the '_attr_supported_languages' attribute or override the 'supported_languages' property"
|
||||
) from err
|
||||
state = await self.async_get_last_state()
|
||||
if (
|
||||
state is not None
|
||||
|
|
|
@ -47,15 +47,8 @@ ORIG_WRITE_TAGS = tts.SpeechManager.write_tags
|
|||
class DefaultEntity(tts.TextToSpeechEntity):
|
||||
"""Test entity."""
|
||||
|
||||
@property
|
||||
def supported_languages(self) -> list[str]:
|
||||
"""Return a list of supported languages."""
|
||||
return SUPPORT_LANGUAGES
|
||||
|
||||
@property
|
||||
def default_language(self) -> str:
|
||||
"""Return the default language."""
|
||||
return DEFAULT_LANG
|
||||
_attr_supported_languages = SUPPORT_LANGUAGES
|
||||
_attr_default_language = DEFAULT_LANG
|
||||
|
||||
|
||||
async def test_default_entity_attributes() -> None:
|
||||
|
@ -523,10 +516,7 @@ class MockProviderWithDefaults(MockProvider):
|
|||
class MockEntityWithDefaults(MockTTSEntity):
|
||||
"""Mock entity with default options."""
|
||||
|
||||
@property
|
||||
def default_options(self):
|
||||
"""Return a mapping with the default options."""
|
||||
return {"voice": "alex"}
|
||||
_attr_default_options = {"voice": "alex"}
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
@ -1758,3 +1748,93 @@ async def test_async_convert_audio_error(hass: HomeAssistant) -> None:
|
|||
with pytest.raises(RuntimeError):
|
||||
# Simulate a bad WAV file
|
||||
await tts.async_convert_audio(hass, "wav", bytes(0), "mp3")
|
||||
|
||||
|
||||
async def test_ttsentity_subclass_properties(
|
||||
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
|
||||
) -> None:
|
||||
"""Test for errors when subclasses of the TextToSpeechEntity are missing required properties."""
|
||||
|
||||
class TestClass1(tts.TextToSpeechEntity):
|
||||
_attr_default_language = DEFAULT_LANG
|
||||
_attr_supported_languages = SUPPORT_LANGUAGES
|
||||
|
||||
await mock_config_entry_setup(hass, TestClass1())
|
||||
|
||||
class TestClass2(tts.TextToSpeechEntity):
|
||||
@property
|
||||
def default_language(self) -> str:
|
||||
return DEFAULT_LANG
|
||||
|
||||
@property
|
||||
def supported_languages(self) -> list[str]:
|
||||
return SUPPORT_LANGUAGES
|
||||
|
||||
await mock_config_entry_setup(hass, TestClass2())
|
||||
|
||||
assert all(record.exc_info is None for record in caplog.records)
|
||||
|
||||
caplog.clear()
|
||||
|
||||
class TestClass3(tts.TextToSpeechEntity):
|
||||
_attr_default_language = DEFAULT_LANG
|
||||
|
||||
await mock_config_entry_setup(hass, TestClass3())
|
||||
|
||||
assert (
|
||||
"TTS entities must either set the '_attr_supported_languages' attribute or override the 'supported_languages' property"
|
||||
in [
|
||||
str(record.exc_info[1])
|
||||
for record in caplog.records
|
||||
if record.exc_info is not None
|
||||
]
|
||||
)
|
||||
caplog.clear()
|
||||
|
||||
class TestClass4(tts.TextToSpeechEntity):
|
||||
_attr_supported_languages = SUPPORT_LANGUAGES
|
||||
|
||||
await mock_config_entry_setup(hass, TestClass4())
|
||||
|
||||
assert (
|
||||
"TTS entities must either set the '_attr_default_language' attribute or override the 'default_language' property"
|
||||
in [
|
||||
str(record.exc_info[1])
|
||||
for record in caplog.records
|
||||
if record.exc_info is not None
|
||||
]
|
||||
)
|
||||
caplog.clear()
|
||||
|
||||
class TestClass5(tts.TextToSpeechEntity):
|
||||
@property
|
||||
def default_language(self) -> str:
|
||||
return DEFAULT_LANG
|
||||
|
||||
await mock_config_entry_setup(hass, TestClass5())
|
||||
|
||||
assert (
|
||||
"TTS entities must either set the '_attr_supported_languages' attribute or override the 'supported_languages' property"
|
||||
in [
|
||||
str(record.exc_info[1])
|
||||
for record in caplog.records
|
||||
if record.exc_info is not None
|
||||
]
|
||||
)
|
||||
caplog.clear()
|
||||
|
||||
class TestClass6(tts.TextToSpeechEntity):
|
||||
@property
|
||||
def supported_languages(self) -> list[str]:
|
||||
return SUPPORT_LANGUAGES
|
||||
|
||||
await mock_config_entry_setup(hass, TestClass6())
|
||||
|
||||
assert (
|
||||
"TTS entities must either set the '_attr_default_language' attribute or override the 'default_language' property"
|
||||
in [
|
||||
str(record.exc_info[1])
|
||||
for record in caplog.records
|
||||
if record.exc_info is not None
|
||||
]
|
||||
)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue