From 94301eee8c746f54460e17515a53640592ddd88c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20L=27hopital?= Date: Sun, 3 Apr 2022 18:55:53 +0200 Subject: [PATCH] [synopanalyzer] Incorrect octa reported (#12541) * 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 --- .../README.md | 6 +- .../SynopAnalyzerBindingConstants.java | 5 +- .../internal/SynopAnalyzerHandlerFactory.java | 66 ++-------------- .../config/SynopAnalyzerConfiguration.java | 4 +- .../SynopAnalyzerDiscoveryService.java | 75 +++++++++--------- .../handler/SynopAnalyzerHandler.java | 76 +++++++++---------- .../internal/stationdb/Station.java | 32 ++++++++ .../internal/stationdb/StationDbService.java | 63 +++++++++++++++ .../internal/synop/Overcast.java | 3 + .../internal/synop/StationDB.java | 46 ----------- .../synopanalyzer/internal/synop/Synop.java | 71 +++++++++-------- .../internal/synop/WindDirections.java | 7 +- .../src/main/resources/db/stations.json | 6 +- 13 files changed, 232 insertions(+), 228 deletions(-) create mode 100644 bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/stationdb/Station.java create mode 100644 bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/stationdb/StationDbService.java delete mode 100644 bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/synop/StationDB.java diff --git a/bundles/org.openhab.binding.synopanalyzer/README.md b/bundles/org.openhab.binding.synopanalyzer/README.md index eee855ae3..f87559182 100644 --- a/bundles/org.openhab.binding.synopanalyzer/README.md +++ b/bundles/org.openhab.binding.synopanalyzer/README.md @@ -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 | diff --git a/bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/SynopAnalyzerBindingConstants.java b/bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/SynopAnalyzerBindingConstants.java index cd444e1c4..dc0787771 100644 --- a/bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/SynopAnalyzerBindingConstants.java +++ b/bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/SynopAnalyzerBindingConstants.java @@ -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"); diff --git a/bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/SynopAnalyzerHandlerFactory.java b/bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/SynopAnalyzerHandlerFactory.java index b88e8da1d..1eb9b8bbf 100644 --- a/bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/SynopAnalyzerHandlerFactory.java +++ b/bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/SynopAnalyzerHandlerFactory.java @@ -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 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; - } - } } diff --git a/bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/config/SynopAnalyzerConfiguration.java b/bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/config/SynopAnalyzerConfiguration.java index df352139c..250df2867 100644 --- a/bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/config/SynopAnalyzerConfiguration.java +++ b/bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/config/SynopAnalyzerConfiguration.java @@ -13,11 +13,9 @@ 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 */ diff --git a/bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/discovery/SynopAnalyzerDiscoveryService.java b/bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/discovery/SynopAnalyzerDiscoveryService.java index 72e6dfb11..961012223 100644 --- a/bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/discovery/SynopAnalyzerDiscoveryService.java +++ b/bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/discovery/SynopAnalyzerDiscoveryService.java @@ -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 distances = new HashMap<>(); private final LocationProvider locationProvider; - private final StationDB stationDB; + private final List 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 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 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> stationIterator = distances.entrySet().iterator(); + if (stationIterator.hasNext()) { + Entry nearest = stationIterator.next(); + Station station = nearest.getValue(); + radius = nearest.getKey(); - Integer nearestId = result.entrySet().iterator().next().getKey(); - Optional 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; + } } } diff --git a/bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/handler/SynopAnalyzerHandler.java b/bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/handler/SynopAnalyzerHandler.java index e0974655d..fb857ec63 100644 --- a/bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/handler/SynopAnalyzerHandler.java +++ b/bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/handler/SynopAnalyzerHandler.java @@ -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 @Nullable ScheduledFuture executionJob; - private @NonNullByDefault({}) String formattedStationId; private final LocationProvider locationProvider; - private final @Nullable StationDB stationDB; + private final List stations; - public SynopAnalyzerHandler(Thing thing, LocationProvider locationProvider, @Nullable StationDB stationDB) { + private Optional> refreshJob = Optional.empty(); + private @NonNullByDefault({}) String formattedStationId; + + public SynopAnalyzerHandler(Thing thing, LocationProvider locationProvider, List 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 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 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 = 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 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 index 000000000..0a3882c8b --- /dev/null +++ b/bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/stationdb/Station.java @@ -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 index 000000000..e91a26609 --- /dev/null +++ b/bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/stationdb/StationDbService.java @@ -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 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 getStations() { + return stations; + } +} diff --git a/bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/synop/Overcast.java b/bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/synop/Overcast.java index 8be1d6370..6702e8c48 100644 --- a/bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/synop/Overcast.java +++ b/bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/synop/Overcast.java @@ -12,11 +12,14 @@ */ 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 index a12a9e660..000000000 --- a/bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/synop/StationDB.java +++ /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 stations; -} diff --git a/bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/synop/Synop.java b/bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/synop/Synop.java index 35f879798..52bf0ca6d 100644 --- a/bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/synop/Synop.java +++ b/bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/synop/Synop.java @@ -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 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); } diff --git a/bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/synop/WindDirections.java b/bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/synop/WindDirections.java index 6be4e3f16..baff53d23 100644 --- a/bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/synop/WindDirections.java +++ b/bundles/org.openhab.binding.synopanalyzer/src/main/java/org/openhab/binding/synopanalyzer/internal/synop/WindDirections.java @@ -12,11 +12,14 @@ */ 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)]; } } diff --git a/bundles/org.openhab.binding.synopanalyzer/src/main/resources/db/stations.json b/bundles/org.openhab.binding.synopanalyzer/src/main/resources/db/stations.json index 437668f82..da2ac4e70 100644 --- a/bundles/org.openhab.binding.synopanalyzer/src/main/resources/db/stations.json +++ b/bundles/org.openhab.binding.synopanalyzer/src/main/resources/db/stations.json @@ -1,5 +1,4 @@ -{ - "stations":[ +[ { "country":"FR", "pack":"RADOME", @@ -2420,5 +2419,4 @@ "elevation":1133, "station_type":1 } - ] -} \ No newline at end of file +]