package de.ph87.data.weather; import lombok.NonNull; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.context.event.ApplicationStartedEvent; import org.springframework.context.event.EventListener; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import tools.jackson.databind.ObjectMapper; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URI; import java.nio.charset.StandardCharsets; import java.time.LocalDate; import java.time.LocalTime; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.List; @Slf4j @Service @EnableScheduling @RequiredArgsConstructor public class WeatherService { private List hours = new ArrayList<>(); private final WeatherConfig weatherConfig; private final ObjectMapper objectMapper; @NonNull public List all() { return new ArrayList<>(hours); } @Scheduled(cron = "0 0 * * * *") @EventListener(ApplicationStartedEvent.class) public void update() { try { final LocalDate today = LocalDate.now(); final LocalDate first = today.minusDays(weatherConfig.getPastDays()); final LocalDate end = today.plusDays(weatherConfig.getFutureDays()); final List hours = new ArrayList<>(); log.debug("Updating Weather..."); for (LocalDate day = first; !day.isAfter(end); day = day.plusDays(1)) { fetchDay(day).getWeather().stream().map(WeatherHour::new).forEach(hours::add); } this.hours = hours; log.info("Weather update complete"); } catch (Exception e) { log.error("Failed fetching Weather data: {}", e.toString()); } } @NonNull public BrightSkyDto fetchDay(@NonNull final LocalDate day) throws IOException { final String url = weatherConfig .getUrlPattern() .replace("{date}", ZonedDateTime.of(day, LocalTime.MIDNIGHT, ZoneId.systemDefault()).format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)) .replace("{latitude}", weatherConfig.getLatitude() + "") .replace("{longitude}", weatherConfig.getLongitude() + ""); final HttpURLConnection connection = (HttpURLConnection) URI.create(url).toURL().openConnection(); final int responseCode = connection.getResponseCode(); final byte[] bytes = connection.getInputStream().readAllBytes(); if (responseCode / 100 != 2) { throw new IOException("responseCode=%d, message: %s".formatted(responseCode, new String(bytes, StandardCharsets.UTF_8))); } return objectMapper.readValue(bytes, BrightSkyDto.class); } }