diff --git a/custom_components/ha_text_ai/const.py b/custom_components/ha_text_ai/const.py index 27e9eb3..2f0c23c 100644 --- a/custom_components/ha_text_ai/const.py +++ b/custom_components/ha_text_ai/const.py @@ -18,13 +18,13 @@ DEFAULT_MAX_TOKENS = 1000 DEFAULT_API_ENDPOINT = "https://api.openai.com/v1" DEFAULT_REQUEST_INTERVAL = 1.0 +# Services +SERVICE_ASK_QUESTION = "ask_question" +SERVICE_CLEAR_HISTORY = "clear_history" +SERVICE_GET_HISTORY = "get_history" +SERVICE_SET_SYSTEM_PROMPT = "set_system_prompt" + # Attributes ATTR_QUESTION = "question" ATTR_RESPONSE = "response" ATTR_LAST_UPDATED = "last_updated" - -# Services -SERVICE_ASK_QUESTION = "ask_question -SERVICE_CLEAR_HISTORY = "clear_history" -SERVICE_GET_HISTORY = "get_history" -SERVICE_SET_SYSTEM_PROMPT = "set_system_prompt" diff --git a/custom_components/ha_text_ai/coordinator.py b/custom_components/ha_text_ai/coordinator.py index d1290b9..a79e9f1 100644 --- a/custom_components/ha_text_ai/coordinator.py +++ b/custom_components/ha_text_ai/coordinator.py @@ -12,6 +12,9 @@ from homeassistant.exceptions import ConfigEntryAuthFailed from .const import ( DOMAIN, DEFAULT_REQUEST_INTERVAL, + CONF_MODEL, + CONF_TEMPERATURE, + CONF_MAX_TOKENS, ) _LOGGER = logging.getLogger(__name__) @@ -44,6 +47,7 @@ class HATextAICoordinator(DataUpdateCoordinator): self.max_tokens = max_tokens self._question_queue = asyncio.Queue() self._responses: Dict[str, Any] = {} + self.system_prompt: Optional[str] = None openai.api_key = self.api_key if endpoint != "https://api.openai.com/v1": @@ -56,10 +60,15 @@ class HATextAICoordinator(DataUpdateCoordinator): try: question = await self._question_queue.get() - response = await self.hass.async_add_executor_job( + response_content = await self.hass.async_add_executor_job( self._make_api_call, question ) + response = { + "question": question, + "response": response_content + } self._responses[question] = response + _LOGGER.debug(f"Response from API: {response}") return self._responses except openai.error.AuthenticationError as err: @@ -71,9 +80,11 @@ class HATextAICoordinator(DataUpdateCoordinator): def _make_api_call(self, question: str) -> str: """Make API call to OpenAI.""" try: + messages = [{"role": "system", "content": self.system_prompt}] if self.system_prompt else [] + messages.append({"role": "user", "content": question}) completion = openai.chat.completions.create( model=self.model, - messages=[{"role": "user", "content": question}], + messages=messages, temperature=self.temperature, max_tokens=self.max_tokens, ) @@ -85,4 +96,19 @@ class HATextAICoordinator(DataUpdateCoordinator): async def async_ask_question(self, question: str) -> None: """Add question to queue.""" await self._question_queue.put(question) + _LOGGER.debug(f"Question added to queue: {question}") await self.async_refresh() + + def clear_history(self) -> None: + """Clear the stored question and response history.""" + self._responses.clear() + _LOGGER.info("History cleared.") + + def get_history(self, limit: int = 10) -> Dict[str, Any]: + """Get the history of questions and responses.""" + return {"history": list(self._responses.values())[-limit:]} + + def set_system_prompt(self, prompt: str) -> None: + """Set a system prompt that will be used for all future questions.""" + self.system_prompt = prompt + _LOGGER.info(f"System prompt set: {prompt}") diff --git a/custom_components/ha_text_ai/sensor.py b/custom_components/ha_text_ai/sensor.py index 28a4f97..43ec019 100644 --- a/custom_components/ha_text_ai/sensor.py +++ b/custom_components/ha_text_ai/sensor.py @@ -33,12 +33,26 @@ class HATextAISensor(CoordinatorEntity, SensorEntity): self._config_entry = config_entry self._attr_unique_id = f"{config_entry.entry_id}" self._attr_name = "HA text AI" + self._attr_state_class = SensorStateClass.MEASUREMENT - @property - def extra_state_attributes(self) -> Optional[Dict[str, Any]]: - """Return entity specific state attributes.""" - return { - ATTR_QUESTION: list(self.coordinator.data.keys())[-1] if self.coordinator.data else None, - ATTR_RESPONSE: list(self.coordinator.data.values())[-1] if self.coordinator.data else None, - ATTR_LAST_UPDATED: self.coordinator.last_update_success_time, - } + @property + def state(self) -> StateType: + """Return the state of the sensor.""" + if self.coordinator.data: + return "Ready" # Assuming "Ready" is a valid state, you might want to return something meaningful, like the last response time. + return "Not Ready" + + @property + def extra_state_attributes(self) -> Optional[Dict[str, Any]]: + """Return entity specific state attributes.""" + if not self.coordinator.data: + return None + keys = list(self.coordinator.data.keys()) + values = list(self.coordinator.data.values()) + last_question = keys[-1] + last_response = values[-1] + return { + ATTR_QUESTION: last_question, + ATTR_RESPONSE: last_response, + ATTR_LAST_UPDATED: self.coordinator.last_update_success_time, + } diff --git a/ha_text_ai.zip b/ha_text_ai.zip new file mode 100644 index 0000000..591e34a Binary files /dev/null and b/ha_text_ai.zip differ