Skip to content

Commit

Permalink
fixes
Browse files Browse the repository at this point in the history
fixed config flow
added unique id generation
  • Loading branch information
sfortis committed May 9, 2024
1 parent 69a10b2 commit 8a4ad58
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 59 deletions.
98 changes: 53 additions & 45 deletions custom_components/openai_tts/config_flow.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
"""Config flow for OpenAI text-to-speech custom component."""
from __future__ import annotations

from typing import Any

import voluptuous as vol
import logging

from homeassistant import data_entry_flow
from homeassistant.config_entries import ConfigFlow
from homeassistant.helpers.selector import selector
from homeassistant.exceptions import HomeAssistantError
Expand All @@ -14,57 +13,66 @@

_LOGGER = logging.getLogger(__name__)

class WrongAPIKey(HomeAssistantError):
"""Error to indicate no or wrong API key."""

async def validate_input(user_input: dict):
""" Function to validate provided data"""
api_key_length = len(user_input['CONF_API_KEY'])
if not (51 <= api_key_length <= 56):
raise WrongAPIKey

async def validate_api_key(api_key: str):
"""Validate the API key format."""
if api_key is None:
raise WrongAPIKey("API key is required")
if not (51 <= len(api_key) <= 56):
raise WrongAPIKey("Invalid API key length")

async def validate_user_input(user_input: dict):
"""Validate user input fields."""
await validate_api_key(user_input.get(CONF_API_KEY))
if user_input.get(CONF_MODEL) is None:
raise ValueError("Model is required")
if user_input.get(CONF_VOICE) is None:
raise ValueError("Voice is required")

class OpenAITTSConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle a config flow ."""

"""Handle a config flow for OpenAI TTS."""
VERSION = 1
data_schema = vol.Schema({
vol.Required(CONF_API_KEY): str,
vol.Optional(CONF_SPEED, default=1.0): vol.Coerce(float),
vol.Required(CONF_MODEL, default="tts-1"): selector({
"select": {
"options": MODELS,
"mode": "dropdown",
"sort": True,
"custom_value": False
}
}),
vol.Required(CONF_VOICE, default="shimmer"): selector({
"select": {
"options": VOICES,
"mode": "dropdown",
"sort": True,
"custom_value": False
}
})
})

async def async_step_user(self, user_input: dict[str, Any] | None = None):
"""Handle the initial step."""

data_schema = {vol.Required(CONF_API_KEY): str,
vol.Optional(CONF_SPEED, default=1.0): float, # default to 1.0 (float)
CONF_MODEL: selector({
"select": {
"options": MODELS,
"mode": "dropdown",
"sort": True,
"custom_value": False
}
}), CONF_VOICE: selector({
"select": {
"options": VOICES,
"mode": "dropdown",
"sort": True,
"custom_value": False
}
})
}

errors = {}

if user_input is not None:
try:
self._async_abort_entries_match({CONF_VOICE: user_input[CONF_VOICE]})
await validate_input(user_input)
await validate_user_input(user_input)
await self.async_set_unique_id(f"{user_input[CONF_VOICE]}_{user_input[CONF_MODEL]}")
self._abort_if_unique_id_configured()
return self.async_create_entry(title="OpenAI TTS", data=user_input)
except WrongAPIKey:
_LOGGER.exception("Wrong or no API key provided.")
errors[CONF_API_KEY] = "wrong_api_key"
except Exception: # pylint: disable=broad-except
_LOGGER.exception("Unknown exception.")
errors["base"] = "Unknown exception."

return self.async_show_form(step_id="user", data_schema=vol.Schema(data_schema))


class WrongAPIKey(HomeAssistantError):
"""Error to indicate no or wrong API key."""
except data_entry_flow.AbortFlow:
return self.async_abort(reason="already_configured")
except HomeAssistantError as e:
_LOGGER.exception(str(e))
errors["api_key"] = "wrong_api_key"
except ValueError as e:
_LOGGER.exception(str(e))
errors["base"] = str(e)
except Exception as e: # pylint: disable=broad-except
_LOGGER.exception(str(e))
errors["base"] = "unknown_error"
return self.async_show_form(step_id="user", data_schema=self.data_schema, errors=errors, description_placeholders=user_input)
8 changes: 6 additions & 2 deletions custom_components/openai_tts/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@
}
},
"error": {
"wrong_api_key": "Connection was not authorized. Wrong or no API key provided."
"wrong_api_key": "Invalid API key. Please enter a valid API key.",
"already_configured": "This voice is already configured."
},
"abort": {
"already_configured": "This voice is already configured."
}
}
}
}
8 changes: 6 additions & 2 deletions custom_components/openai_tts/translations/cs.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@
}
},
"error": {
"wrong_api_key": "Nebyl poskytnut správný API klíč."
"wrong_api_key": "Nebyl poskytnut správný API klíč.",
"already_configured": "Tento hlas je již nastaven."
},
"abort": {
"already_configured": "Tento hlas je již nastaven."
}
}
}
}
8 changes: 6 additions & 2 deletions custom_components/openai_tts/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@
}
},
"error": {
"wrong_api_key": "Connection was not authorized. Wrong or no API key provided."
"wrong_api_key": "Invalid API key. Please enter a valid API key.",
"already_configured": "This voice is already configured."
},
"abort": {
"already_configured": "This voice is already configured."
}
}
}
}
20 changes: 12 additions & 8 deletions custom_components/openai_tts/tts.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import CONF_API_KEY,CONF_MODEL, CONF_SPEED, CONF_VOICE, DOMAIN
from homeassistant.helpers.entity import generate_entity_id
from .const import CONF_API_KEY, CONF_MODEL, CONF_SPEED, CONF_VOICE, DOMAIN
from .openaitts_engine import OpenAITTSEngine
from homeassistant.exceptions import MaxLengthExceeded

Expand Down Expand Up @@ -38,7 +39,8 @@ def __init__(self, hass, config, engine):
self.hass = hass
self._engine = engine
self._config = config
self._attr_unique_id = self._config.data[CONF_VOICE]
self._attr_unique_id = f"{config.data[CONF_VOICE]}_{config.data[CONF_MODEL]}"
self.entity_id = generate_entity_id("tts.openai_tts_{}", config.data[CONF_VOICE], hass=hass)

@property
def default_language(self):
Expand All @@ -52,16 +54,19 @@ def supported_languages(self):

@property
def device_info(self):
return {"identifiers": {(DOMAIN, self._attr_unique_id)}, "name": f"OpenAI {self._config.data[CONF_VOICE]}", "manufacturer": "OpenAI"}
return {
"identifiers": {(DOMAIN, self._attr_unique_id)},
"model": f"{self._config.data[CONF_VOICE]}",
"manufacturer": "OpenAI"
}

@property
def name(self):
"""Return name of entity"""
return " engine"
return f"{self._config.data[CONF_VOICE]}"

def get_tts_audio(self, message, language, options=None):
"""Convert a given text to speech and return it as bytes."""

try:
if len(message) > 4096:
raise MaxLengthExceeded
Expand All @@ -70,10 +75,9 @@ def get_tts_audio(self, message, language, options=None):

# The response should contain the audio file content
return "mp3", speech.content
except Exception as e:
_LOGGER.error("Unknown Error: %s", e)

except MaxLengthExceeded:
_LOGGER.error("Maximum length of the message exceeded")
except Exception as e:
_LOGGER.error("Unknown Error: %s", e)

return None, None

0 comments on commit 8a4ad58

Please sign in to comment.