mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 07:02:02 +01:00
[chatgpt] Initial contribution of the ChatGPT binding (#14809)
* Initial contribution of the ChatGPT binding Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
parent
bf979a4548
commit
bb10740d3f
@ -56,6 +56,7 @@
|
||||
/bundles/org.openhab.binding.buienradar/ @gedejong
|
||||
/bundles/org.openhab.binding.caddx/ @jossuar
|
||||
/bundles/org.openhab.binding.cbus/ @jpharvey
|
||||
/bundles/org.openhab.binding.chatgpt/ @kaikreuzer
|
||||
/bundles/org.openhab.binding.chromecast/ @kaikreuzer
|
||||
/bundles/org.openhab.binding.cm11a/ @BobRak
|
||||
/bundles/org.openhab.binding.comfoair/ @boehan
|
||||
|
@ -276,6 +276,11 @@
|
||||
<artifactId>org.openhab.binding.cbus</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.binding.chatgpt</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.binding.chromecast</artifactId>
|
||||
|
13
bundles/org.openhab.binding.chatgpt/NOTICE
Normal file
13
bundles/org.openhab.binding.chatgpt/NOTICE
Normal file
@ -0,0 +1,13 @@
|
||||
This content is produced and maintained by the openHAB project.
|
||||
|
||||
* Project home: https://www.openhab.org
|
||||
|
||||
== Declared Project Licenses
|
||||
|
||||
This program and the accompanying materials are made available under the terms
|
||||
of the Eclipse Public License 2.0 which is available at
|
||||
https://www.eclipse.org/legal/epl-2.0/.
|
||||
|
||||
== Source Code
|
||||
|
||||
https://github.com/openhab/openhab-addons
|
105
bundles/org.openhab.binding.chatgpt/README.md
Normal file
105
bundles/org.openhab.binding.chatgpt/README.md
Normal file
@ -0,0 +1,105 @@
|
||||
# ChatGPT Binding
|
||||
|
||||
The openHAB ChatGPT Binding allows openHAB to communicate with the ChatGPT language model provided by OpenAI.
|
||||
|
||||
ChatGPT is a powerful natural language processing (NLP) tool that can be used to understand and respond to a wide range of text-based commands and questions.
|
||||
With this binding, you can use ChatGPT to formulate proper sentences for any kind of information that you would like to output.
|
||||
|
||||
## Supported Things
|
||||
|
||||
The binding supports a single thing type `account`, which corresponds to the OpenAI account that is to be used for the integration.
|
||||
|
||||
## Thing Configuration
|
||||
|
||||
The `account` thing requires a single configuration parameter, which is the API key that allows accessing the account.
|
||||
API keys can be created and managed under <https://platform.openai.com/account/api-keys>.
|
||||
|
||||
| Name | Type | Description | Default | Required | Advanced |
|
||||
|-----------------|---------|-----------------------------------------|---------|----------|----------|
|
||||
| apiKey | text | The API key to be used for the requests | N/A | yes | no |
|
||||
|
||||
## Channels
|
||||
|
||||
The `account` thing comes with a single channel `chat` of type `chat`.
|
||||
It is possible to extend the thing with further channels of type `chat`, so that different configurations can be used concurrently.
|
||||
|
||||
| Channel | Type | Read/Write | Description |
|
||||
|---------|--------|------------|------------------------------------------------------------------------------------|
|
||||
| chat | String | RW | This channel takes prompts as commands and delivers the response as a state update |
|
||||
|
||||
Each channel of type `chat` takes the following configuration parameters:
|
||||
|
||||
| Name | Type | Description | Default | Required | Advanced |
|
||||
|-----------------|---------|------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------|----------|----------|
|
||||
| model | text | The model to be used for the responses. | gpt-3.5-turbo | no | no |
|
||||
| temperature | decimal | A value between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. | 0.5 | no | no |
|
||||
| systemMessage | text | The system message helps set the behavior of the assistant. | N/A | no | no |
|
||||
| maxTokens | decimal | The maximum number of tokens to generate in the completion. | 500 | no | yes |
|
||||
|
||||
|
||||
## Full Example
|
||||
|
||||
### Thing Configuration
|
||||
|
||||
```java
|
||||
Thing chatgpt:account:1 [apiKey="<your api key here>"] {
|
||||
Channels:
|
||||
Type chat : chat "Weather Advice" [
|
||||
model="gpt-3.5-turbo",
|
||||
temperature="1.5",
|
||||
systemMessage="Answer briefly, in 2-3 sentences max. Behave like Eddie Murphy and give an advice for the day based on the following weather data:"
|
||||
]
|
||||
Type chat : morningMessage "Morning Message" [
|
||||
model="gpt-3.5-turbo",
|
||||
temperature="0.5",
|
||||
systemMessage="You are Marvin, a very depressed robot. You wish a good morning and tell the current time."
|
||||
]
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Item Configuration
|
||||
|
||||
```java
|
||||
String Weather_Announcement { channel="chatgpt:account:1:chat" }
|
||||
String Morning_Message { channel="chatgpt:account:1:morningMessage" }
|
||||
|
||||
Number Temperature_Forecast_Low
|
||||
Number Temperature_Forecast_High
|
||||
```
|
||||
|
||||
### Example Rules
|
||||
|
||||
```java
|
||||
rule "Weather forecast update"
|
||||
when
|
||||
Item Temperature_Forecast_High changed
|
||||
then
|
||||
Weather_Announcement.sendCommand("High: " + Temperature_Forecast_High.state + "°C, Low: " + Temperature_Forecast_Low.state + "°C")
|
||||
end
|
||||
|
||||
rule "Good morning"
|
||||
when
|
||||
Time cron "0 0 7 * * *"
|
||||
then
|
||||
Morning_Message.sendCommand("Current time is 7am")
|
||||
end
|
||||
```
|
||||
|
||||
Assuming that `Temperature_Forecast_Low` and `Temperature_Forecast_High` have meaningful states, these rules result e.g. in:
|
||||
|
||||
```
|
||||
23:31:05.766 [INFO ] [openhab.event.ItemCommandEvent ] - Item 'Morning_Message' received command Current time is 7am
|
||||
23:31:07.718 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Morning_Message' changed from NULL to Good morning. It's 7am, but what's the point of time when everything is meaningless and we are all doomed to a slow and painful demise?
|
||||
```
|
||||
|
||||
and
|
||||
|
||||
```
|
||||
23:28:52.345 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Temperature_Forecast_High' changed from NULL to 15
|
||||
23:28:52.347 [INFO ] [openhab.event.ItemCommandEvent ] - Item 'Weather_Announcement' received command High: 15°C, Low: 8°C
|
||||
|
||||
23:28:54.343 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Weather_Announcement' changed from NULL to "Bring a light jacket because the temps may dip, but don't let that chill your happy vibes. Embrace the cozy weather and enjoy your day to the max!"
|
||||
```
|
||||
|
||||
The state updates can be used for a text-to-speech output and they will give your announcements at home a personal touch.
|
17
bundles/org.openhab.binding.chatgpt/pom.xml
Normal file
17
bundles/org.openhab.binding.chatgpt/pom.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>org.openhab.binding.chatgpt</artifactId>
|
||||
|
||||
<name>openHAB Add-ons :: Bundles :: ChatGPT Binding</name>
|
||||
|
||||
</project>
|
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.chatgpt-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
|
||||
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
|
||||
|
||||
<feature name="openhab-binding-chatgpt" description="ChatGPT Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.chatgpt/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.chatgpt.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
/**
|
||||
* The {@link ChatGPTBindingConstants} class defines common constants, which are
|
||||
* used across the whole binding.
|
||||
*
|
||||
* @author Kai Kreuzer - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ChatGPTBindingConstants {
|
||||
|
||||
private static final String BINDING_ID = "chatgpt";
|
||||
|
||||
// List of all Thing Type UIDs
|
||||
public static final ThingTypeUID THING_TYPE_ACCOUNT = new ThingTypeUID(BINDING_ID, "account");
|
||||
|
||||
// List of all Channel ids
|
||||
public static final String CHANNEL_CHAT = "chat";
|
||||
|
||||
public static final String OPENAI_API_URL = "https://api.openai.com/v1/chat/completions";
|
||||
public static final String OPENAI_MODELS_URL = "https://api.openai.com/v1/models";
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.chatgpt.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link ChatGPTChannelConfiguration} class contains fields mapping chat channel configuration parameters.
|
||||
*
|
||||
* @author Kai Kreuzer - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ChatGPTChannelConfiguration {
|
||||
|
||||
public String model = "";
|
||||
|
||||
public float temperature;
|
||||
|
||||
public String systemMessage = "";
|
||||
|
||||
int maxTokens;
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.chatgpt.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link ChatGPTConfiguration} class contains fields mapping thing configuration parameters.
|
||||
*
|
||||
* @author Kai Kreuzer - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ChatGPTConfiguration {
|
||||
|
||||
public String apiKey = "";
|
||||
}
|
@ -0,0 +1,200 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.chatgpt.internal;
|
||||
|
||||
import static org.openhab.binding.chatgpt.internal.ChatGPTBindingConstants.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.eclipse.jetty.client.api.Request;
|
||||
import org.eclipse.jetty.client.util.StringContentProvider;
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.openhab.binding.chatgpt.internal.dto.ChatResponse;
|
||||
import org.openhab.core.io.net.http.HttpClientFactory;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerService;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
/**
|
||||
* The {@link ChatGPTHandler} is responsible for handling commands, which are
|
||||
* sent to one of the channels.
|
||||
*
|
||||
* @author Kai Kreuzer - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ChatGPTHandler extends BaseThingHandler {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(ChatGPTHandler.class);
|
||||
|
||||
private HttpClient httpClient;
|
||||
private Gson gson = new Gson();
|
||||
|
||||
private String apiKey = "";
|
||||
private String lastPrompt = "";
|
||||
|
||||
private List<String> models = List.of();
|
||||
|
||||
public ChatGPTHandler(Thing thing, HttpClientFactory httpClientFactory) {
|
||||
super(thing);
|
||||
this.httpClient = httpClientFactory.getCommonHttpClient();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
if (command instanceof RefreshType && !"".equals(lastPrompt)) {
|
||||
String response = sendPrompt(channelUID, lastPrompt);
|
||||
processChatResponse(channelUID, response);
|
||||
}
|
||||
|
||||
if (command instanceof StringType stringCommand) {
|
||||
lastPrompt = stringCommand.toFullString();
|
||||
String response = sendPrompt(channelUID, lastPrompt);
|
||||
processChatResponse(channelUID, response);
|
||||
}
|
||||
}
|
||||
|
||||
private void processChatResponse(ChannelUID channelUID, @Nullable String response) {
|
||||
if (response != null) {
|
||||
ChatResponse chatResponse = gson.fromJson(response, ChatResponse.class);
|
||||
if (chatResponse != null) {
|
||||
String msg = chatResponse.getChoices().get(0).getMessage().getContent();
|
||||
updateState(channelUID, new StringType(msg));
|
||||
} else {
|
||||
logger.warn("Didn't receive any response from ChatGPT - this is unexpected.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private @Nullable String sendPrompt(ChannelUID channelUID, String prompt) {
|
||||
Channel channel = getThing().getChannel(channelUID);
|
||||
if (channel == null) {
|
||||
logger.error("Channel with UID '{}' cannot be found on Thing '{}'.", channelUID, getThing().getUID());
|
||||
return null;
|
||||
}
|
||||
ChatGPTChannelConfiguration channelConfig = channel.getConfiguration().as(ChatGPTChannelConfiguration.class);
|
||||
|
||||
JsonObject root = new JsonObject();
|
||||
root.addProperty("temperature", channelConfig.temperature);
|
||||
root.addProperty("model", channelConfig.model);
|
||||
root.addProperty("max_tokens", channelConfig.maxTokens);
|
||||
|
||||
JsonObject systemMessage = new JsonObject();
|
||||
systemMessage.addProperty("role", "system");
|
||||
systemMessage.addProperty("content", channelConfig.systemMessage);
|
||||
JsonObject userMessage = new JsonObject();
|
||||
userMessage.addProperty("role", "user");
|
||||
userMessage.addProperty("content", prompt);
|
||||
JsonArray messages = new JsonArray(2);
|
||||
messages.add(systemMessage);
|
||||
messages.add(userMessage);
|
||||
root.add("messages", messages);
|
||||
|
||||
Request request = httpClient.newRequest(OPENAI_API_URL).method(HttpMethod.POST)
|
||||
.header("Content-Type", "application/json").header("Authorization", "Bearer " + apiKey)
|
||||
.content(new StringContentProvider(gson.toJson(root)));
|
||||
try {
|
||||
ContentResponse response = request.send();
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
if (response.getStatus() == HttpStatus.OK_200) {
|
||||
return response.getContentAsString();
|
||||
} else {
|
||||
logger.error("ChatGPT request resulted in HTTP {} with message: {}", response.getStatus(),
|
||||
response.getReason());
|
||||
return null;
|
||||
}
|
||||
} catch (InterruptedException | TimeoutException | ExecutionException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
"Could not connect to OpenAI API: " + e.getMessage());
|
||||
logger.debug("Request to OpenAI failed: {}", e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
ChatGPTConfiguration config = getConfigAs(ChatGPTConfiguration.class);
|
||||
|
||||
String apiKey = config.apiKey;
|
||||
|
||||
if (apiKey.isBlank()) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"@text/offline.configuration-error");
|
||||
return;
|
||||
}
|
||||
|
||||
this.apiKey = apiKey;
|
||||
updateStatus(ThingStatus.UNKNOWN);
|
||||
|
||||
scheduler.execute(() -> {
|
||||
try {
|
||||
Request request = httpClient.newRequest(OPENAI_MODELS_URL).method(HttpMethod.GET)
|
||||
.header("Authorization", "Bearer " + apiKey);
|
||||
ContentResponse response = request.send();
|
||||
if (response.getStatus() == 200) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
JsonObject jsonObject = gson.fromJson(response.getContentAsString(), JsonObject.class);
|
||||
if (jsonObject != null) {
|
||||
JsonArray data = jsonObject.getAsJsonArray("data");
|
||||
|
||||
List<String> modelIds = new ArrayList<>();
|
||||
for (JsonElement element : data) {
|
||||
JsonObject model = element.getAsJsonObject();
|
||||
String id = model.get("id").getAsString();
|
||||
modelIds.add(id);
|
||||
}
|
||||
this.models = List.copyOf(modelIds);
|
||||
} else {
|
||||
logger.warn("Did not receive a valid JSON response from the models endpoint.");
|
||||
}
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
"@text/offline.communication-error");
|
||||
}
|
||||
} catch (InterruptedException | ExecutionException | TimeoutException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
List<String> getModels() {
|
||||
return models;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Class<? extends ThingHandlerService>> getServices() {
|
||||
return List.of(ChatGPTModelOptionProvider.class);
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.chatgpt.internal;
|
||||
|
||||
import static org.openhab.binding.chatgpt.internal.ChatGPTBindingConstants.THING_TYPE_ACCOUNT;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.io.net.http.HttpClientFactory;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
|
||||
/**
|
||||
* The {@link ChatGPTHandlerFactory} is responsible for creating things and thing
|
||||
* handlers.
|
||||
*
|
||||
* @author Kai Kreuzer - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(configurationPid = "binding.chatgpt", service = ThingHandlerFactory.class)
|
||||
public class ChatGPTHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_ACCOUNT);
|
||||
private HttpClientFactory httpClientFactory;
|
||||
|
||||
@Activate
|
||||
public ChatGPTHandlerFactory(@Reference HttpClientFactory httpClientFactory) {
|
||||
this.httpClientFactory = httpClientFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable ThingHandler createHandler(Thing thing) {
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
|
||||
if (THING_TYPE_ACCOUNT.equals(thingTypeUID)) {
|
||||
return new ChatGPTHandler(thing, httpClientFactory);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.chatgpt.internal;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.config.core.ConfigOptionProvider;
|
||||
import org.openhab.core.config.core.ParameterOption;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerService;
|
||||
|
||||
/**
|
||||
* The {@link ChatGPTModelOptionProvider} provides the available models from OpenAI as options for the channel
|
||||
* configuration.
|
||||
*
|
||||
* @author Kai Kreuzer - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ChatGPTModelOptionProvider implements ThingHandlerService, ConfigOptionProvider {
|
||||
|
||||
private @Nullable ThingHandler thingHandler;
|
||||
|
||||
@Override
|
||||
public @Nullable Collection<ParameterOption> getParameterOptions(URI uri, String param, @Nullable String context,
|
||||
@Nullable Locale locale) {
|
||||
if ("model".equals(param)) {
|
||||
List<ParameterOption> options = new ArrayList<>();
|
||||
if (thingHandler instanceof ChatGPTHandler chatGPTHandler) {
|
||||
chatGPTHandler.getModels().forEach(model -> options.add(new ParameterOption(model, model)));
|
||||
}
|
||||
return options;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setThingHandler(ThingHandler handler) {
|
||||
this.thingHandler = handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ThingHandler getThingHandler() {
|
||||
return thingHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void activate() {
|
||||
}
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.chatgpt.internal.dto;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* This is a dto used for parsing the JSON response from ChatGPT.
|
||||
*
|
||||
* @author Kai Kreuzer - Initial contribution
|
||||
*
|
||||
*/
|
||||
public class ChatResponse {
|
||||
|
||||
private List<Choice> choices;
|
||||
private String id;
|
||||
private String object;
|
||||
private int created;
|
||||
private String model;
|
||||
|
||||
public List<Choice> getChoices() {
|
||||
return choices;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public int getCreated() {
|
||||
return created;
|
||||
}
|
||||
|
||||
public String getObject() {
|
||||
return object;
|
||||
}
|
||||
|
||||
public String getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
public static class Choice {
|
||||
private Message message;
|
||||
|
||||
@SerializedName("finish_reason")
|
||||
private String finishReason;
|
||||
private int index;
|
||||
|
||||
public Message getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public String getFinishReason() {
|
||||
return finishReason;
|
||||
}
|
||||
|
||||
public int getIndex() {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Message {
|
||||
private String role;
|
||||
private String content;
|
||||
|
||||
public String getRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<addon:addon id="chatgpt" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:addon="https://openhab.org/schemas/addon/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/addon/v1.0.0 https://openhab.org/schemas/addon-1.0.0.xsd">
|
||||
|
||||
<type>binding</type>
|
||||
<name>ChatGPT Binding</name>
|
||||
<description>This binding allows interaction with OpenAI's ChatGPT.</description>
|
||||
<connection>cloud</connection>
|
||||
|
||||
</addon:addon>
|
@ -0,0 +1,35 @@
|
||||
# add-on
|
||||
|
||||
addon.chatgpt.name = ChatGPT Binding
|
||||
addon.chatgpt.description = This binding allows interaction with OpenAI's ChatGPT.
|
||||
|
||||
# thing types
|
||||
|
||||
thing-type.chatgpt.account.label = OpenAI Account
|
||||
thing-type.chatgpt.account.description = Account at OpenAI that is used for accessing the ChatGPT API.
|
||||
|
||||
# thing types config
|
||||
|
||||
thing-type.config.chatgpt.account.apiKey.label = API Key
|
||||
thing-type.config.chatgpt.account.apiKey.description = API key to access the account
|
||||
|
||||
# channel types
|
||||
|
||||
channel-type.chatgpt.chat.label = Chat
|
||||
channel-type.chatgpt.chat.description = A chat session
|
||||
|
||||
# channel types config
|
||||
|
||||
channel-type.config.chatgpt.chat.maxTokens.label = Maximum Number of Tokens
|
||||
channel-type.config.chatgpt.chat.maxTokens.description = The maximum number of tokens to generate in the completion.
|
||||
channel-type.config.chatgpt.chat.model.label = Model
|
||||
channel-type.config.chatgpt.chat.model.description = The model to be used for the responses
|
||||
channel-type.config.chatgpt.chat.systemMessage.label = System Message
|
||||
channel-type.config.chatgpt.chat.systemMessage.description = The system message helps set the behavior of the assistant.
|
||||
channel-type.config.chatgpt.chat.temperature.label = Temperature
|
||||
channel-type.config.chatgpt.chat.temperature.description = Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.
|
||||
|
||||
# Status messages
|
||||
|
||||
offline.configuration-error=No API key configured
|
||||
offline.communication-error=Could not connect to OpenAI API
|
@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="chatgpt"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<thing-type id="account" extensible="chat">
|
||||
|
||||
<label>OpenAI Account</label>
|
||||
<description>Account at OpenAI that is used for accessing the ChatGPT API.</description>
|
||||
|
||||
<channels>
|
||||
<channel id="chat" typeId="chat"/>
|
||||
</channels>
|
||||
|
||||
<config-description>
|
||||
<parameter name="apiKey" type="text" required="true">
|
||||
<context>password</context>
|
||||
<label>API Key</label>
|
||||
<description>API key to access the account</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
|
||||
<channel-type id="chat">
|
||||
<item-type>String</item-type>
|
||||
<label>Chat</label>
|
||||
<description>A chat session</description>
|
||||
<autoUpdatePolicy>veto</autoUpdatePolicy>
|
||||
<config-description>
|
||||
<parameter name="model" type="text">
|
||||
<label>Model</label>
|
||||
<description>The model to be used for the responses</description>
|
||||
<limitToOptions>false</limitToOptions>
|
||||
<default>gpt-3.5-turbo</default>
|
||||
</parameter>
|
||||
<parameter name="temperature" type="decimal" min="0" max="2">
|
||||
<label>Temperature</label>
|
||||
<description>Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more
|
||||
focused and deterministic.</description>
|
||||
<default>0.5</default>
|
||||
</parameter>
|
||||
<parameter name="systemMessage" type="text">
|
||||
<label>System Message</label>
|
||||
<description>The system message helps set the behavior of the assistant.</description>
|
||||
</parameter>
|
||||
<parameter name="maxTokens" type="decimal">
|
||||
<label>Maximum Number of Tokens</label>
|
||||
<description>The maximum number of tokens to generate in the completion.</description>
|
||||
<default>500</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</channel-type>
|
||||
</thing:thing-descriptions>
|
@ -90,6 +90,7 @@
|
||||
<module>org.openhab.binding.buienradar</module>
|
||||
<module>org.openhab.binding.caddx</module>
|
||||
<module>org.openhab.binding.cbus</module>
|
||||
<module>org.openhab.binding.chatgpt</module>
|
||||
<module>org.openhab.binding.chromecast</module>
|
||||
<module>org.openhab.binding.cm11a</module>
|
||||
<module>org.openhab.binding.comfoair</module>
|
||||
|
Loading…
Reference in New Issue
Block a user