diff --git a/custom_components/openai_compatible/__init__.py b/custom_components/openai_compatible/__init__.py index cb67c46..459338a 100644 --- a/custom_components/openai_compatible/__init__.py +++ b/custom_components/openai_compatible/__init__.py @@ -54,6 +54,7 @@ from .const import ( DEFAULT_TTS_NAME, DOMAIN, LOGGER, + PLACEHOLDER_API_KEY, RECOMMENDED_AI_TASK_OPTIONS, RECOMMENDED_CHAT_MODEL, RECOMMENDED_MAX_TOKENS, @@ -282,7 +283,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: OpenAIConfigEntry) -> bool: """Set up OpenAI Conversation from a config entry.""" client = openai.AsyncOpenAI( - api_key=entry.data[CONF_API_KEY], + api_key=entry.data.get(CONF_API_KEY) or PLACEHOLDER_API_KEY, base_url=entry.data[CONF_BASE_URL], http_client=get_async_client(hass), ) diff --git a/custom_components/openai_compatible/config_flow.py b/custom_components/openai_compatible/config_flow.py index 2c4da64..0940a27 100644 --- a/custom_components/openai_compatible/config_flow.py +++ b/custom_components/openai_compatible/config_flow.py @@ -74,6 +74,7 @@ from .const import ( DEFAULT_STT_PROMPT, DEFAULT_TTS_NAME, DOMAIN, + PLACEHOLDER_API_KEY, RECOMMENDED_AI_TASK_OPTIONS, RECOMMENDED_CHAT_MODEL, RECOMMENDED_CODE_INTERPRETER, @@ -110,7 +111,7 @@ _LOGGER = logging.getLogger(__name__) STEP_USER_DATA_SCHEMA = vol.Schema( { vol.Required(CONF_BASE_URL, default=DEFAULT_BASE_URL): str, - vol.Required(CONF_API_KEY): str, + vol.Optional(CONF_API_KEY, default=""): str, } ) @@ -122,7 +123,7 @@ async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> None: """ data[CONF_BASE_URL] = normalize_base_url(data[CONF_BASE_URL]) client = openai.AsyncOpenAI( - api_key=data[CONF_API_KEY], + api_key=data.get(CONF_API_KEY) or PLACEHOLDER_API_KEY, base_url=data[CONF_BASE_URL], http_client=get_async_client(hass), ) @@ -654,7 +655,7 @@ class OpenAISubentryFlowHandler(ConfigSubentryFlow): zone_home = self.hass.states.get(ENTITY_ID_HOME) if zone_home is not None: client = openai.AsyncOpenAI( - api_key=self._get_entry().data[CONF_API_KEY], + api_key=self._get_entry().data.get(CONF_API_KEY) or PLACEHOLDER_API_KEY, base_url=self._get_entry().data[CONF_BASE_URL], http_client=get_async_client(self.hass), ) diff --git a/custom_components/openai_compatible/const.py b/custom_components/openai_compatible/const.py index 000d88c..43bb5c4 100644 --- a/custom_components/openai_compatible/const.py +++ b/custom_components/openai_compatible/const.py @@ -11,6 +11,7 @@ LOGGER: logging.Logger = logging.getLogger(__package__) CONF_BASE_URL = "base_url" DEFAULT_BASE_URL = "https://api.openai.com/v1" +PLACEHOLDER_API_KEY = "not-required" DEFAULT_CONVERSATION_NAME = "OpenAI Conversation" DEFAULT_AI_TASK_NAME = "OpenAI AI Task" diff --git a/tests/test_base_url.py b/tests/test_base_url.py index 420b9e1..2e14217 100644 --- a/tests/test_base_url.py +++ b/tests/test_base_url.py @@ -25,3 +25,22 @@ async def test_client_built_with_base_url(hass: HomeAssistant) -> None: await hass.async_block_till_done() assert mock_client.call_args.kwargs["base_url"] == "http://192.0.2.10:4000/v1" + + +async def test_api_key_optional(hass: HomeAssistant) -> None: + """An endpoint needing no auth should still set up.""" + entry = MockConfigEntry( + domain=DOMAIN, + data={CONF_BASE_URL: "http://192.0.2.10:4000/v1"}, + ) + entry.add_to_hass(hass) + + with patch( + "custom_components.openai_compatible.openai.AsyncOpenAI" + ) as mock_client: + mock_client.return_value.platform_headers = lambda: {} + mock_client.return_value.with_options.return_value.models.list = AsyncMock() + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert mock_client.call_args.kwargs["api_key"] == "not-required"