Explorar o código

Added MET.NO to SensLog and OpenMeteo to SensLog

Lukas Cerny hai 9 meses
pai
achega
355cd32e4e
Modificáronse 29 ficheiros con 981 adicións e 18 borrados
  1. 26 0
      config/metnoToSenslog.yaml
  2. 41 0
      config/openMeteoToSenslog.yaml
  3. 32 0
      connector-fetch-metno/pom.xml
  4. 34 0
      connector-fetch-metno/src/main/java/cz/senslog/connector/fetch/metno/ConnectorFetchMetnoProvider.java
  5. 47 0
      connector-fetch-metno/src/main/java/cz/senslog/connector/fetch/metno/MetnoConfig.java
  6. 95 0
      connector-fetch-metno/src/main/java/cz/senslog/connector/fetch/metno/MetnoFetcher.java
  7. 29 0
      connector-fetch-metno/src/main/java/cz/senslog/connector/fetch/metno/Station.java
  8. 1 0
      connector-fetch-metno/src/main/resources/META-INF/services/cz.senslog.connector.fetch.api.ConnectorFetchProvider
  9. 35 0
      connector-fetch-metno/src/test/java/cz/senslog/connector/fetch/metno/MetnoFetcherTest.java
  10. 32 0
      connector-fetch-openmeteo/pom.xml
  11. 35 0
      connector-fetch-openmeteo/src/main/java/cz/senslog/connector/fetch/openmeteo/ConnectorFetchOpenMeteoProvider.java
  12. 52 0
      connector-fetch-openmeteo/src/main/java/cz/senslog/connector/fetch/openmeteo/OpenMeteoConfig.java
  13. 66 0
      connector-fetch-openmeteo/src/main/java/cz/senslog/connector/fetch/openmeteo/OpenMeteoFetcher.java
  14. 1 0
      connector-fetch-openmeteo/src/main/resources/META-INF/services/cz.senslog.connector.fetch.api.ConnectorFetchProvider
  15. 54 0
      connector-fetch-openmeteo/src/test/java/cz/senslog/connector/fetch/openmeteo/OpenMeteoFetcherTest.java
  16. 56 18
      connector-model/src/main/java/cz/senslog/connector/model/config/PropertyConfig.java
  17. 31 0
      connector-model/src/main/java/cz/senslog/connector/model/converter/MetnoToSensLogConverter.java
  18. 2 0
      connector-model/src/main/java/cz/senslog/connector/model/converter/ModelConverterProvider.java
  19. 31 0
      connector-model/src/main/java/cz/senslog/connector/model/converter/OpenMeteoToSensLogConverter.java
  20. 21 0
      connector-model/src/main/java/cz/senslog/connector/model/metno/MetnoModel.java
  21. 21 0
      connector-model/src/main/java/cz/senslog/connector/model/openmeteo/OpenMeteoModel.java
  22. 47 0
      connector-model/src/main/java/cz/senslog/connector/model/senslog/SensLogModel.java
  23. 39 0
      connector-push-senslog/pom.xml
  24. 29 0
      connector-push-senslog/src/main/java/cz/senslog/connector/push/senslog/ConnectorPushSensLogProvider.java
  25. 16 0
      connector-push-senslog/src/main/java/cz/senslog/connector/push/senslog/SensLogConfig.java
  26. 59 0
      connector-push-senslog/src/main/java/cz/senslog/connector/push/senslog/SensLogPusher.java
  27. 1 0
      connector-push-senslog/src/main/resources/META-INF/services/cz.senslog.connector.push.api.ConnectorPushProvider
  28. 26 0
      docker-compose.yaml
  29. 22 0
      pom.xml

+ 26 - 0
config/metnoToSenslog.yaml

@@ -0,0 +1,26 @@
+settings:
+  - Metno:
+      name: "Metno"
+      provider: "cz.senslog.connector.fetch.metno.ConnectorFetchMetnoProvider"
+
+      baseUrl: "https://api.met.no/weatherapi/locationforecast/2.0/complete"
+      timeZone: "Europe/Prague"
+
+#      allowedStations:
+#        - location: [13.351931666613327, 49.72680307543005] # [lon, lat]
+
+      allowedStationURL: "https://sensor.lesprojekt.cz/senslogOTS3/rest/forecast/locationforecast/plan"
+
+  - Senslog:
+      name: "Senslog Pass Trough"
+      provider: "cz.senslog.connector.push.senslog.ConnectorPushSensLogProvider"
+
+      baseUrl: "https://sensor.lesprojekt.cz/senslogOTS3/rest/forecast/locationforecast"
+
+connectors:
+  - MetnoSenslog:
+      fetcher: "Metno"
+      pusher: "Senslog"
+      period: 3_600          # seconds
+#      startAt: "02:30:00" # hh:mm:ss  # non-mandatory attribute
+      initDelay: 10        # non-mandatory attribute

+ 41 - 0
config/openMeteoToSenslog.yaml

