Skip to content

Commit

Permalink
#75 add morning images from DALL•E
Browse files Browse the repository at this point in the history
  • Loading branch information
AndreKoepke committed Nov 4, 2022
1 parent 884ab3d commit 3321b6e
Show file tree
Hide file tree
Showing 11 changed files with 283 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package ch.akop.homesystem.config.properties;

import ch.akop.homesystem.openai.ImageRequest;
import lombok.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConstructorBinding;

@Value
@ConstructorBinding
@ConfigurationProperties(prefix = "home-automation.openai")
public class OpenAIProperties {
String apiKey;
ImageRequest.Size size;
}
39 changes: 39 additions & 0 deletions src/main/java/ch/akop/homesystem/openai/ImageRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package ch.akop.homesystem.openai;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonValue;
import lombok.Data;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Data
public class ImageRequest {
private String prompt;
private int n;
private Size size;

@JsonProperty("response_format")
private ResponseFormat responseFormat;

@RequiredArgsConstructor
public enum Size {
SMALL("256x256"),
MEDIUM("512x512"),
BIG("1024x1024");

@Getter
@JsonValue
private final String text;
}

@RequiredArgsConstructor
public enum ResponseFormat {
URL("url"),
B64_JSON("b64_json");

@Getter
@JsonValue
private final String text;

}
}
56 changes: 56 additions & 0 deletions src/main/java/ch/akop/homesystem/openai/OpenAIService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package ch.akop.homesystem.openai;

import ch.akop.homesystem.config.properties.OpenAIProperties;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import java.util.Base64;

import static ch.akop.homesystem.openai.ImageRequest.ResponseFormat.B64_JSON;

@Slf4j
@Service
@RequiredArgsConstructor
public class OpenAIService {

private final OpenAIProperties openAIProperties;
private WebClient apiWebClient;


@PostConstruct
protected void initializeWebClients() {
apiWebClient = WebClient.builder()
.baseUrl("https://api.openai.com/v1/")
.defaultHeaders(header -> header.setBearerAuth(openAIProperties.getApiKey()))
.codecs(codecs -> codecs.defaultCodecs().maxInMemorySize(1024 * 1024 * 10))
.build();
}

@SneakyThrows
public Mono<byte[]> requestImage(String text) {
var requestBody = new ImageRequest()
.setResponseFormat(B64_JSON)
.setN(1)
.setSize(openAIProperties.getSize())
.setPrompt(text);

log.info("Request a {} open-ai image for: {}", requestBody.getSize(), text);

return apiWebClient.post()
.uri("images/generations")
.body(BodyInserters.fromValue(requestBody))
.headers(header -> header.setContentType(MediaType.APPLICATION_JSON))
.retrieve()
.bodyToMono(Response.class)
.map(response -> response.getData().get(0).getB64_json())
.map(b64 -> Base64.getDecoder().decode(b64));
}

}
18 changes: 18 additions & 0 deletions src/main/java/ch/akop/homesystem/openai/Response.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package ch.akop.homesystem.openai;

import lombok.Data;

import java.util.List;

