]> git.basschouten.com Git - openhab-addons.git/commitdiff
[synopanalyzer] Incorrect octa reported (#12541)
authorGaël L'hopital <gael@lhopital.org>
Sun, 3 Apr 2022 16:55:53 +0000 (18:55 +0200)
committerGitHub <noreply@github.com>
Sun, 3 Apr 2022 16:55:53 +0000 (18:55 +0200)
* Some stations does not report octa dimension, thus leading the binding to incorrect values.
Enhanced discovery process
Code enhancements
SAT corrections
Enhanced Exception catching.

Signed-off-by: clinique <gael@lhopital.org>
13 files changed:
bundles/org.openhab.binding.synopanalyzer/README.md
bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/SynopAnalyzerBindingConstants.java
bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/SynopAnalyzerHandlerFactory.java
bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/config/SynopAnalyzerConfiguration.java
bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/discovery/SynopAnalyzerDiscoveryService.java
bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/handler/SynopAnalyzerHandler.java
bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/stationdb/Station.java [new file with mode: 0644]
bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/stationdb/StationDbService.java [new file with mode: 0644]
bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/synop/Overcast.java
bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/synop/StationDB.java [deleted file]
bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/synop/Synop.java
bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/synop/WindDirections.java
bundles/org.openhab.binding.synopanalyzer/src/main/resources/db/stations.json

index eee855ae330de5e44b7c981b30030b68017ca24b..f87559182d030ba694cc9ab0d2c4e718c2bff3d6 100644 (file)
@@ -12,7 +12,9 @@ There is exactly one supported thing, which represents a Synop message. It has t
 
 ## Discovery
 
-If a system location is set, the nearest availabble Synop station be automatically discovered for this location.
+If a system location is set, the nearest available Synop station be automatically discovered for this location.
+The search radius will expand at each successive scan.
+
 
 ## Thing Configuration
 
@@ -26,7 +28,7 @@ The weather information that is retrieved is available as these channels:
 
 | Channel Type ID       | Item Type          | Description                                |
 |-----------------------|--------------------|--------------------------------------------|
-| temperature           | Number:Temperature | Current temperature                        |
+| temperature           | Number:Temperature | Current outdoor temperature                |
 | pressure              | Number:Pressure    | Current pressure                           |
 | wind-speed            | Number:Speed       | Current wind speed                         |
 | wind-speed-beaufort   | Number             | Wind speed according to Beaufort scale     |
index cd444e1c4779732579c4803f8b5d8b54d0259387..dc0787771b65fc4d875d581760eb093cf85959da 100644 (file)
@@ -25,14 +25,13 @@ import org.openhab.core.library.unit.Units;
 import org.openhab.core.thing.ThingTypeUID;
 
 /**
- * The {@link SynopAnalyzerBinding} class defines common constants, which are
- * used across the whole binding.
+ * The {@link SynopAnalyzerBinding} class defines common constants used across the whole binding.
  *
  * @author Gaël L'hopital - Initial contribution
  */
 @NonNullByDefault
 public class SynopAnalyzerBindingConstants {
-    public static final String BINDING_ID = "synopanalyzer";
+    private static final String BINDING_ID = "synopanalyzer";
 
     // List of all Thing Type UIDs
     public static final ThingTypeUID THING_SYNOP = new ThingTypeUID(BINDING_ID, "synopanalyzer");
index b88e8da1da65974a780a2564f77bd7aa517c23ea..1eb9b8bbfc620395bc19715ff13b4839f6608c18 100644 (file)
@@ -14,38 +14,25 @@ package org.openhab.binding.synopanalyzer.internal;
 
 import static org.openhab.binding.synopanalyzer.internal.SynopAnalyzerBindingConstants.THING_SYNOP;
 
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.nio.charset.StandardCharsets;
-import java.util.Hashtable;
+import java.util.List;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.synopanalyzer.internal.discovery.SynopAnalyzerDiscoveryService;
 import org.openhab.binding.synopanalyzer.internal.handler.SynopAnalyzerHandler;
-import org.openhab.binding.synopanalyzer.internal.synop.StationDB;
-import org.openhab.core.config.discovery.DiscoveryService;
+import org.openhab.binding.synopanalyzer.internal.stationdb.Station;
+import org.openhab.binding.synopanalyzer.internal.stationdb.StationDbService;
 import org.openhab.core.i18n.LocationProvider;
 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.framework.ServiceRegistration;
-import org.osgi.service.component.ComponentContext;
 import org.osgi.service.component.annotations.Activate;
 import org.osgi.service.component.annotations.Component;
 import org.osgi.service.component.annotations.Reference;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.gson.Gson;
 
 /**
- * The {@link SynopAnalyzerHandlerFactory} is responsible for creating things and thing
- * handlers.
+ * The {@link SynopAnalyzerHandlerFactory} is responsible for creating things and thing handlers.
  *
  * @author Gaël L'hopital - Initial contribution
  */
@@ -53,15 +40,14 @@ import com.google.gson.Gson;
 @Component(service = ThingHandlerFactory.class, configurationPid = "binding.synopanalyzer")
 @NonNullByDefault
 public class SynopAnalyzerHandlerFactory extends BaseThingHandlerFactory {
-    private final Logger logger = LoggerFactory.getLogger(SynopAnalyzerHandlerFactory.class);
     private final LocationProvider locationProvider;
-    private final Gson gson = new Gson();
-    private @Nullable StationDB stationDB;
-    private @Nullable ServiceRegistration<?> serviceReg;
+    private final List<Station> stationDB;
 
     @Activate
-    public SynopAnalyzerHandlerFactory(@Reference LocationProvider locationProvider) {
+    public SynopAnalyzerHandlerFactory(@Reference StationDbService stationDBService,
+            @Reference LocationProvider locationProvider) {
         this.locationProvider = locationProvider;
+        this.stationDB = stationDBService.getStations();
     }
 
     @Override
@@ -74,40 +60,4 @@ public class SynopAnalyzerHandlerFactory extends BaseThingHandlerFactory {
         return supportsThingType(thing.getThingTypeUID()) ? new SynopAnalyzerHandler(thing, locationProvider, stationDB)
                 : null;
     }
-
-    @Override
-    protected void activate(ComponentContext componentContext) {
-        super.activate(componentContext);
-
-        try (InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("/db/stations.json");
-                Reader reader = new InputStreamReader(is, StandardCharsets.UTF_8);) {
-
-            StationDB stations = gson.fromJson(reader, StationDB.class);
-            registerDiscoveryService(stations);
-            this.stationDB = stations;
-            logger.debug("Discovery service for Synop Stations registered.");
-        } catch (IOException e) {
-            logger.warn("Unable to read synop stations database");
-        }
-    }
-
-    @Override
-    protected void deactivate(ComponentContext componentContext) {
-        unregisterDiscoveryService();
-        super.deactivate(componentContext);
-    }
-
-    private void registerDiscoveryService(StationDB stations) {
-        SynopAnalyzerDiscoveryService discoveryService = new SynopAnalyzerDiscoveryService(stations, locationProvider);
-
-        serviceReg = bundleContext.registerService(DiscoveryService.class.getName(), discoveryService,
-                new Hashtable<>());
-    }
-
-    private void unregisterDiscoveryService() {
-        if (serviceReg != null) {
-            serviceReg.unregister();
-            serviceReg = null;
-        }
-    }
 }
index df352139cdd0e3030bcd0afa3f0caf12189d054a..250df2867792ef41092da6ca0fd5e6ec8daae435 100644 (file)
 package org.openhab.binding.synopanalyzer.internal.config;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.binding.synopanalyzer.internal.handler.SynopAnalyzerHandler;
 
 /**
- * The {@link SynopAnalyzerConfiguration} is responsible for holding configuration
- * informations needed for {@link SynopAnalyzerHandler}
+ * The {@link SynopAnalyzerConfiguration} holds configuration informations needed for the Synop thing
  *
  * @author Gaël L'hopital - Initial contribution
  */
index 72e6dfb118077d1c22c467508c0772c7c4b8a0ba..96101222392dbef8150ea6a12604f37ee924654f 100644 (file)
@@ -14,49 +14,50 @@ package org.openhab.binding.synopanalyzer.internal.discovery;
 
 import static org.openhab.binding.synopanalyzer.internal.SynopAnalyzerBindingConstants.THING_SYNOP;
 
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
+import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
-import java.util.Optional;
-import java.util.stream.Collectors;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeMap;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.openhab.binding.synopanalyzer.internal.config.SynopAnalyzerConfiguration;
-import org.openhab.binding.synopanalyzer.internal.synop.StationDB;
-import org.openhab.binding.synopanalyzer.internal.synop.StationDB.Station;
+import org.openhab.binding.synopanalyzer.internal.stationdb.Station;
+import org.openhab.binding.synopanalyzer.internal.stationdb.StationDbService;
 import org.openhab.core.config.discovery.AbstractDiscoveryService;
 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
+import org.openhab.core.config.discovery.DiscoveryService;
 import org.openhab.core.i18n.LocationProvider;
-import org.openhab.core.library.types.DecimalType;
 import org.openhab.core.library.types.PointType;
 import org.openhab.core.thing.ThingUID;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * The {@link SynopAnalyzerDiscoveryService} creates things based on the configured location.
+ * The {@link SynopAnalyzerDiscoveryService} discovers synop stations based on the configured location.
  *
  * @author Gaël L'hopital - Initial Contribution
  */
+@Component(service = DiscoveryService.class)
 @NonNullByDefault
 public class SynopAnalyzerDiscoveryService extends AbstractDiscoveryService {
-    private static final int DISCOVER_TIMEOUT_SECONDS = 5;
+    private static final int DISCOVER_TIMEOUT_SECONDS = 2;
 
     private final Logger logger = LoggerFactory.getLogger(SynopAnalyzerDiscoveryService.class);
-    private final Map<Integer, Double> distances = new HashMap<>();
     private final LocationProvider locationProvider;
-    private final StationDB stationDB;
+    private final List<Station> stations;
+    private double radius = 0;
 
-    /**
-     * Creates a SynopAnalyzerDiscoveryService with enabled autostart.
-     *
-     */
-    public SynopAnalyzerDiscoveryService(StationDB stationDB, LocationProvider locationProvider) {
-        super(Collections.singleton(THING_SYNOP), DISCOVER_TIMEOUT_SECONDS);
+    @Activate
+    public SynopAnalyzerDiscoveryService(@Reference StationDbService dBService,
+            @Reference LocationProvider locationProvider) {
+        super(Set.of(THING_SYNOP), DISCOVER_TIMEOUT_SECONDS);
         this.locationProvider = locationProvider;
-        this.stationDB = stationDB;
+        this.stations = dBService.getStations();
     }
 
     @Override
@@ -71,23 +72,29 @@ public class SynopAnalyzerDiscoveryService extends AbstractDiscoveryService {
     }
 
     public void createResults(PointType serverLocation) {
-        distances.clear();
+        Map<Double, Station> distances = new TreeMap<>();
 
-        stationDB.stations.forEach(s -> {
-            PointType stationLocation = new PointType(s.getLocation());
-            DecimalType distance = serverLocation.distanceFrom(stationLocation);
-            distances.put(s.idOmm, distance.doubleValue());
+        stations.forEach(station -> {
+            PointType stationLocation = new PointType(station.getLocation());
+            double distance = serverLocation.distanceFrom(stationLocation).doubleValue();
+            if (distance > radius) {
+                distances.put(distance, station);
+            }
         });
 
-        Map<Integer, Double> result = distances.entrySet().stream()
-                .sorted(Map.Entry.comparingByValue(Comparator.naturalOrder())).collect(Collectors.toMap(
-                        Map.Entry::getKey, Map.Entry::getValue, (oldValue, newValue) -> oldValue, LinkedHashMap::new));
+        Iterator<Entry<Double, Station>> stationIterator = distances.entrySet().iterator();
+        if (stationIterator.hasNext()) {
+            Entry<Double, Station> nearest = stationIterator.next();
+            Station station = nearest.getValue();
+            radius = nearest.getKey();
 
-        Integer nearestId = result.entrySet().iterator().next().getKey();
-        Optional<Station> station = stationDB.stations.stream().filter(s -> s.idOmm == nearestId).findFirst();
-        thingDiscovered(DiscoveryResultBuilder.create(new ThingUID(THING_SYNOP, nearestId.toString()))
-                .withLabel(String.format("Synop : %s", station.get().usualName))
-                .withProperty(SynopAnalyzerConfiguration.STATION_ID, nearestId)
-                .withRepresentationProperty(SynopAnalyzerConfiguration.STATION_ID).build());
+            thingDiscovered(DiscoveryResultBuilder.create(new ThingUID(THING_SYNOP, Integer.toString(station.idOmm)))
+                    .withLabel(String.format("Synop : %s", station.usualName))
+                    .withProperty(SynopAnalyzerConfiguration.STATION_ID, station.idOmm)
+                    .withRepresentationProperty(SynopAnalyzerConfiguration.STATION_ID).build());
+        } else {
+            logger.info("No Synop station available at a radius higher than {} m - resetting to 0 m", radius);
+            radius = 0;
+        }
     }
 }
index e0974655d2c2e4031f5d81c5a3b8aacb7f1032c1..fb857ec63d155afae72a2f451eb622f131151152 100644 (file)
@@ -33,8 +33,8 @@ import javax.ws.rs.HttpMethod;
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
 import org.openhab.binding.synopanalyzer.internal.config.SynopAnalyzerConfiguration;
+import org.openhab.binding.synopanalyzer.internal.stationdb.Station;
 import org.openhab.binding.synopanalyzer.internal.synop.Overcast;
-import org.openhab.binding.synopanalyzer.internal.synop.StationDB;
 import org.openhab.binding.synopanalyzer.internal.synop.Synop;
 import org.openhab.binding.synopanalyzer.internal.synop.SynopLand;
 import org.openhab.binding.synopanalyzer.internal.synop.SynopMobile;
@@ -51,6 +51,7 @@ import org.openhab.core.library.unit.SIUnits;
 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.types.Command;
 import org.openhab.core.types.RefreshType;
@@ -75,16 +76,16 @@ public class SynopAnalyzerHandler extends BaseThingHandler {
     private static final double OCTA_MAX = 8.0;
 
     private final Logger logger = LoggerFactory.getLogger(SynopAnalyzerHandler.class);
+    private final LocationProvider locationProvider;
+    private final List<Station> stations;
 
-    private @Nullable ScheduledFuture<?> executionJob;
+    private Optional<ScheduledFuture<?>> refreshJob = Optional.empty();
     private @NonNullByDefault({}) String formattedStationId;
-    private final LocationProvider locationProvider;
-    private final @Nullable StationDB stationDB;
 
-    public SynopAnalyzerHandler(Thing thing, LocationProvider locationProvider, @Nullable StationDB stationDB) {
+    public SynopAnalyzerHandler(Thing thing, LocationProvider locationProvider, List<Station> stations) {
         super(thing);
         this.locationProvider = locationProvider;
-        this.stationDB = stationDB;
+        this.stations = stations;
     }
 
     @Override
@@ -94,26 +95,23 @@ public class SynopAnalyzerHandler extends BaseThingHandler {
         logger.info("Scheduling Synop update thread to run every {} minute for Station '{}'",
                 configuration.refreshInterval, formattedStationId);
 
-        StationDB stations = stationDB;
-        if (thing.getProperties().isEmpty() && stations != null) {
-            discoverAttributes(stations, configuration.stationId);
+        if (thing.getProperties().isEmpty()) {
+            discoverAttributes(configuration.stationId, locationProvider.getLocation());
         }
 
         updateStatus(ThingStatus.UNKNOWN);
 
-        executionJob = scheduler.scheduleWithFixedDelay(this::updateSynopChannels, 0, configuration.refreshInterval,
-                TimeUnit.MINUTES);
+        refreshJob = Optional.of(scheduler.scheduleWithFixedDelay(this::updateChannels, 0,
+                configuration.refreshInterval, TimeUnit.MINUTES));
     }
 
-    protected void discoverAttributes(StationDB stations, int stationId) {
-        stations.stations.stream().filter(s -> stationId == s.idOmm).findFirst().ifPresent(s -> {
-            Map<String, String> properties = new HashMap<>();
-            properties.put("Usual name", s.usualName);
-            properties.put("Location", s.getLocation());
+    private void discoverAttributes(int stationId, @Nullable PointType serverLocation) {
+        stations.stream().filter(s -> stationId == s.idOmm).findFirst().ifPresent(station -> {
+            Map<String, String> properties = new HashMap<>(
+                    Map.of("Usual name", station.usualName, "Location", station.getLocation()));
 
-            PointType stationLocation = new PointType(s.getLocation());
-            PointType serverLocation = locationProvider.getLocation();
             if (serverLocation != null) {
+                PointType stationLocation = new PointType(station.getLocation());
                 DecimalType distance = serverLocation.distanceFrom(stationLocation);
 
                 properties.put("Distance", new QuantityType<>(distance, SIUnits.METRE).toString());
@@ -149,33 +147,35 @@ public class SynopAnalyzerHandler extends BaseThingHandler {
         return Optional.empty();
     }
 
-    private void updateSynopChannels() {
+    private void updateChannels() {
         logger.debug("Updating device channels");
 
-        Optional<Synop> synop = getLastAvailableSynop();
-        updateStatus(synop.isPresent() ? ThingStatus.ONLINE : ThingStatus.OFFLINE);
-        synop.ifPresent(theSynop -> {
+        getLastAvailableSynop().ifPresentOrElse(synop -> {
+            updateStatus(ThingStatus.ONLINE);
             getThing().getChannels().forEach(channel -> {
                 String channelId = channel.getUID().getId();
                 if (isLinked(channelId)) {
-                    updateState(channelId, getChannelState(channelId, theSynop));
+                    updateState(channelId, getChannelState(channelId, synop));
                 }
             });
-        });
+        }, () -> updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "No Synop message available"));
     }
 
     private State getChannelState(String channelId, Synop synop) {
+        int octa = synop.getOcta();
         switch (channelId) {
             case HORIZONTAL_VISIBILITY:
                 return new StringType(synop.getHorizontalVisibility().name());
             case OCTA:
-                return new DecimalType(Math.max(0, synop.getOcta()));
+                return octa >= 0 ? new DecimalType(octa) : UnDefType.NULL;
             case ATTENUATION_FACTOR:
-                double kc = Math.max(0, Math.min(synop.getOcta(), OCTA_MAX)) / OCTA_MAX;
-                kc = 1 - 0.75 * Math.pow(kc, KASTEN_POWER);
-                return new DecimalType(kc);
+                if (octa >= 0) {
+                    double kc = Math.max(0, Math.min(octa, OCTA_MAX)) / OCTA_MAX;
+                    kc = 1 - 0.75 * Math.pow(kc, KASTEN_POWER);
+                    return new DecimalType(kc);
+                }
+                return UnDefType.NULL;
             case OVERCAST:
-                int octa = synop.getOcta();
                 Overcast overcast = Overcast.fromOcta(octa);
                 return overcast == Overcast.UNDEFINED ? UnDefType.NULL : new StringType(overcast.name());
             case PRESSURE:
@@ -190,15 +190,14 @@ public class SynopAnalyzerHandler extends BaseThingHandler {
                 return getWindStrength(synop);
             case WIND_SPEED_BEAUFORT:
                 QuantityType<Speed> wsKpH = getWindStrength(synop).toUnit(SIUnits.KILOMETRE_PER_HOUR);
-                return (wsKpH != null) ? new DecimalType(Math.round(Math.pow(wsKpH.floatValue() / 3.01, 0.666666666)))
+                return wsKpH != null ? new DecimalType(Math.round(Math.pow(wsKpH.floatValue() / 3.01, 0.666666666)))
                         : UnDefType.NULL;
             case TIME_UTC:
                 ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC);
                 int year = synop.getYear() == 0 ? now.getYear() : synop.getYear();
                 int month = synop.getMonth() == 0 ? now.getMonth().getValue() : synop.getMonth();
-                ZonedDateTime zdt = ZonedDateTime.of(year, month, synop.getDay(), synop.getHour(), 0, 0, 0,
-                        ZoneOffset.UTC);
-                return new DateTimeType(zdt);
+                return new DateTimeType(
+                        ZonedDateTime.of(year, month, synop.getDay(), synop.getHour(), 0, 0, 0, ZoneOffset.UTC));
             default:
                 logger.error("Unsupported channel Id '{}'", channelId);
                 return UnDefType.UNDEF;
@@ -232,17 +231,14 @@ public class SynopAnalyzerHandler extends BaseThingHandler {
 
     @Override
     public void dispose() {
-        ScheduledFuture<?> job = this.executionJob;
-        if (job != null && !job.isCancelled()) {
-            job.cancel(true);
-        }
-        executionJob = null;
+        refreshJob.ifPresent(job -> job.cancel(true));
+        refreshJob = Optional.empty();
     }
 
     @Override
     public void handleCommand(ChannelUID channelUID, Command command) {
         if (command == RefreshType.REFRESH) {
-            updateSynopChannels();
+            updateChannels();
         }
     }
 }
diff --git a/bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/stationdb/Station.java b/bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/stationdb/Station.java
new file mode 100644 (file)
index 0000000..0a3882c
--- /dev/null
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2010-2022 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.synopanalyzer.internal.stationdb;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The {@link Station} is a DTO for stations.json database.
+ *
+ * @author Gaël L'hopital - Initial Contribution
+ */
+@NonNullByDefault
+public class Station {
+    public int idOmm;
+    public String usualName = "";
+    private double latitude;
+    private double longitude;
+
+    public String getLocation() {
+        return Double.toString(latitude) + "," + Double.toString(longitude);
+    }
+}
diff --git a/bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/stationdb/StationDbService.java b/bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/stationdb/StationDbService.java
new file mode 100644 (file)
index 0000000..e91a266
--- /dev/null
@@ -0,0 +1,63 @@
+/**
+ * Copyright (c) 2010-2022 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.synopanalyzer.internal.stationdb;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.FieldNamingPolicy;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonIOException;
+import com.google.gson.JsonSyntaxException;
+
+/**
+ * The {@link StationDbService} makes available a list of known Synop stations.
+ *
+ * @author Gaël L'hopital - Initial Contribution
+ */
+
+@Component(service = StationDbService.class)
+@NonNullByDefault
+public class StationDbService {
+    private final Logger logger = LoggerFactory.getLogger(StationDbService.class);
+
+    private List<Station> stations;
+
+    @Activate
+    public StationDbService() {
+        try (InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("/db/stations.json");
+                Reader reader = new InputStreamReader(is, StandardCharsets.UTF_8);) {
+            Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
+            stations = Arrays.asList(gson.fromJson(reader, Station[].class));
+        } catch (IOException | JsonSyntaxException | JsonIOException e) {
+            logger.warn("Unable to load station list : {}", e.getMessage());
+            stations = List.of();
+        }
+    }
+
+    public List<Station> getStations() {
+        return stations;
+    }
+}
index 8be1d6370adba1a3797aa0e6ddec1813e601b554..6702e8c4806083c40f8e7e2c0fe49b8283293857 100644 (file)
  */
 package org.openhab.binding.synopanalyzer.internal.synop;
 
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
 /**
  * The {@link WindDirections} enum possible overcast descriptions
  *
  * @author Gaël L'hopital - Initial contribution
  */
+@NonNullByDefault
 public enum Overcast {
     UNDEFINED,
     CLEAR_SKY,
diff --git a/bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/synop/StationDB.java b/bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/synop/StationDB.java
deleted file mode 100644 (file)
index a12a9e6..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/**
- * Copyright (c) 2010-2022 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.synopanalyzer.internal.synop;
-
-import java.util.List;
-
-import com.google.gson.annotations.SerializedName;
-
-/**
- * The {@link StationDB} creates is a DTO for stations.json database.
- *
- * @author Gaël L'hopital - Initial Contribution
- */
-public class StationDB {
-    public class Station {
-        public String country;
-        public String pack;
-        @SerializedName("id_omm")
-        public int idOmm;
-        @SerializedName("numer_sta")
-        public long numerSta;
-        @SerializedName("usual_name")
-        public String usualName;
-        public double latitude;
-        public double longitude;
-        public double elevation;
-        @SerializedName("station_type")
-        public int stationType;
-
-        public String getLocation() {
-            return Double.toString(latitude) + "," + Double.toString(longitude);
-        }
-    }
-
-    public List<Station> stations;
-}
index 35f8797986ed3fffb4d3c8000a3ac8c2522bd25c..52bf0ca6d7087beca4aa02c6174ca783b1a43641 100644 (file)
@@ -30,14 +30,14 @@ import org.openhab.core.library.unit.Units;
 @NonNullByDefault
 public abstract class Synop {
     protected static final int INITIAL_VALUE = -1000;
-    protected static final char PLUS_SIGN_TEMPERATURE = '0';
-    protected static final char MINUS_SIGN_TEMPERATURE = '1';
+    private static final char PLUS_SIGN_TEMPERATURE = '0';
+    private static final char MINUS_SIGN_TEMPERATURE = '1';
 
     /*
      * WS - WIND SPEED
      */
-    protected static final int WS_WILDTYPE_IN_MPS = 0;
-    protected static final int WS_ANEMOMETER_IN_MPS = 1;
+    private static final int WS_WILDTYPE_IN_MPS = 0;
+    private static final int WS_ANEMOMETER_IN_MPS = 1;
 
     /*
      * HV - HORIZONTAL VISIBILITY [IN KILOMETERS]
@@ -59,12 +59,12 @@ public abstract class Synop {
      * 99 - > 50 km
      * HP - high precision
      */
-    protected static final int HV_LESS_THAN_1_LIMIT = 10;
-    protected static final int HV_LESS_THAN_10_LIMIT = 60;
-    protected static final int HV_LESS_THAN_50_LIMIT = 84;
-    protected static final int HV_LESS_THAN_1_HP_LIMIT = 93;
-    protected static final int HV_LESS_THAN_10_HP_LIMIT = 96;
-    protected static final int HV_LESS_THAN_50_HP_LIMIT = 98;
+    private static final int HV_LESS_THAN_1_LIMIT = 10;
+    private static final int HV_LESS_THAN_10_LIMIT = 60;
+    private static final int HV_LESS_THAN_50_LIMIT = 84;
+    private static final int HV_LESS_THAN_1_HP_LIMIT = 93;
+    private static final int HV_LESS_THAN_10_HP_LIMIT = 96;
+    private static final int HV_LESS_THAN_50_HP_LIMIT = 98;
 
     public static enum HorizontalVisibility {
         UNDEFINED,
@@ -74,7 +74,7 @@ public abstract class Synop {
         MORE_THAN_50
     }
 
-    private final int VALID_STRING_LENGTH = 5;
+    private static final int VALID_STRING_LENGTH = 5;
 
     protected final List<String> stringArray;
 
@@ -107,6 +107,14 @@ public abstract class Synop {
         setPressure();
     }
 
+    protected abstract void setTemperatureString();
+
+    protected abstract void setHorizontalVisibilityInt();
+
+    protected abstract void setPressureString();
+
+    protected abstract void setWindString();
+
     private void setDateHourAndWindIndicator() {
         String dayHourAndWindIndicator = "";
 
@@ -147,9 +155,7 @@ public abstract class Synop {
 
     private void setHorizontalVisibility() {
         setHorizontalVisibilityInt();
-
         if (horizontalVisibilityInt != INITIAL_VALUE) {
-
             if (horizontalVisibilityInt < HV_LESS_THAN_1_LIMIT || horizontalVisibilityInt < HV_LESS_THAN_1_HP_LIMIT) {
                 horizontalVisibility = HorizontalVisibility.LESS_THAN_1;
             } else if (horizontalVisibilityInt < HV_LESS_THAN_10_LIMIT
@@ -166,8 +172,6 @@ public abstract class Synop {
         }
     }
 
-    protected abstract void setHorizontalVisibilityInt();
-
     private void setTemperature() {
         setTemperatureString();
         temperature = INITIAL_VALUE;
@@ -183,12 +187,11 @@ public abstract class Synop {
         }
     }
 
-    protected abstract void setTemperatureString();
-
     private void setWindAndOvercast() {
         setWindString();
-        if (windString != null) {
-            String gustyFlag = windString.substring(0, 2);
+        String localWind = windString;
+        if (localWind != null) {
+            String gustyFlag = localWind.substring(0, 2);
             if ("00".equals(gustyFlag)) {
                 setWindSpeed(true);
             } else {
@@ -203,18 +206,20 @@ public abstract class Synop {
     }
 
     private void setOcta() {
-        if (windString != null) {
-            octa = Character.getNumericValue(windString.charAt(0));
+        String localWind = windString;
+        if (localWind != null) {
+            octa = Character.getNumericValue(localWind.charAt(0));
         } else {
             octa = -1;
         }
     }
 
     private void setWindDirection() {
-        if (windString != null) {
-            String windDirectionString = windString.substring(1, 3);
+        String localWind = windString;
+        if (localWind != null) {
+            String windDirectionString = localWind.substring(1, 3);
 
-            if (windDirectionString.equals("99") || windDirectionString.equals("||")) {
+            if ("99".equals(windDirectionString) || "||".equals(windDirectionString)) {
                 windDirection = INITIAL_VALUE;
             } else {
                 try {
@@ -228,8 +233,9 @@ public abstract class Synop {
 
     private void setWindSpeed(boolean gustyWind) {
         String speedString = null;
-        if (windString != null) {
-            speedString = windString.substring(gustyWind ? 2 : 3, 5);
+        String localWind = windString;
+        if (localWind != null) {
+            speedString = localWind.substring(gustyWind ? 2 : 3, 5);
             try {
                 windSpeed = Integer.parseInt(speedString);
             } catch (NumberFormatException e) {
@@ -238,19 +244,14 @@ public abstract class Synop {
         }
     }
 
-    protected abstract void setWindString();
-
     private void setPressure() {
         setPressureString();
-
-        if (pressureString != null) {
-
-            String pressureTemp = pressureString.substring(1, 5);
-
+        String localPressure = pressureString;
+        if (localPressure != null) {
+            String pressureTemp = localPressure.substring(1, 5);
             if (pressureTemp.charAt(0) == '0') {
                 pressureTemp = '1' + pressureTemp;
             }
-
             try {
                 pressure = (float) Integer.parseInt(pressureTemp) / 10;
             } catch (NumberFormatException e) {
@@ -259,8 +260,6 @@ public abstract class Synop {
         }
     }
 
-    protected abstract void setPressureString();
-
     protected boolean isValidString(String str) {
         return (str.length() == VALID_STRING_LENGTH);
     }
index 6be4e3f162dd13f369749e1a3876b2ace05cdd56..baff53d23b448893e7c27206784ad2a8b0d4b5fc 100644 (file)
  */
 package org.openhab.binding.synopanalyzer.internal.synop;
 
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
 /**
  * The {@link WindDirections} enum possible wind directions
  *
  * @author Gaël L'hopital - Initial contribution
  */
+@NonNullByDefault
 public enum WindDirections {
     N,
     NNE,
@@ -39,8 +42,8 @@ public enum WindDirections {
      * Returns the wind direction based on degree.
      */
     public static WindDirections getWindDirection(int degree) {
-        double step = 360.0 / WindDirections.values().length;
+        double step = 360.0 / values().length;
         double b = Math.floor((degree + (step / 2.0)) / step);
-        return WindDirections.values()[(int) (b % WindDirections.values().length)];
+        return values()[(int) (b % values().length)];
     }
 }
index 437668f82190258d0ac8521598d0d0b98f442e77..da2ac4e70130317445447713a8fc85b2958c2f74 100644 (file)
@@ -1,5 +1,4 @@
-{
-   "stations":[
+[
       {
          "country":"FR",
          "pack":"RADOME",
          "elevation":1133,
          "station_type":1
       }
-   ]
-}
\ No newline at end of file
+]