@@ -0,0 +1,41 @@
+settings:
+  - OpenMeteo:
+      name: "Open Meteo"
+      provider: "cz.senslog.connector.fetch.openmeteo.ConnectorFetchOpenMeteoProvider"
+
+      baseUrl: "https://previous-runs-api.open-meteo.com/v1/forecast"
+      timeZone: "Europe/Prague"
+
+      allowedStations:
+        - latitude: 49.72680307543005
+          longitude: 13.351931666613327
+          timezone: GMT
+          past_days: 1
+          hourly: [temperature_2m,temperature_2m_previous_day1,temperature_2m_previous_day2,temperature_2m_previous_day3,temperature_2m_previous_day4,temperature_2m_previous_day5,
+                   relative_humidity_2m,relative_humidity_2m_previous_day1,relative_humidity_2m_previous_day2,relative_humidity_2m_previous_day3,relative_humidity_2m_previous_day4,relative_humidity_2m_previous_day5,
+                   dew_point_2m,dew_point_2m_previous_day1,dew_point_2m_previous_day2,dew_point_2m_previous_day3,dew_point_2m_previous_day4,dew_point_2m_previous_day5,
+                   apparent_temperature,apparent_temperature_previous_day1,apparent_temperature_previous_day2,apparent_temperature_previous_day3,apparent_temperature_previous_day4,apparent_temperature_previous_day5,
+                   precipitation,precipitation_previous_day1,precipitation_previous_day2,precipitation_previous_day3,precipitation_previous_day4,precipitation_previous_day5,
+                   rain,rain_previous_day1,rain_previous_day2,rain_previous_day3,rain_previous_day4,rain_previous_day5,
+                   showers,showers_previous_day1,showers_previous_day2,showers_previous_day3,showers_previous_day4,showers_previous_day5,
+                   snowfall,snowfall_previous_day1,snowfall_previous_day2,snowfall_previous_day3,snowfall_previous_day4,snowfall_previous_day5,
+                   weather_code,weather_code_previous_day1,weather_code_previous_day2,weather_code_previous_day3,weather_code_previous_day4,weather_code_previous_day5,
+                   pressure_msl,pressure_msl_previous_day1,pressure_msl_previous_day2,pressure_msl_previous_day3,pressure_msl_previous_day4,pressure_msl_previous_day5,
+                   surface_pressure,surface_pressure_previous_day1,surface_pressure_previous_day2,surface_pressure_previous_day3,surface_pressure_previous_day4,surface_pressure_previous_day5,
+                   cloud_cover,cloud_cover_previous_day1,cloud_cover_previous_day2,cloud_cover_previous_day3,cloud_cover_previous_day4,cloud_cover_previous_day5,
+                   wind_speed_10m,wind_speed_10m_previous_day1,wind_speed_10m_previous_day2,wind_speed_10m_previous_day3,wind_speed_10m_previous_day4,wind_speed_10m_previous_day5,
+                   wind_direction_10m,wind_direction_10m_previous_day1,wind_direction_10m_previous_day2,wind_direction_10m_previous_day3,wind_direction_10m_previous_day4,wind_direction_10m_previous_day5]
+
+  - Senslog:
+      name: "Senslog Pass Trough"
+      provider: "cz.senslog.connector.push.senslog.ConnectorPushSensLogProvider"
+
+      baseUrl: "https://sensor.lesprojekt.cz"
+
+connectors:
+  - OpenMeteoSenslog:
+      fetcher: "OpenMeteo"
+      pusher: "Senslog"
+      period: 3_600          # seconds
+#      startAt: "02:30:00" # hh:mm:ss  # non-mandatory attribute
+      initDelay: 10        # non-mandatory attribute

+ 32 - 0
connector-fetch-metno/pom.xml

@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>cz.senslog</groupId>
+        <artifactId>connector-period</artifactId>
+        <version>1.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>connector-fetch-metno</artifactId>
+    <name>fetch-metno</name>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>cz.senslog</groupId>
+            <artifactId>connector-fetch-api</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-assembly-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>

+ 34 - 0
connector-fetch-metno/src/main/java/cz/senslog/connector/fetch/metno/ConnectorFetchMetnoProvider.java

@@ -0,0 +1,34 @@
+package cz.senslog.connector.fetch.metno;
+
+import cz.senslog.connector.fetch.api.ConnectorFetchProvider;
+import cz.senslog.connector.fetch.api.ExecutableFetcher;
+import cz.senslog.connector.model.config.DefaultConfig;
+import cz.senslog.connector.model.metno.MetnoModel;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import static cz.senslog.connector.tools.http.HttpClient.newHttpClient;
+
+public class ConnectorFetchMetnoProvider implements ConnectorFetchProvider {
+
+    private static final Logger logger = LogManager.getLogger(ConnectorFetchMetnoProvider.class);
+
+    @Override
+    public ExecutableFetcher<MetnoModel> createExecutableFetcher(DefaultConfig defaultConfig) {
+        logger.info("Initialization a new fetch provider {}.", ConnectorFetchMetnoProvider.class);
+
+        logger.debug("Creating a new configuration.");
+        MetnoConfig config = new MetnoConfig(defaultConfig);
+        logger.info("Configuration for {} was created successfully.", MetnoFetcher.class);
+
+
+        logger.debug("Creating a new instance of {}.", MetnoFetcher.class);
+        MetnoFetcher fetcher = new MetnoFetcher(config, newHttpClient());
+        logger.info("Fetcher for {} was created successfully.", MetnoFetcher.class);
+
+        ExecutableFetcher<MetnoModel> executor = ExecutableFetcher.create(fetcher);
+        logger.info("Fetcher executor for {} was created successfully.", MetnoFetcher.class);
+
+        return executor;
+    }
+}

+ 47 - 0
connector-fetch-metno/src/main/java/cz/senslog/connector/fetch/metno/MetnoConfig.java

@@ -0,0 +1,47 @@
+package cz.senslog.connector.fetch.metno;
+
+import cz.senslog.connector.model.config.DefaultConfig;
+import cz.senslog.connector.model.config.PropertyConfig;
+
+import java.util.*;
+
+public class MetnoConfig {
+
+    private final String url;
+    private final TimeZone timeZone;
+    private final List<Station> stations;
+    private final String allowedStationURL;
+
+    MetnoConfig(DefaultConfig defaultConfig) {
+        this.url = defaultConfig.getStringProperty("baseUrl");
+        this.timeZone = TimeZone.getTimeZone(defaultConfig.getStringProperty("timeZone"));
+
+        this.allowedStationURL = defaultConfig.containsProperty("allowedStationURL") ? defaultConfig.getStringProperty("allowedStationURL") : null;
+        Set<PropertyConfig> stations = defaultConfig.containsProperty("allowedStations") ? defaultConfig.getSetProperty("allowedStations") : Collections.emptySet();
+        this.stations = new ArrayList<>(stations.size());
+        for (PropertyConfig station : stations) {
+            List<Double> locations = station.getListProperty("location", Double.class);
+            this.stations.add(new Station(locations.get(0), locations.get(1)));
+        }
+
+        if (allowedStationURL == null && this.stations.isEmpty()) {
+            throw new IllegalStateException("Metno config requires at least one station");
+        }
+    }
+
+    public String getUrl() {
+        return url;
+    }
+
+    public TimeZone getTimeZone() {
+        return timeZone;
+    }
+
+    public List<Station> getStations() {
+        return stations;
+    }
+
+    public String getAllowedStationURL() {
+        return allowedStationURL;
+    }
+}