@Data
public class Response {

private List<ResponseData> data;

@Data
public static class ResponseData {
private String url;
private String b64_json;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package ch.akop.homesystem.persistence.model;

import lombok.Getter;
import lombok.NonNull;
import lombok.Setter;

import javax.persistence.*;
import java.time.LocalDateTime;

@Entity
@Table(name = "openai_images")
@Getter
@Setter
public class ImageOfOpenAI {

@Id
private LocalDateTime created = LocalDateTime.now();

@Column(nullable = false)
@NonNull
private String prompt;

@Lob
private byte[] image;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package ch.akop.homesystem.persistence.repository;

import ch.akop.homesystem.persistence.model.ImageOfOpenAI;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.time.LocalDateTime;

@Repository
public interface OpenAIImageRepository extends JpaRepository<ImageOfOpenAI, LocalDateTime> {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package ch.akop.homesystem.services;

public interface ImageCreatorService {

void generateAndSendDailyImage();

}
2 changes: 2 additions & 0 deletions src/main/java/ch/akop/homesystem/services/MessageService.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,7 @@ public interface MessageService {

MessageService sendMessageToMainChannel(@Nullable String message);

MessageService sendImageToMainChannel(@NonNull byte [] image, @NonNull String caption);

MessageService sendMessageToUser(@Nullable String message, @NonNull String chatId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package ch.akop.homesystem.services.impl;

import ch.akop.homesystem.openai.OpenAIService;
import ch.akop.homesystem.persistence.model.ImageOfOpenAI;
import ch.akop.homesystem.persistence.repository.OpenAIImageRepository;
import ch.akop.homesystem.services.ImageCreatorService;
import ch.akop.homesystem.services.MessageService;
import ch.akop.homesystem.services.WeatherService;
import ch.akop.weathercloud.Weather;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.List;

import static ch.akop.weathercloud.rain.RainUnit.MILLIMETER_PER_HOUR;
import static ch.akop.weathercloud.temperature.TemperatureUnit.DEGREE;

@Service
@RequiredArgsConstructor
public class ImageCreatorServiceImpl implements ImageCreatorService {

private final OpenAIService imageService;
private final OpenAIImageRepository imageRepository;
private final MessageService messageService;
private final WeatherService weatherService;


@Override
public void generateAndSendDailyImage() {
var prompt = generatePrompt();
imageService.requestImage(prompt)
.subscribe(image -> {
messageService.sendImageToMainChannel(image, prompt);
imageRepository.save(new ImageOfOpenAI().setPrompt(prompt).setImage(image));
});
}

private String generatePrompt() {
var atTheBeginning = List.of("A swiss house in the mountains with a lake",
"A train passing wonderful mountains",
"A blue Ford Kuga MK 2 on the highway");

var inTheMiddle = weatherService.getWeather()
.take(1)
.map(this::extractTextFromWeather)
.blockingFirst();

var atTheEnd = List.of("as an oil painting",
"as a stained glass window",
"as an abstract pencil and watercolor drawing",
"in digital art",
"as a realistic photograph",
"as a 3D render",
"in Van Gogh style");

return "%s %s %s".formatted(atTheBeginning, inTheMiddle, atTheEnd);
}

private String extractTextFromWeather(Weather weather) {

var isRaining = weather.getRain().isBiggerThan(0, MILLIMETER_PER_HOUR);
var isCold = weather.getOuterTemperatur().isSmallerThan(5, DEGREE);
var isWarm = weather.getOuterTemperatur().isBiggerThan(15, DEGREE);

if (isRaining && isCold) {
return "on cold and rainy day";
}

if (isRaining && isWarm) {
return "on a summer rainy day";
}

if (isRaining) {
return "on a rainy day";
}

if (isCold) {
return "in the winter";
}

if (isWarm) {
return "in the summer";
}

return "";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.pengrad.telegrambot.TelegramBot;
import com.pengrad.telegrambot.model.Update;
import com.pengrad.telegrambot.request.SendMessage;
import com.pengrad.telegrambot.request.SendPhoto;
import com.pengrad.telegrambot.request.SetWebhook;
import io.reactivex.rxjava3.subjects.PublishSubject;
import io.reactivex.rxjava3.subjects.Subject;
Expand Down Expand Up @@ -60,6 +61,15 @@ public MessageService sendMessageToMainChannel(@Nullable String message) {
return sendMessageToUser(message, mainChannel);
}

@Override
public MessageService sendImageToMainChannel(byte @NonNull [] image, @NonNull String caption) {
if (mainChannel == null) {
return this;
}

return sendImageToUser(image, mainChannel, caption);
}

@Override
public MessageService sendMessageToUser(@Nullable String message, @NonNull String chatId) {
return sendMessageToUser(message, List.of(chatId));
Expand All @@ -74,6 +84,15 @@ public MessageService sendMessageToUser(@Nullable String message, @NonNull List<
return this;
}

public MessageService sendImageToUser(byte @NonNull [] image, @NonNull String chatId, @NonNull String text) {
if (bot != null) {
bot.execute(new SendPhoto(chatId, image)
.caption(text));
}

return this;
}

public void process(Update update) {
log.info("Message from {}@{}: {}", update.message().from().firstName(),
update.message().chat().id(),
Expand Down
7 changes: 3 additions & 4 deletions src/main/java/ch/akop/homesystem/states/SleepState.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@
import ch.akop.homesystem.config.properties.HomeSystemProperties;
import ch.akop.homesystem.models.devices.other.Group;
import ch.akop.homesystem.models.devices.other.Scene;
import ch.akop.homesystem.services.DeviceService;
import ch.akop.homesystem.services.MessageService;
import ch.akop.homesystem.services.UserService;
import ch.akop.homesystem.services.WeatherService;
import ch.akop.homesystem.services.*;
import ch.akop.homesystem.services.impl.StateServiceImpl;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.disposables.Disposable;
Expand Down Expand Up @@ -50,6 +47,7 @@ public class SleepState implements State {
private final HomeSystemProperties homeSystemProperties;
private final WeatherService weatherService;
private final UserService userService;
private final ImageCreatorService imageCreatorService;


private Disposable timerDoorOpen;
Expand Down Expand Up @@ -104,6 +102,7 @@ public void turnLightsOff() {
@Override
public void leave() {
messageService.sendMessageToMainChannel(POSSIBLE_MORNING_TEXTS.get(RANDOM.nextInt(POSSIBLE_MORNING_TEXTS.size())));
imageCreatorService.generateAndSendDailyImage();

if (weatherService.isActive()) {
var weather = weatherService.getWeather().blockingFirst();
Expand Down

0 comments on commit 3321b6e

Please sign in to comment.