mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 21:27:38 +00:00
Add _attr
class attributes to TextToSpeechEntity (#115684)
This commit is contained in:
parent
790d22dc46
commit
e2141dc208
@ -2,11 +2,10 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from abc import abstractmethod
|
|
||||||
import asyncio
|
import asyncio
|
||||||
from collections.abc import Mapping
|
from collections.abc import Mapping
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from functools import partial
|
from functools import cached_property, partial
|
||||||
import hashlib
|
import hashlib
|
||||||
from http import HTTPStatus
|
from http import HTTPStatus
|
||||||
import io
|
import io
|
||||||
@ -373,12 +372,25 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
return await component.async_unload_entry(entry)
|
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."""
|
"""Represent a single TTS engine."""
|
||||||
|
|
||||||
_attr_should_poll = False
|
_attr_should_poll = False
|
||||||
__last_tts_loaded: str | None = None
|
__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
|
@property
|
||||||
@final
|
@final
|
||||||
def state(self) -> str | None:
|
def state(self) -> str | None:
|
||||||
@ -387,25 +399,25 @@ class TextToSpeechEntity(RestoreEntity):
|
|||||||
return None
|
return None
|
||||||
return self.__last_tts_loaded
|
return self.__last_tts_loaded
|
||||||
|
|
||||||
@property
|
@cached_property
|
||||||
@abstractmethod
|
|
||||||
def supported_languages(self) -> list[str]:
|
def supported_languages(self) -> list[str]:
|
||||||
"""Return a list of supported languages."""
|
"""Return a list of supported languages."""
|
||||||
|
return self._attr_supported_languages
|
||||||
|
|
||||||
@property
|
@cached_property
|
||||||
@abstractmethod
|
|
||||||
def default_language(self) -> str:
|
def default_language(self) -> str:
|
||||||
"""Return the default language."""
|
"""Return the default language."""
|
||||||
|
return self._attr_default_language
|
||||||
|
|
||||||
@property
|
@cached_property
|
||||||
def supported_options(self) -> list[str] | None:
|
def supported_options(self) -> list[str] | None:
|
||||||
"""Return a list of supported options like voice, emotions."""
|
"""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:
|
def default_options(self) -> Mapping[str, Any] | None:
|
||||||
"""Return a mapping with the default options."""
|
"""Return a mapping with the default options."""
|
||||||
return None
|
return self._attr_default_options
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_get_supported_voices(self, language: str) -> list[Voice] | None:
|
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:
|
async def async_internal_added_to_hass(self) -> None:
|
||||||
"""Call when the entity is added to hass."""
|
"""Call when the entity is added to hass."""
|
||||||
await super().async_internal_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()
|
state = await self.async_get_last_state()
|
||||||
if (
|
if (
|
||||||
state is not None
|
state is not None
|
||||||
|
@ -47,15 +47,8 @@ ORIG_WRITE_TAGS = tts.SpeechManager.write_tags
|
|||||||
class DefaultEntity(tts.TextToSpeechEntity):
|
class DefaultEntity(tts.TextToSpeechEntity):
|
||||||
"""Test entity."""
|
"""Test entity."""
|
||||||
|
|
||||||
@property
|
_attr_supported_languages = SUPPORT_LANGUAGES
|
||||||
def supported_languages(self) -> list[str]:
|
_attr_default_language = DEFAULT_LANG
|
||||||
"""Return a list of supported languages."""
|
|
||||||
return SUPPORT_LANGUAGES
|
|
||||||
|
|
||||||
@property
|
|
||||||
def default_language(self) -> str:
|
|
||||||
"""Return the default language."""
|
|
||||||
return DEFAULT_LANG
|
|
||||||
|
|
||||||
|
|
||||||
async def test_default_entity_attributes() -> None:
|
async def test_default_entity_attributes() -> None:
|
||||||
@ -523,10 +516,7 @@ class MockProviderWithDefaults(MockProvider):
|
|||||||
class MockEntityWithDefaults(MockTTSEntity):
|
class MockEntityWithDefaults(MockTTSEntity):
|
||||||
"""Mock entity with default options."""
|
"""Mock entity with default options."""
|
||||||
|
|
||||||
@property
|
_attr_default_options = {"voice": "alex"}
|
||||||
def default_options(self):
|
|
||||||
"""Return a mapping with the default options."""
|
|
||||||
return {"voice": "alex"}
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
@ -1758,3 +1748,93 @@ async def test_async_convert_audio_error(hass: HomeAssistant) -> None:
|
|||||||
with pytest.raises(RuntimeError):
|
with pytest.raises(RuntimeError):
|
||||||
# Simulate a bad WAV file
|
# Simulate a bad WAV file
|
||||||
await tts.async_convert_audio(hass, "wav", bytes(0), "mp3")
|
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…
x
Reference in New Issue
Block a user