+ 95 - 0
connector-fetch-metno/src/main/java/cz/senslog/connector/fetch/metno/MetnoFetcher.java

@@ -0,0 +1,95 @@
+package cz.senslog.connector.fetch.metno;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import cz.senslog.connector.fetch.api.ConnectorFetcher;
+import cz.senslog.connector.model.api.VoidSession;
+import cz.senslog.connector.model.metno.MetnoModel;
+import cz.senslog.connector.tools.http.HttpClient;
+import cz.senslog.connector.tools.http.HttpRequest;
+import cz.senslog.connector.tools.http.HttpResponse;
+import cz.senslog.connector.tools.http.URLBuilder;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.time.OffsetDateTime;
+import java.util.*;
+
+import static cz.senslog.connector.tools.json.BasicJson.jsonToObject;
+
+public class MetnoFetcher implements ConnectorFetcher<VoidSession, MetnoModel> {
+
+    private static final Logger logger = LogManager.getLogger(MetnoFetcher.class);
+
+    private final MetnoConfig config;
+    private final HttpClient httpClient;
+    private final Set<Station> stations;
+
+
+    public MetnoFetcher(MetnoConfig config, HttpClient httpClient) {
+        this.config = config;
+        this.httpClient = httpClient;
+        this.stations = new HashSet<>();
+    }
+
+    @Override
+    public void init() throws Exception {
+
+        stations.addAll(config.getStations());
+
+        if (config.getAllowedStationURL() != null) {
+            HttpRequest.Builder req = HttpRequest.newBuilder().GET()
+                    .url(URLBuilder.newBuilder(config.getAllowedStationURL()).build());
+
+            HttpResponse res = httpClient.send(req.build());
+
+            if (res.isError()) {
+                throw logger.throwing(new RuntimeException(String.format("Error while getting the metno stations from %s.", config.getAllowedStationURL())));
+            }
+
+            JsonArray listJSON = jsonToObject(res.getBody(), JsonArray.class);
+            for (JsonElement element : listJSON) {
+                JsonObject stationJSON = element.getAsJsonObject();
+                JsonObject geometryJSON = stationJSON.get("geometry").getAsJsonObject();
+                String geometryType = geometryJSON.get("type").getAsString();
+                if (geometryType.equals("Point")) {
+                    JsonArray coordinatesJSON = geometryJSON.get("coordinates").getAsJsonArray();
+                    stations.add(new Station(coordinatesJSON.get(0).getAsDouble(), coordinatesJSON.get(1).getAsDouble()));
+                }
+            }
+        }
+
+    }
+
+    @Override
+    public MetnoModel fetch(Optional<VoidSession> session) {
+
+
+        List<JsonObject> predictions = new ArrayList<>(config.getStations().size());
+
+        OffsetDateTime minPredictionDate = OffsetDateTime.MAX;
+        OffsetDateTime maxPredictionDate = OffsetDateTime.MIN;
+
+        for (Station station : stations) {
+
+            HttpRequest.Builder req = HttpRequest.newBuilder().GET()
+                    .url(URLBuilder.newBuilder(config.getUrl())
+                            .addParam("lon", Double.toString(station.getLongitude()))
+                            .addParam("lat", Double.toString(station.getLatitude()))
+                            .build());
+
+            HttpResponse res = httpClient.send(req.build());
+
+            if (res.isError()) {
+                logger.warn("Metno station lat: {}, lon: {} failed to fetch. Error: {}", station.getLatitude(), station.getLongitude(), res.getBody());
+                continue;
+            }
+
+            JsonObject geoJson = jsonToObject(res.getBody(), JsonObject.class);
+            predictions.add(geoJson);
+        }
+
+        return new MetnoModel(minPredictionDate, maxPredictionDate, predictions);
+    }
+}

+ 29 - 0
connector-fetch-metno/src/main/java/cz/senslog/connector/fetch/metno/Station.java

@@ -0,0 +1,29 @@
+package cz.senslog.connector.fetch.metno;
+
+import cz.senslog.connector.model.config.PropertyConfig;
+
+import java.util.List;
+
+public class Station {
+
+    private double latitude, longitude;
+
+    Station(PropertyConfig stationConfig) {
+        List<Double> locations = stationConfig.getListProperty("location", Double.class);
+        this.longitude = locations.get(0);
+        this.latitude = locations.get(1);
+    }
+
+    Station(double latitude, double longitude) {
+        this.latitude = latitude;
+        this.longitude = longitude;
+    }
+
+    public double getLatitude() {
+        return latitude;
+    }
+
+    public double getLongitude() {
+        return longitude;
+    }
+}

+ 1 - 0
connector-fetch-metno/src/main/resources/META-INF/services/cz.senslog.connector.fetch.api.ConnectorFetchProvider

@@ -0,0 +1 @@
+cz.senslog.connector.fetch.metno.ConnectorFetchMetnoProvider

+ 35 - 0
connector-fetch-metno/src/test/java/cz/senslog/connector/fetch/metno/MetnoFetcherTest.java

