From 0dffb570afc86646bbb35a14a2723ba86a5d6c2e Mon Sep 17 00:00:00 2001 From: valentinfrlch Date: Fri, 17 May 2024 14:08:12 +0200 Subject: [PATCH] Added config flow, code optimizations --- custom_components/gpt4vision/__init__.py | 53 +++++++++++-------- custom_components/gpt4vision/config_flow.py | 22 ++++++++ custom_components/gpt4vision/const.py | 9 ++++ custom_components/gpt4vision/manifest.json | 3 +- .../gpt4vision/translations/en.json | 13 +++++ strings.json | 13 +++++ 6 files changed, 91 insertions(+), 22 deletions(-) create mode 100644 custom_components/gpt4vision/config_flow.py create mode 100644 custom_components/gpt4vision/const.py create mode 100644 custom_components/gpt4vision/translations/en.json create mode 100644 strings.json diff --git a/custom_components/gpt4vision/__init__.py b/custom_components/gpt4vision/__init__.py index 2a75f49..344896c 100644 --- a/custom_components/gpt4vision/__init__.py +++ b/custom_components/gpt4vision/__init__.py @@ -1,21 +1,25 @@ # Declare variables +from .const import DOMAIN, CONF_API_KEY, CONF_MAXTOKENS, CONF_TARGET_WIDTH, CONF_MODEL, CONF_MESSAGE, CONF_IMAGE_FILE import base64 import requests import io +import os from homeassistant.core import SupportsResponse from homeassistant.exceptions import ServiceValidationError from PIL import Image -DOMAIN = 'gpt4vision' -# config -CONF_API = 'api' -CONF_MAXTOKENS = 'max_tokens' -CONF_TARGET_WIDTH = 'target_width' -CONF_MODEL = 'model' -CONF_MESSAGE = 'message' -CONF_IMAGE_FILE = 'image_file' -CON_RESPONSE_FILE_PATH = '/config/custom_components/gpt4vision/' +async def async_setup_entry(hass, entry): + """Set up gpt4vision from a config entry.""" + # Get the API key from the configuration entry + api_key = entry.data[CONF_API_KEY] + + # Store the API key in hass.data + hass.data[DOMAIN] = { + "api_key": api_key + } + + return True def setup(hass, config): @@ -26,15 +30,13 @@ def image_analyzer(data_call): json: response_text """ - # Read api key from configuration.yaml - try: - api_key = str(config[DOMAIN][CONF_API]) - except KeyError: - raise ServiceValidationError("API key is missing. Please check your configuration.yaml file.") - + # Try to get the API key from hass.data + api_key = hass.data.get(DOMAIN, {}).get("api_key") + # Check if api key is present if not api_key: - raise ServiceValidationError("API key is missing. Please check your configuration.yaml file.") + raise ServiceValidationError( + "API key is required. Please set up the integration again.") # Read data from service call # Resolution (width only) of the image. Example: 1280 for 720p etc. @@ -48,6 +50,11 @@ def image_analyzer(data_call): # Message to be sent to AI model message = str(data_call.data.get(CONF_MESSAGE)[0:2000]) + # Check if image file exists + if not os.path.exists(image_path): + raise ServiceValidationError( + f"Image does not exist: {image_path}") + def encode_image(image_path): """Encode image as base64 @@ -57,6 +64,7 @@ def encode_image(image_path): Returns: string: image encoded as base64 """ + # Open the image file with Image.open(image_path) as img: width, height = img.size @@ -90,13 +98,16 @@ def encode_image(image_path): {"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{base64_image}"}}]}], "max_tokens": max_tokens} # Get response from OpenAI and read content inside message - response = requests.post("https://api.openai.com/v1/chat/completions", headers=headers, json=data) - + response = requests.post( + "https://api.openai.com/v1/chat/completions", headers=headers, json=data) + # Check if response is successful if response.status_code != 200: - raise ServiceValidationError(response.json().get('error').get('message')) - - response_text = response.json().get("choices")[0].get("message").get("content") + raise ServiceValidationError( + response.json().get('error').get('message')) + + response_text = response.json().get( + "choices")[0].get("message").get("content") return {"response_text": response_text} hass.services.register( diff --git a/custom_components/gpt4vision/config_flow.py b/custom_components/gpt4vision/config_flow.py new file mode 100644 index 0000000..ef01383 --- /dev/null +++ b/custom_components/gpt4vision/config_flow.py @@ -0,0 +1,22 @@ +from homeassistant import config_entries +from .const import DOMAIN, CONF_API_KEY +import voluptuous as vol + + +class gpt4visionConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): + VERSION = 1 + + async def async_step_user(self, user_input=None): + data_schema = vol.Schema({ + vol.Required(CONF_API_KEY): str + }) + + if user_input is not None: + # Save the API key + return self.async_create_entry(title="GPT4Vision Configuration", data=user_input) + + return self.async_show_form( + step_id="user", + data_schema=data_schema, + description_placeholders=user_input + ) diff --git a/custom_components/gpt4vision/const.py b/custom_components/gpt4vision/const.py new file mode 100644 index 0000000..a1fa1ae --- /dev/null +++ b/custom_components/gpt4vision/const.py @@ -0,0 +1,9 @@ +""" Constants for gpt4vision component""" + +DOMAIN = "gpt4vision" +CONF_API_KEY = 'api_key' +CONF_MAXTOKENS = 'max_tokens' +CONF_TARGET_WIDTH = 'target_width' +CONF_MODEL = 'model' +CONF_MESSAGE = 'message' +CONF_IMAGE_FILE = 'image_file' \ No newline at end of file diff --git a/custom_components/gpt4vision/manifest.json b/custom_components/gpt4vision/manifest.json index f5016d4..4166dc9 100644 --- a/custom_components/gpt4vision/manifest.json +++ b/custom_components/gpt4vision/manifest.json @@ -1,9 +1,10 @@ { "domain": "gpt4vision", "name": "GPT-4 vision", + "config_flow": true, "codeowners": ["@valentinfrlch"], "issue_tracker": "https://github.com/valentinfrlch/ha-gpt4vision/issues", "documentation": "https://github.com/valentinfrlch/ha-gpt4vision", "iot_class": "cloud_polling", - "version": "0.1.4" + "version": "0.2.0" } diff --git a/custom_components/gpt4vision/translations/en.json b/custom_components/gpt4vision/translations/en.json new file mode 100644 index 0000000..1be0dee --- /dev/null +++ b/custom_components/gpt4vision/translations/en.json @@ -0,0 +1,13 @@ +{ + "config": { + "step": { + "user": { + "title": "Add API key", + "description": "Provide your API key. See docs for more information.", + "data": { + "api_key": "Your API key" + } + } + } + } +} \ No newline at end of file diff --git a/strings.json b/strings.json new file mode 100644 index 0000000..1be0dee --- /dev/null +++ b/strings.json @@ -0,0 +1,13 @@ +{ + "config": { + "step": { + "user": { + "title": "Add API key", + "description": "Provide your API key. See docs for more information.", + "data": { + "api_key": "Your API key" + } + } + } + } +} \ No newline at end of file