From 2d3f8cd3b0ae011fc20f3e75368123c8f9c4237b Mon Sep 17 00:00:00 2001 From: Howell Jiang Date: Fri, 18 Apr 2025 15:42:59 +0800 Subject: [PATCH] =?UTF-8?q?=E6=A0=B9=E6=8D=AE=E5=AE=98=E7=BD=91=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E8=A7=84=E8=8C=83=E4=BC=98=E5=8C=96=E7=BB=93=E6=9E=84?= =?UTF-8?q?=EF=BC=8C=E7=B2=BE=E7=AE=80=E4=BB=A3=E7=A0=81=E3=80=82=E4=BD=BF?= =?UTF-8?q?=E7=94=A8reconfigure=E6=A8=A1=E5=BC=8F=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E9=87=8D=E8=AE=BE=E5=8F=82=E6=95=B0=E3=80=82=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E5=A4=9A=E5=AE=9E=E4=BE=8B=E6=94=AF=E6=8C=81=E3=80=82=E5=8E=BB?= =?UTF-8?q?=E6=8E=89debug=E4=BF=A1=E6=81=AF=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- custom_components/baidu_voice/__init__.py | 9 +- custom_components/baidu_voice/config_flow.py | 134 ++++++------------ custom_components/baidu_voice/const.py | 2 +- custom_components/baidu_voice/hacs.json | 6 - custom_components/baidu_voice/icon.png | Bin 10598 -> 0 bytes custom_components/baidu_voice/manifest.json | 3 +- custom_components/baidu_voice/strings.json | 45 +++++- custom_components/baidu_voice/stt.py | 7 +- .../baidu_voice/system_health.py | 30 ++++ .../baidu_voice/translations/en.json | 12 +- .../baidu_voice/translations/zh-Hans.json | 12 +- custom_components/baidu_voice/tts.py | 69 +-------- 12 files changed, 129 insertions(+), 200 deletions(-) delete mode 100644 custom_components/baidu_voice/hacs.json delete mode 100644 custom_components/baidu_voice/icon.png create mode 100644 custom_components/baidu_voice/system_health.py diff --git a/custom_components/baidu_voice/__init__.py b/custom_components/baidu_voice/__init__.py index 2893852..db730e1 100644 --- a/custom_components/baidu_voice/__init__.py +++ b/custom_components/baidu_voice/__init__.py @@ -20,18 +20,17 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """设置百度语音配置项.""" - # 存储配置数据 - hass.data.setdefault(DOMAIN, {}) - hass.data[DOMAIN] = entry.data.copy() # 保存所有配置数据 - # 加载平台 + hass.data.setdefault(DOMAIN, {}) + hass.data[DOMAIN] = entry.data.copy() + await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) return True async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """卸载百度语音配置项.""" - # 卸载平台 + unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS) if unload_ok: diff --git a/custom_components/baidu_voice/config_flow.py b/custom_components/baidu_voice/config_flow.py index 7de9778..92cf4de 100644 --- a/custom_components/baidu_voice/config_flow.py +++ b/custom_components/baidu_voice/config_flow.py @@ -1,4 +1,4 @@ -"""Config flow for Baidu TTS integration.""" +"""Config flow for Baidu Voice integration.""" from __future__ import annotations @@ -7,13 +7,12 @@ from typing import Any import voluptuous as vol -from homeassistant.config_entries import ( - ConfigEntry, - ConfigFlow, - ConfigFlowResult, - OptionsFlow, +from homeassistant.config_entries import ConfigFlow, ConfigFlowResult +from homeassistant.helpers.selector import ( + SelectSelector, + SelectSelectorConfig, + SelectSelectorMode, ) -from homeassistant.core import callback from .const import ( CONF_API_KEY, @@ -62,6 +61,9 @@ STEP_USER_DATA_SCHEMA = vol.Schema( vol.Optional(TTS_CONF_VOICE, default=TTS_DEFAULT_VOICE): vol.In( TTS_SUPPORTED_VOICES ), + vol.Optional(TTS_CONF_FILEFORMAT, default=TTS_DEFAULT_FILEFORMAT): vol.In( + TTS_FILEFORMAT_MAP + ), vol.Optional(STT_CONF_LANGUAGE, default=STT_DEFAULT_LANGUAGE): vol.In( STT_LANGUAGES ), @@ -69,102 +71,46 @@ STEP_USER_DATA_SCHEMA = vol.Schema( ) -class BaiduTTSConfigFlow(ConfigFlow, domain=DOMAIN): - """Handle a config flow for Baidu TTS.""" +class BaiduVoiceConfigFlow(ConfigFlow, domain=DOMAIN): + """Handle a config flow for Baidu Voice.""" VERSION = 1 + async def async_step_reconfigure(self, user_input: dict[str, Any] | None = None): + """Handle reconfiguration of the integration.""" + if user_input is not None: + return self.async_update_reload_and_abort( + self._get_reconfigure_entry(), + data_updates=user_input, + ) + + schema = self.add_suggested_values_to_schema( + STEP_USER_DATA_SCHEMA, self._get_reconfigure_entry().data + ) + schema = schema.extend( + { + vol.Required(CONF_APP_ID): SelectSelector( + SelectSelectorConfig( + options=[self._get_reconfigure_entry().data[CONF_APP_ID]], + mode=SelectSelectorMode.DROPDOWN, + ) + ) + } + ) + + return self.async_show_form( + step_id="reconfigure", + data_schema=schema, + ) + async def async_step_user( self, user_input: dict[str, Any] | None = None ) -> ConfigFlowResult: """Handle the initial step.""" if user_input is not None: - # Provide unique ID to prevent duplicate entries app_id = user_input[CONF_APP_ID] - await self.async_set_unique_id(f"baidu_tts_{app_id}") + await self.async_set_unique_id(f"baidu_voice_{app_id}") self._abort_if_unique_id_configured() - - return self.async_create_entry(title="百度语音合成", data=user_input) + return self.async_create_entry(title="Baidu Voice", data=user_input) return self.async_show_form(step_id="user", data_schema=STEP_USER_DATA_SCHEMA) - - @classmethod - @callback - def async_get_options_flow(cls, config_entry: ConfigEntry) -> BaiduTTSOptionsFlow: - """Get the options flow for this handler.""" - return BaiduTTSOptionsFlow() - - -class BaiduTTSOptionsFlow(OptionsFlow): - """Baidu TTS integration options handler.""" - - async def async_step_init( - self, user_input: dict[str, Any] | None = None - ) -> ConfigFlowResult: - """Manage the options.""" - if user_input is not None: - return self.async_create_entry(title="", data=user_input) - - options = self.config_entry.options - data = self.config_entry.data - - schema = { - vol.Required( - CONF_APP_ID, - default=options.get(CONF_APP_ID, data.get(CONF_APP_ID)), - ): str, - vol.Required( - CONF_API_KEY, - default=options.get(CONF_API_KEY, data.get(CONF_API_KEY)), - ): str, - vol.Required( - CONF_SECRET_KEY, - default=options.get(CONF_SECRET_KEY, data.get(CONF_SECRET_KEY)), - ): str, - vol.Required( - TTS_CONF_LANGUAGE, - default=options.get(TTS_CONF_LANGUAGE, data.get(TTS_CONF_LANGUAGE)), - ): vol.In(TTS_LANGUAGES), - vol.Optional( - TTS_CONF_SPEED, - default=options.get( - TTS_CONF_SPEED, data.get(TTS_CONF_SPEED, TTS_DEFAULT_SPEED) - ), - ): vol.All(vol.Coerce(int), vol.Range(min=0, max=9)), - vol.Optional( - TTS_CONF_PITCH, - default=options.get( - TTS_CONF_PITCH, data.get(TTS_CONF_PITCH, TTS_DEFAULT_PITCH) - ), - ): vol.All(vol.Coerce(int), vol.Range(min=0, max=9)), - vol.Optional( - TTS_CONF_VOLUME, - default=options.get( - TTS_CONF_VOLUME, data.get(TTS_CONF_VOLUME, TTS_DEFAULT_VOLUME) - ), - ): vol.All(vol.Coerce(int), vol.Range(min=0, max=15)), - vol.Optional( - TTS_CONF_VOICE, - default=options.get( - TTS_CONF_VOICE, data.get(TTS_CONF_VOICE, TTS_DEFAULT_VOICE) - ), - ): vol.In(TTS_SUPPORTED_VOICES), - vol.Optional( - TTS_CONF_FILEFORMAT, - default=options.get( - TTS_CONF_FILEFORMAT, - data.get(TTS_CONF_FILEFORMAT, TTS_DEFAULT_FILEFORMAT), - ), - ): vol.In(TTS_FILEFORMAT_MAP), - vol.Optional( - STT_CONF_LANGUAGE, - default=options.get( - STT_CONF_LANGUAGE, data.get(STT_CONF_LANGUAGE, STT_DEFAULT_LANGUAGE) - ), - ): vol.In(STT_LANGUAGES), - } - - return self.async_show_form( - step_id="init", - data_schema=vol.Schema(schema), - ) diff --git a/custom_components/baidu_voice/const.py b/custom_components/baidu_voice/const.py index baec114..49c099a 100644 --- a/custom_components/baidu_voice/const.py +++ b/custom_components/baidu_voice/const.py @@ -3,7 +3,7 @@ from typing import Final DOMAIN: Final = "baidu_voice" - +ENDPOINT: Final = "http://vop.baidu.com/server_api" # 配置项 CONF_APP_ID: Final = "app_id" CONF_API_KEY: Final = "api_key" diff --git a/custom_components/baidu_voice/hacs.json b/custom_components/baidu_voice/hacs.json deleted file mode 100644 index c25719e..0000000 --- a/custom_components/baidu_voice/hacs.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "Baidu Voice", - "domains": ["tts", "stt"], - "homeassistant": "2023.1.0", - "render_readme": true -} \ No newline at end of file diff --git a/custom_components/baidu_voice/icon.png b/custom_components/baidu_voice/icon.png deleted file mode 100644 index 794a77e8b15269cf29e9e037ca2e53910a6034cd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10598 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 None: """Initialize Baidu speech-to-text entity.""" - super().__init__() self.hass = hass self._config = config self._client = AipSpeech( @@ -62,7 +61,7 @@ class BaiduSTTEntity(stt.SpeechToTextEntity): @property def supported_languages(self) -> list[str]: """Return list of supported languages.""" - return ["zh-CN", "en-US", "yue-CN", "sichuan-CN"] + return ["zh-CN", "en-US", "zh-HK", "zh-TW"] @property def supported_formats(self) -> list[str]: @@ -118,7 +117,7 @@ class BaiduSTTEntity(stt.SpeechToTextEntity): ) if "err_no" in result and result["err_no"] != 0: - _LOGGER.debug( + _LOGGER.error( "Error from Baidu API: %s - %s", result.get("err_msg", "Unknown error"), result.get("err_detail", "No details"), @@ -139,7 +138,7 @@ class BaiduSTTEntity(stt.SpeechToTextEntity): result=stt.SpeechResultState.SUCCESS, ) - except Exception: # pylint: disable=broad-except + except Exception: _LOGGER.exception("Error processing Baidu STT") return stt.SpeechResult( text=None, diff --git a/custom_components/baidu_voice/system_health.py b/custom_components/baidu_voice/system_health.py new file mode 100644 index 0000000..9471287 --- /dev/null +++ b/custom_components/baidu_voice/system_health.py @@ -0,0 +1,30 @@ +"""Provide info to system health.""" + +from typing import Any + +from homeassistant.components import system_health +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant, callback + +from .const import DOMAIN, ENDPOINT + + +@callback +def async_register( + hass: HomeAssistant, register: system_health.SystemHealthRegistration +) -> None: + """Register system health callbacks.""" + register.async_register_info(system_health_info) + + +async def system_health_info(hass: HomeAssistant) -> dict[str, Any]: + """Get info for the info page.""" + config_entry: ConfigEntry = hass.config_entries.async_entries(DOMAIN)[0] + quota_info = await config_entry.runtime_data.async_get_quota_info() + + return { + "consumed_requests": quota_info.consumed_requests, + "remaining_requests": quota_info.requests_remaining, + # checking the url can take a while, so set the coroutine in the info dict + "can_reach_server": system_health.async_check_can_reach_url(hass, ENDPOINT), + } diff --git a/custom_components/baidu_voice/translations/en.json b/custom_components/baidu_voice/translations/en.json index e3ffda3..94f8b23 100644 --- a/custom_components/baidu_voice/translations/en.json +++ b/custom_components/baidu_voice/translations/en.json @@ -27,12 +27,8 @@ "fileformat": "Audio Format", "test_connection": "Test Connection" } - } - } - }, - "options": { - "step": { - "init": { + }, + "reconfigure": { "title": "Update Baidu Voice Settings", "description": "Modify Baidu Voice settings", "data": { @@ -49,10 +45,6 @@ "test_connection": "Test Connection" } } - }, - "error": { - "auth": "Authentication failed", - "test_success": "Connection test successful" } } } \ No newline at end of file diff --git a/custom_components/baidu_voice/translations/zh-Hans.json b/custom_components/baidu_voice/translations/zh-Hans.json index 96985fb..fc34915 100644 --- a/custom_components/baidu_voice/translations/zh-Hans.json +++ b/custom_components/baidu_voice/translations/zh-Hans.json @@ -27,12 +27,8 @@ "fileformat": "音频格式", "test_connection": "测试连接" } - } - } - }, - "options": { - "step": { - "init": { + }, + "reconfigure": { "title": "更新百度语音设置", "description": "修改百度语音设置", "data": { @@ -49,10 +45,6 @@ "test_connection": "测试连接" } } - }, - "error": { - "auth": "认证失败", - "test_success": "连接测试成功" } } } \ No newline at end of file diff --git a/custom_components/baidu_voice/tts.py b/custom_components/baidu_voice/tts.py index 3c97f82..084a250 100644 --- a/custom_components/baidu_voice/tts.py +++ b/custom_components/baidu_voice/tts.py @@ -16,13 +16,11 @@ from homeassistant.components.tts import ( from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError -from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo from .const import ( CONF_API_KEY, CONF_APP_ID, CONF_SECRET_KEY, - DOMAIN, TTS_DEFAULT_FILEFORMAT, TTS_DEFAULT_LANGUAGE, TTS_DEFAULT_PITCH, @@ -53,29 +51,11 @@ class BaiduTTSEntity(TextToSpeechEntity): def __init__(self, hass: HomeAssistant, config_entry: ConfigEntry) -> None: """Initialize the Baidu TTS entity.""" - self.hass = hass - self._config_entry = config_entry # Generate unique ID and set name app_id = config_entry.data[CONF_APP_ID] self._attr_unique_id = f"baidu_tts_{app_id}" - self._attr_name = "百度语音合成" - - # Add device info - self._attr_device_info = DeviceInfo( - identifiers={(DOMAIN, app_id)}, - name="百度语音", - manufacturer="百度", - model="语音合成服务", - entry_type=DeviceEntryType.SERVICE, - ) - - _LOGGER.debug( - "Initializing Baidu TTS entity with app_id: %s, unique_id: %s, name: %s", - app_id, - self._attr_unique_id, - self._attr_name, - ) + self._attr_name = "Baidu TTS" # Initialize the TTS client self._client = AipSpeech( @@ -87,28 +67,23 @@ class BaiduTTSEntity(TextToSpeechEntity): @property def default_language(self) -> str: """Return the default language.""" - _LOGGER.debug("Getting default language: %s", TTS_DEFAULT_LANGUAGE) return TTS_DEFAULT_LANGUAGE @property def supported_languages(self) -> list[str]: """Return a list of supported languages.""" - languages = list(TTS_LANGUAGES.keys()) - _LOGGER.debug("Getting supported languages: %s", languages) - return languages + return list(TTS_LANGUAGES.keys()) @property def supported_options(self) -> list[str]: """Return list of supported options.""" - options = ["speed", "pitch", "volume", "voice", "fileformat"] - _LOGGER.debug("Getting supported options: %s", options) - return options + return ["speed", "pitch", "volume", "voice", "fileformat"] @property def default_options(self) -> dict[str, Any]: """Return a dict of default options.""" # Get current values from config_entry.options first, then data, then fall back to defaults - options = { + return { "speed": self._config_entry.options.get( "speed", self._config_entry.data.get("speed", TTS_DEFAULT_SPEED) ), @@ -126,8 +101,6 @@ class BaiduTTSEntity(TextToSpeechEntity): self._config_entry.data.get("fileformat", TTS_DEFAULT_FILEFORMAT), ), } - _LOGGER.debug("Getting default options: %s", options) - return options @callback def async_get_supported_voices(self, language: str) -> list[Voice] | None: @@ -135,77 +108,48 @@ class BaiduTTSEntity(TextToSpeechEntity): voices = [] for voice_id, voice_name in TTS_SUPPORTED_VOICES.items(): voices.append(Voice(str(voice_id), voice_name)) - _LOGGER.debug("Getting supported voices for language %s: %s", language, voices) return voices def get_tts_audio( self, message: str, language: str, options: dict[str, Any] ) -> TtsAudioType: """Get TTS audio from Baidu.""" - _LOGGER.debug("=================== TTS Debug Info Start ===================") - _LOGGER.debug("Message: %s", message) - _LOGGER.debug("Language: %s", language) - _LOGGER.debug("Raw options dict: %s", options) - _LOGGER.debug("Config entry data: %s", self._config_entry.data) - _LOGGER.debug("Config entry options: %s", self._config_entry.options) - # 获取配置值,优先级:options > config_entry.options > config_entry.data > 默认值 + # options > config_entry.data > default try: speed = int( options.get("speed") - or self._config_entry.options.get("speed") or self._config_entry.data.get("speed") or TTS_DEFAULT_SPEED ) pitch = int( options.get("pitch") - or self._config_entry.options.get("pitch") or self._config_entry.data.get("pitch") or TTS_DEFAULT_PITCH ) volume = int( options.get("volume") - or self._config_entry.options.get("volume") or self._config_entry.data.get("volume") or TTS_DEFAULT_VOLUME ) voice = int( options.get("voice") - or self._config_entry.options.get("voice") or self._config_entry.data.get("voice") or TTS_DEFAULT_VOICE ) fileformat = int( options.get("fileformat") - or self._config_entry.options.get("fileformat") or self._config_entry.data.get("fileformat") or TTS_DEFAULT_FILEFORMAT ) - _LOGGER.debug( - "Parsed values - speed: %s(%s), pitch: %s(%s), volume: %s(%s), " - "voice: %s(%s), fileformat: %s(%s)", - speed, - type(speed), - pitch, - type(pitch), - volume, - type(volume), - voice, - type(voice), - fileformat, - type(fileformat), - ) - except ValueError as ex: _LOGGER.error("Error parsing options: %s", ex) raise HomeAssistantError(f"Invalid option value: {ex}") from ex - # 获取音频格式 format_config = TTS_FILEFORMAT_MAP.get(fileformat, TTS_FILEFORMAT_MAP[6]) _LOGGER.debug("Using audio format: %s", format_config) - # 准备API参数 api_params = { "spd": speed, "pit": pitch, @@ -216,7 +160,6 @@ class BaiduTTSEntity(TextToSpeechEntity): _LOGGER.debug("API parameters: %s", api_params) try: - _LOGGER.debug("Calling Baidu TTS API") result = self._client.synthesis( message, language, @@ -244,6 +187,4 @@ class BaiduTTSEntity(TextToSpeechEntity): type(ex).__name__, ) raise HomeAssistantError("Failed to generate TTS audio") from ex - - _LOGGER.debug("=================== TTS Debug Info End ===================") return format_config, result