@@ -0,0 +1,35 @@
+package cz.senslog.connector.fetch.metno;
+
+import cz.senslog.connector.fetch.api.ExecutableFetcher;
+import cz.senslog.connector.model.config.DefaultConfig;
+import cz.senslog.connector.model.converter.MetnoToSensLogConverter;
+import cz.senslog.connector.model.metno.MetnoModel;
+import org.junit.jupiter.api.Test;
+
+import java.util.*;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class MetnoFetcherTest {
+
+    @Test
+    void fetch() throws Exception {
+        ConnectorFetchMetnoProvider provider = new ConnectorFetchMetnoProvider();
+
+        DefaultConfig defaultConfig = new DefaultConfig("", null);
+
+        defaultConfig.setProperty("baseUrl", "https://api.met.no/weatherapi/locationforecast/2.0/complete");
+        defaultConfig.setProperty("timeZone", "Europe/Prague");
+
+        Map<String, Object> st1 = new HashMap<String, Object>() {{
+            put("location", Arrays.asList(13.351931666613327, 49.72680307543005));
+        }};
+
+//        defaultConfig.setProperty("allowedStations", Collections.singletonList(st1));
+        defaultConfig.setProperty("allowedStationURL", "https://sensor.lesprojekt.cz/senslogOTS3/rest/forecast/locationforecast/plan");
+
+        ExecutableFetcher<MetnoModel> fetcher = provider.createExecutableFetcher(defaultConfig);
+        fetcher.getRawFetcher().init();
+        fetcher.execute();
+    }
+}

+ 32 - 0
connector-fetch-openmeteo/pom.xml

@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>cz.senslog</groupId>
+        <artifactId>connector-period</artifactId>
+        <version>1.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>connector-fetch-openmeteo</artifactId>
+    <name>fetch-openmeteo</name>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>cz.senslog</groupId>
+            <artifactId>connector-fetch-api</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-assembly-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>

+ 35 - 0
connector-fetch-openmeteo/src/main/java/cz/senslog/connector/fetch/openmeteo/ConnectorFetchOpenMeteoProvider.java

@@ -0,0 +1,35 @@
+package cz.senslog.connector.fetch.openmeteo;
+
+import cz.senslog.connector.fetch.api.ConnectorFetchProvider;
+import cz.senslog.connector.fetch.api.ExecutableFetcher;
+import cz.senslog.connector.model.config.DefaultConfig;
+import cz.senslog.connector.model.openmeteo.OpenMeteoModel;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import static cz.senslog.connector.tools.http.HttpClient.newHttpClient;
+
+public class ConnectorFetchOpenMeteoProvider implements ConnectorFetchProvider  {
+
+    private static final Logger logger = LogManager.getLogger(ConnectorFetchOpenMeteoProvider.class);
+
+
+    @Override
+    public ExecutableFetcher<OpenMeteoModel> createExecutableFetcher(DefaultConfig defaultConfig) {
+        logger.info("Initialization a new fetch provider {}.", ConnectorFetchOpenMeteoProvider.class);
+
+        logger.debug("Creating a new configuration.");
+        OpenMeteoConfig config = new OpenMeteoConfig(defaultConfig);
+        logger.info("Configuration for {} was created successfully.", OpenMeteoFetcher.class);
+
+
+        logger.debug("Creating a new instance of {}.", OpenMeteoFetcher.class);
+        OpenMeteoFetcher fetcher = new OpenMeteoFetcher(config, newHttpClient());
+        logger.info("Fetcher for {} was created successfully.", OpenMeteoFetcher.class);
+
+        ExecutableFetcher<OpenMeteoModel> executor = ExecutableFetcher.create(fetcher);
+        logger.info("Fetcher executor for {} was created successfully.", OpenMeteoFetcher.class);
+
+        return executor;
+    }
+}

+ 52 - 0
connector-fetch-openmeteo/src/main/java/cz/senslog/connector/fetch/openmeteo/OpenMeteoConfig.java

@@ -0,0 +1,52 @@
+package cz.senslog.connector.fetch.openmeteo;
+
+import cz.senslog.connector.model.config.DefaultConfig;
+import cz.senslog.connector.model.config.PropertyConfig;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.TimeZone;
+
+public class OpenMeteoConfig {
+
+    public static class Station {
+        double latitude, longitude;
+        String timezone;
+        int past_days;
+        List<String> hourly;
+        public Station(PropertyConfig stationConfig) {
+            this.latitude = stationConfig.getDoubleProperty("latitude");
+            this.longitude = stationConfig.getDoubleProperty("longitude");
+            this.timezone = stationConfig.getStringProperty("timezone");
+            this.past_days = stationConfig.getIntegerProperty("past_days");
+            this.hourly = stationConfig.getListProperty("hourly", String.class);
+        }
+    }
+
+    private final String url;
+    private final TimeZone timeZone;
+    private final List<Station> stations;
+
+    OpenMeteoConfig(DefaultConfig defaultConfig) {
+        this.url = defaultConfig.getStringProperty("baseUrl");
+        this.timeZone = TimeZone.getTimeZone(defaultConfig.getStringProperty("timeZone"));
+        Set<PropertyConfig> stations = defaultConfig.getSetProperty("allowedStations");
+        this.stations = new ArrayList<>(stations.size());
+        for (PropertyConfig station : stations) {
+            this.stations.add(new Station(station));
+        }
+    }
+
+    public String getUrl() {
+        return url;
+    }
+
+    public TimeZone getTimeZone() {
+        return timeZone;
+    }
+
+    public List<Station> getStations() {
+        return stations;
+    }
+}

+ 66 - 0
connector-fetch-openmeteo/src/main/java/cz/senslog/connector/fetch/openmeteo/OpenMeteoFetcher.java

@@ -0,0 +1,66 @@
+package cz.senslog.connector.fetch.openmeteo;
+
+import com.google.gson.JsonObject;
+import cz.senslog.connector.fetch.api.ConnectorFetcher;
+import cz.senslog.connector.model.api.VoidSession;
+import cz.senslog.connector.model.openmeteo.OpenMeteoModel;
+import cz.senslog.connector.tools.http.HttpClient;
+import cz.senslog.connector.tools.http.HttpRequest;
+import cz.senslog.connector.tools.http.HttpResponse;
+import cz.senslog.connector.tools.http.URLBuilder;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+import static cz.senslog.connector.tools.json.BasicJson.jsonToObject;
+
+public class OpenMeteoFetcher implements ConnectorFetcher<VoidSession, OpenMeteoModel> {
+
+    private static final Logger logger = LogManager.getLogger(OpenMeteoFetcher.class);
+
+    private final HttpClient httpClient;
+    private final OpenMeteoConfig config;
+
+    public OpenMeteoFetcher(OpenMeteoConfig config, HttpClient httpClient) {
+        this.httpClient = httpClient;
+        this.config = config;
+    }
+
+    @Override
+    public void init() throws Exception {
+
+    }
+
+    @Override
+    public OpenMeteoModel fetch(Optional<VoidSession> session) {
+
+        List<JsonObject> results = new ArrayList<>(config.getStations().size());
+
+        for (OpenMeteoConfig.Station station : config.getStations()) {
+
+            HttpRequest.Builder req = HttpRequest.newBuilder().GET()
+                    .url(URLBuilder.newBuilder(config.getUrl())
+                            .addParam("latitude", Double.toString(station.latitude))
+                            .addParam("longitude", Double.toString(station.longitude))
+                            .addParam("timezone", station.timezone)
+                            .addParam("past_days", Integer.toString(station.past_days))
+                            .addParam("hourly", String.join(",", station.hourly))
+                            .build());
+
+            HttpResponse res = httpClient.send(req.build());
+
+            if (res.isError()) {
+                logger.warn("Open Meteo station lat: {}, lon: {} failed to fetch. Error: {}", station.latitude, station.longitude, res.getBody());
+                continue;
+            }
+
+            JsonObject geoJson = jsonToObject(res.getBody(), JsonObject.class);
+            results.add(geoJson);
+        }
+
+        return new OpenMeteoModel(null, null, results);
+    }
+}

+ 1 - 0
connector-fetch-openmeteo/src/main/resources/META-INF/services/cz.senslog.connector.fetch.api.ConnectorFetchProvider

@@ -0,0 +1 @@
+cz.senslog.connector.fetch.openmeteo.ConnectorFetchOpenMeteoProvider

+ 54 - 0
connector-fetch-openmeteo/src/test/java/cz/senslog/connector/fetch/openmeteo/OpenMeteoFetcherTest.java

@@ -0,0 +1,54 @@
+package cz.senslog.connector.fetch.openmeteo;
+
+import cz.senslog.connector.fetch.api.ExecutableFetcher;
+import cz.senslog.connector.model.config.DefaultConfig;
+import cz.senslog.connector.model.metno.MetnoModel;
+import cz.senslog.connector.model.openmeteo.OpenMeteoModel;
+import org.junit.jupiter.api.Test;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class OpenMeteoFetcherTest {
+
+    @Test
+    void fetch() {
+        ConnectorFetchOpenMeteoProvider provider = new ConnectorFetchOpenMeteoProvider();
+
+        DefaultConfig defaultConfig = new DefaultConfig("", null);
+
+        defaultConfig.setProperty("baseUrl", "https://previous-runs-api.open-meteo.com/v1/forecast");
+        defaultConfig.setProperty("timeZone", "Europe/Prague");
+
+        Map<String, Object> st1 = new HashMap<String, Object>() {{
+            put("latitude", 49.72680307543005);
+            put("longitude", 13.351931666613327);
+            put("timezone", "GMT");
+            put("past_days", 1);
+            put("hourly", Arrays.asList("temperature_2m","temperature_2m_previous_day1","temperature_2m_previous_day2","temperature_2m_previous_day3","temperature_2m_previous_day4","temperature_2m_previous_day5",
+                    "relative_humidity_2m","relative_humidity_2m_previous_day1","relative_humidity_2m_previous_day2","relative_humidity_2m_previous_day3","relative_humidity_2m_previous_day4","relative_humidity_2m_previous_day5",
+                    "dew_point_2m","dew_point_2m_previous_day1","dew_point_2m_previous_day2","dew_point_2m_previous_day3","dew_point_2m_previous_day4","dew_point_2m_previous_day5",
+                    "apparent_temperature","apparent_temperature_previous_day1","apparent_temperature_previous_day2","apparent_temperature_previous_day3","apparent_temperature_previous_day4","apparent_temperature_previous_day5",
+                    "precipitation","precipitation_previous_day1","precipitation_previous_day2","precipitation_previous_day3","precipitation_previous_day4","precipitation_previous_day5",
+                    "rain","rain_previous_day1","rain_previous_day2","rain_previous_day3","rain_previous_day4","rain_previous_day5",
+                    "showers","showers_previous_day1","showers_previous_day2","showers_previous_day3","showers_previous_day4","showers_previous_day5",
+                    "snowfall","snowfall_previous_day1","snowfall_previous_day2","snowfall_previous_day3","snowfall_previous_day4","snowfall_previous_day5",
+                    "weather_code","weather_code_previous_day1","weather_code_previous_day2","weather_code_previous_day3","weather_code_previous_day4","weather_code_previous_day5",
+                    "pressure_msl","pressure_msl_previous_day1","pressure_msl_previous_day2","pressure_msl_previous_day3","pressure_msl_previous_day4","pressure_msl_previous_day5",
+                    "surface_pressure","surface_pressure_previous_day1","surface_pressure_previous_day2","surface_pressure_previous_day3","surface_pressure_previous_day4","surface_pressure_previous_day5",
+                    "cloud_cover","cloud_cover_previous_day1","cloud_cover_previous_day2","cloud_cover_previous_day3","cloud_cover_previous_day4","cloud_cover_previous_day5",
+                    "wind_speed_10m","wind_speed_10m_previous_day1","wind_speed_10m_previous_day2","wind_speed_10m_previous_day3","wind_speed_10m_previous_day4","wind_speed_10m_previous_day5",
+                    "wind_direction_10m","wind_direction_10m_previous_day1","wind_direction_10m_previous_day2","wind_direction_10m_previous_day3","wind_direction_10m_previous_day4","wind_direction_10m_previous_day5"));
+        }};
+
+        defaultConfig.setProperty("allowedStations", Collections.singletonList(st1));
+
+        ExecutableFetcher<OpenMeteoModel> fetcher = provider.createExecutableFetcher(defaultConfig);
+        fetcher.execute();
+
+    }
+}

+ 56 - 18
connector-model/src/main/java/cz/senslog/connector/model/config/PropertyConfig.java

@@ -5,8 +5,8 @@ package cz.senslog.connector.model.config;
 
 import cz.senslog.connector.tools.exception.PropertyNotFoundException;
 import cz.senslog.connector.tools.util.ClassUtils;
-import cz.senslog.connector.tools.util.LocalDateTimeUtils;
 
+import java.lang.reflect.Array;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.LocalTime;
@@ -15,6 +15,8 @@ import java.time.format.DateTimeFormatter;
 import java.util.*;
 
 import static java.lang.String.format;
+import static java.lang.String.valueOf;
+import static java.util.Collections.emptyList;
 import static java.util.Collections.emptySet;
 import static java.util.Optional.ofNullable;
 
@@ -116,6 +118,15 @@ public class PropertyConfig {
     }
 
     /**
+     * Returns property as a Double object.
+     * @param name - name of property.
+     * @return double value.
+     */
+    public Double getDoubleProperty(String name) {
+        return ClassUtils.cast(getProperty(name), Double.class);
+    }
+
+    /**
      * Returns property as a LocalDateTime.
      * @param name - name of property.
      * @return localDateTime value.
@@ -202,22 +213,54 @@ public class PropertyConfig {
      */
     public <T> Set<T> getSetProperty(String name, Class<T> type) {
         Object value = properties.get(name);
-
-        if (value instanceof  List) {
-            List<Object> list = (List<Object>) value;
+        if (value instanceof  Collection) {
+            Collection<?> list = (Collection<?>) value;
             Set<T> res = new HashSet<>(list.size());
             for (Object o : list) {
                 res.add(ClassUtils.cast(o, type));
             }
             return res;
-        } else if (value instanceof Set) {
-            Set<?> set = (Set<?>) value;
-            Set<T> res = new HashSet<>(set.size());
-            for (Object o : set) {
+        }
+
+        return emptySet();
+    }
+
+    public <T> List<T> getListProperty(String name, Class<T> type) {
+        Object object = getProperty(name);
+        if (object instanceof Collection) {
+            Collection<?> list = (Collection<?>) object;
+            List<T> res = new ArrayList<>(list.size());
+            for (Object o : list) {
                 res.add(ClassUtils.cast(o, type));
             }
             return res;
         }
+        return emptyList();
+    }
+
+    private static PropertyConfig mapMapObjectoToPropertyConfig(String id, Object mapObject) {
+        PropertyConfig config = new PropertyConfig(id);
+        if (mapObject instanceof Map) {
+            Map<?, ?> properties = (Map<?, ?>) mapObject;
+            for (Map.Entry<?, ?> propertyEntry : properties.entrySet()) {
+                config.setProperty(propertyEntry.getKey().toString(), propertyEntry.getValue());
+            }
+        }
+        return config;
+    }
+
+    public Set<PropertyConfig> getSetProperty(String name) {
+        Object value = properties.get(name);
+        if (value instanceof  Collection) {
+            Collection<?> collection = (Collection<?>) value;
+            Iterator<?> iter = collection.iterator();
+            Set<PropertyConfig> res = new HashSet<>(collection.size());
+            int index = 0;
+            while (iter.hasNext()) {
+                res.add(mapMapObjectoToPropertyConfig(getNewPropertyId(name, index++), iter.next()));
+            }
+            return res;
+        }
 
         return emptySet();
     }
@@ -229,16 +272,7 @@ public class PropertyConfig {
      */
     public PropertyConfig getPropertyConfig(String name) {
         Object property = getProperty(name);
-        PropertyConfig config = new PropertyConfig(getNewPropertyId(name));
-
-        if (property instanceof Map) {
-            Map<String, Object> properties = (Map<String, Object>) property;
-            for (Map.Entry<String, Object> propertyEntry : properties.entrySet()) {
-                config.setProperty(propertyEntry.getKey(), propertyEntry.getValue());
-            }
-        }
-
-        return config;
+        return mapMapObjectoToPropertyConfig(getNewPropertyId(name), property);
     }
 
     public Set<String> getAttributes() {
@@ -249,6 +283,10 @@ public class PropertyConfig {
         return id + PATH_DELIMITER + name;
     }
 
+    private String getNewPropertyId(String name, int index) {
+        return getNewPropertyId(name) + PATH_DELIMITER + index;
+    }
+
     public String getId() {
         return id;
     }

+ 31 - 0
connector-model/src/main/java/cz/senslog/connector/model/converter/MetnoToSensLogConverter.java

@@ -0,0 +1,31 @@
+package cz.senslog.connector.model.converter;
+
+import com.google.gson.JsonObject;
+import cz.senslog.connector.model.api.Converter;
+import cz.senslog.connector.model.metno.MetnoModel;
+import cz.senslog.connector.model.senslog.SensLogModel;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class MetnoToSensLogConverter implements Converter<MetnoModel, SensLogModel> {
+
+    @Override
+    public SensLogModel convert(MetnoModel model) {
+        if (model == null) {
+             return null;
+        }
+
+        if (model.getPredictions() == null || model.getPredictions().isEmpty()) {
+            return new SensLogModel(model.getFrom(), model.getTo(), Collections.emptyList());
+        }
+
+        List<SensLogModel.PassingData> data = new ArrayList<>(model.getPredictions().size());
+        for (JsonObject prediction : model.getPredictions()) {
+            data.add(new SensLogModel.PassingData(prediction.toString()));
+        }
+
+        return new SensLogModel(model.getFrom(), model.getTo(), data);
+    }
+}

+ 2 - 0
connector-model/src/main/java/cz/senslog/connector/model/converter/ModelConverterProvider.java

@@ -31,5 +31,7 @@ public class ModelConverterProvider extends ConverterProvider {
         register(DemoModelLoggerModelConverter.class);
         register(FofrModelTelemetryModelConverter.class);
         register(SoilscountSenslogV1Converter.class);
+        register(MetnoToSensLogConverter.class);
+        register(OpenMeteoToSensLogConverter.class);
     }
 }

+ 31 - 0
connector-model/src/main/java/cz/senslog/connector/model/converter/OpenMeteoToSensLogConverter.java

@@ -0,0 +1,31 @@
+package cz.senslog.connector.model.converter;
+
+import com.google.gson.JsonObject;
+import cz.senslog.connector.model.api.Converter;
+import cz.senslog.connector.model.openmeteo.OpenMeteoModel;
+import cz.senslog.connector.model.senslog.SensLogModel;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class OpenMeteoToSensLogConverter implements Converter<OpenMeteoModel, SensLogModel> {
+
+    @Override
+    public SensLogModel convert(OpenMeteoModel model) {
+        if (model == null) {
+            return null;
+        }
+
+        if (model.getData() == null || model.getData().isEmpty()) {
+            return new SensLogModel(model.getFrom(), model.getTo(), Collections.emptyList());
+        }
+
+        List<SensLogModel.PassingData> data = new ArrayList<>(model.getData().size());
+        for (JsonObject prediction : model.getData()) {
+            data.add(new SensLogModel.PassingData(prediction.toString()));
+        }
+
+        return new SensLogModel(model.getFrom(), model.getTo(), data);
+    }
+}

+ 21 - 0
connector-model/src/main/java/cz/senslog/connector/model/metno/MetnoModel.java

@@ -0,0 +1,21 @@
+package cz.senslog.connector.model.metno;
+
+import com.google.gson.JsonObject;
+import cz.senslog.connector.model.api.AbstractModel;
+
+import java.time.OffsetDateTime;
+import java.util.List;
+
+public class MetnoModel extends AbstractModel {
+
+    private final List<JsonObject> predictions;
+
+    public MetnoModel(OffsetDateTime from, OffsetDateTime to, List<JsonObject> predictions) {
+        super(from, to);
+        this.predictions = predictions;
+    }
+
+    public List<JsonObject> getPredictions() {
+        return predictions;
+    }
+}

+ 21 - 0
connector-model/src/main/java/cz/senslog/connector/model/openmeteo/OpenMeteoModel.java

@@ -0,0 +1,21 @@
+package cz.senslog.connector.model.openmeteo;
+
+import com.google.gson.JsonObject;
+import cz.senslog.connector.model.api.AbstractModel;
+
+import java.time.OffsetDateTime;
+import java.util.List;
+
+public class OpenMeteoModel extends AbstractModel {
+
+    private final List<JsonObject> data;
+
+    public OpenMeteoModel(OffsetDateTime from, OffsetDateTime to, List<JsonObject> data) {
+        super(from, to);
+        this.data = data;
+    }
+
+    public List<JsonObject> getData() {
+        return data;
+    }
+}

+ 47 - 0
connector-model/src/main/java/cz/senslog/connector/model/senslog/SensLogModel.java

@@ -0,0 +1,47 @@
+package cz.senslog.connector.model.senslog;
+
+import cz.senslog.connector.model.api.AbstractModel;
+
+import java.time.OffsetDateTime;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+public class SensLogModel extends AbstractModel {
+
+    public static final class PassingData {
+        private final Map<String, String> params;
+        private final String payload;
+
+        public PassingData(Map<String, String> params, String payload) {
+            Objects.requireNonNull(params);
+            Objects.requireNonNull(payload);
+            this.params = params;
+            this.payload = payload;
+        }
+
+        public PassingData(String payload) {
+            this(Collections.emptyMap(), payload);
+        }
+
+        public Map<String, String> getParams() {
+            return params;
+        }
+
+        public String getPayload() {
+            return payload;
+        }
+    }
+
+    private final List<PassingData> passThroughData;
+
+    public SensLogModel(OffsetDateTime from, OffsetDateTime to, List<PassingData> passThroughData) {
+        super(from, to);
+        this.passThroughData = passThroughData;
+    }
+
+    public List<PassingData> getPassThroughData() {
+        return passThroughData;
+    }
+}

+ 39 - 0
connector-push-senslog/pom.xml

@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>cz.senslog</groupId>
+        <artifactId>connector-period</artifactId>
+        <version>1.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>connector-push-senslog</artifactId>
+    <name>push-senslog</name>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>cz.senslog</groupId>
+            <artifactId>connector-push-api</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>cz.senslog</groupId>
+            <artifactId>connector-model</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-assembly-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 29 - 0
connector-push-senslog/src/main/java/cz/senslog/connector/push/senslog/ConnectorPushSensLogProvider.java

@@ -0,0 +1,29 @@
+package cz.senslog.connector.push.senslog;
+
+import cz.senslog.connector.model.config.DefaultConfig;
+import cz.senslog.connector.push.api.ConnectorPushProvider;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import static cz.senslog.connector.tools.http.HttpClient.newHttpClient;
+
+public class ConnectorPushSensLogProvider implements ConnectorPushProvider {
+
+    private static final Logger logger = LogManager.getLogger(ConnectorPushSensLogProvider.class);
+
+
+    @Override
+    public SensLogPusher createPusher(DefaultConfig config) {
+        logger.info("Initialization a new push provider {}.", ConnectorPushSensLogProvider.class);
+
+        logger.debug("Creating a new configuration.");
+        SensLogConfig defaultConfig = new SensLogConfig(config);
+        logger.info("Configuration for {} was created successfully.", SensLogPusher.class);
+
+        logger.debug("Creating a new instance of {}.", SensLogPusher.class);
+        SensLogPusher pusher = new SensLogPusher(defaultConfig, newHttpClient());
+        logger.info("Pusher for {} was created successfully.", SensLogPusher.class);
+
+        return pusher;
+    }
+}

+ 16 - 0
connector-push-senslog/src/main/java/cz/senslog/connector/push/senslog/SensLogConfig.java

@@ -0,0 +1,16 @@
+package cz.senslog.connector.push.senslog;
+
+import cz.senslog.connector.model.config.DefaultConfig;
+
+public class SensLogConfig {
+
+    private final String baseUrl;
+
+    SensLogConfig(DefaultConfig defaultConfig) {
+        this.baseUrl = defaultConfig.getStringProperty("baseUrl");
+    }
+
+    public String getBaseUrl() {
+        return baseUrl;
+    }
+}

+ 59 - 0
connector-push-senslog/src/main/java/cz/senslog/connector/push/senslog/SensLogPusher.java

@@ -0,0 +1,59 @@
+package cz.senslog.connector.push.senslog;
+
+import cz.senslog.connector.model.senslog.SensLogModel;
+import cz.senslog.connector.push.api.ConnectorPusher;
+import cz.senslog.connector.tools.http.*;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import static cz.senslog.connector.tools.http.HttpContentType.APPLICATION_JSON;
+
+public class SensLogPusher implements ConnectorPusher<SensLogModel> {
+
+    private static final Logger logger = LogManager.getLogger(SensLogPusher.class);
+
+    private final SensLogConfig config;
+    private final HttpClient httpClient;
+
+    public SensLogPusher(SensLogConfig config, HttpClient httpClient) {
+        this.config = config;
+        this.httpClient = httpClient;
+    }
+
+    @Override
+    public void init() {}
+
+    @Override
+    public void push(SensLogModel model) {
+        if (model == null || model.getPassThroughData() == null || model.getPassThroughData().isEmpty()) {
+            logger.warn("Model has no observations."); return;
+        }
+
+        int successfullyPushed = 0;
+        for (SensLogModel.PassingData data : model.getPassThroughData()) {
+
+            URLBuilder urlBuilder = URLBuilder.newBuilder(config.getBaseUrl());
+            data.getParams().forEach(urlBuilder::addParam);
+
+            HttpRequest.Builder req = HttpRequest.newBuilder().POST()
+                    .url(urlBuilder.build())
+                    .contentType(APPLICATION_JSON)
+                    .body(data.getPayload());
+
+            HttpResponse res = httpClient.send(req.build());
+
+            if (res.isError()) {
+                logger.error("Request error <{}> with reason: {}", res.getStatus(), res.getBody());
+                continue;
+            }
+
+            if (res.isOk()) {
+                logger.info("Pushed <{}> successfully: {}", res.getStatus(), res.getBody());
+                successfullyPushed++;
+            }
+
+        }
+
+        logger.info("Pushed {}/{} payloads.", successfullyPushed, model.getPassThroughData().size());
+    }
+}

+ 1 - 0
connector-push-senslog/src/main/resources/META-INF/services/cz.senslog.connector.push.api.ConnectorPushProvider

@@ -0,0 +1 @@
+cz.senslog.connector.push.senslog.ConnectorPushSensLogProvider

+ 26 - 0
docker-compose.yaml

@@ -132,3 +132,29 @@ services:
       APP_PARAMS: -cf config/soilscountToSenslog.yaml
       DEBUG: "true"
 
+  metno2senslog:
+    container_name: metnosenslog
+    build:
+      dockerfile: docker/Dockerfile
+      context: .
+      args:
+        MAVEN_PROFILE: MetnoSenslog
+    ports:
+      - "5005:5005"
+    environment:
+      APP_PARAMS: -cf config/metnoToSenslog.yaml
+      DEBUG: "true"
+
+  openmeteo2senslog:
+    container_name: openmeteosenslog
+    build:
+      dockerfile: docker/Dockerfile
+      context: .
+      args:
+        MAVEN_PROFILE: OpenMeteoSenslog
+    ports:
+      - "5005:5005"
+    environment:
+      APP_PARAMS: -cf config/openMeteoToSenslog.yaml
+      DEBUG: "true"
+

+ 22 - 0
pom.xml

@@ -101,6 +101,22 @@
         </profile>
 
         <profile>
+            <id>MetnoSenslog</id>
+            <modules>
+                <module>connector-fetch-metno</module>
+                <module>connector-push-senslog</module>
+            </modules>
+        </profile>
+
+        <profile>
+            <id>OpenMeteoSenslog</id>
+            <modules>
+                <module>connector-fetch-openmeteo</module>
+                <module>connector-push-senslog</module>
+            </modules>
+        </profile>
+
+        <profile>
             <id>all</id>
             <activation>
                 <activeByDefault>true</activeByDefault>
@@ -121,6 +137,9 @@
                 <module>connector-fetch-fofr</module>
                 <module>connector-fetch-soilscount</module>
                 <module>connector-push-telemetry</module>
+                <module>connector-fetch-metno</module>
+                <module>connector-fetch-openmeteo</module>
+                <module>connector-push-senslog</module>
             </modules>
         </profile>
     </profiles>
@@ -142,6 +161,9 @@
         <module>connector-fetch-fofr</module>
         <module>connector-fetch-soilscount</module>
         <module>connector-push-telemetry</module>
+        <module>connector-fetch-metno</module>
+        <module>connector-fetch-openmeteo</module>
+        <module>connector-push-senslog</module>
     </modules>
 
     <properties>