[Synopanalyzer] Adding French localization, correction on overcast channel (#10113)

* Adding French localization of the binding
* Correction on overcast channel
* Correcting nullable issues

Signed-off-by: clinique <gael@lhopital.org>
This commit is contained in:
Gaël L'hopital 2021-02-17 11:50:14 +01:00 committed by GitHub
parent cb9a118581
commit da5c4c8650
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 85 additions and 42 deletions

View File

@ -42,11 +42,12 @@ import org.slf4j.LoggerFactory;
*/ */
@NonNullByDefault @NonNullByDefault
public class SynopAnalyzerDiscoveryService extends AbstractDiscoveryService { public class SynopAnalyzerDiscoveryService extends AbstractDiscoveryService {
private final Logger logger = LoggerFactory.getLogger(SynopAnalyzerDiscoveryService.class);
private static final int DISCOVER_TIMEOUT_SECONDS = 5; private static final int DISCOVER_TIMEOUT_SECONDS = 5;
private LocationProvider locationProvider;
private final StationDB stationDB; private final Logger logger = LoggerFactory.getLogger(SynopAnalyzerDiscoveryService.class);
private final Map<Integer, Double> distances = new HashMap<>(); private final Map<Integer, Double> distances = new HashMap<>();
private final LocationProvider locationProvider;
private final StationDB stationDB;
/** /**
* Creates a SynopAnalyzerDiscoveryService with enabled autostart. * Creates a SynopAnalyzerDiscoveryService with enabled autostart.
@ -84,8 +85,8 @@ public class SynopAnalyzerDiscoveryService extends AbstractDiscoveryService {
Integer nearestId = result.entrySet().iterator().next().getKey(); Integer nearestId = result.entrySet().iterator().next().getKey();
Optional<Station> station = stationDB.stations.stream().filter(s -> s.idOmm == nearestId).findFirst(); Optional<Station> station = stationDB.stations.stream().filter(s -> s.idOmm == nearestId).findFirst();
thingDiscovered(DiscoveryResultBuilder.create(new ThingUID(THING_SYNOP, Integer.toString(nearestId))) thingDiscovered(DiscoveryResultBuilder.create(new ThingUID(THING_SYNOP, nearestId.toString()))
.withLabel("Synop : " + station.get().usualName) .withLabel(String.format("Synop : %s", station.get().usualName))
.withProperty(SynopAnalyzerConfiguration.STATION_ID, nearestId) .withProperty(SynopAnalyzerConfiguration.STATION_ID, nearestId)
.withRepresentationProperty(SynopAnalyzerConfiguration.STATION_ID).build()); .withRepresentationProperty(SynopAnalyzerConfiguration.STATION_ID).build());
} }

View File

@ -29,7 +29,7 @@ public enum Overcast {
public static Overcast fromOcta(int octa) { public static Overcast fromOcta(int octa) {
if (octa == 0) { if (octa == 0) {
return Overcast.CLEAR_SKY; return Overcast.CLEAR_SKY;
} else if (octa > 0 && octa < 8) { } else if (octa > 0 && octa < 9) {
return Overcast.CLOUDY; return Overcast.CLOUDY;
} else if (octa == 9) { } else if (octa == 9) {
return Overcast.SKY_NOT_VISIBLE; return Overcast.SKY_NOT_VISIBLE;

View File

@ -18,9 +18,8 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.Reader; import java.io.Reader;
import java.util.Collections; import java.nio.charset.StandardCharsets;
import java.util.Hashtable; import java.util.Hashtable;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
@ -55,28 +54,25 @@ import com.google.gson.Gson;
@NonNullByDefault @NonNullByDefault
public class SynopAnalyzerHandlerFactory extends BaseThingHandlerFactory { public class SynopAnalyzerHandlerFactory extends BaseThingHandlerFactory {
private final Logger logger = LoggerFactory.getLogger(SynopAnalyzerHandlerFactory.class); private final Logger logger = LoggerFactory.getLogger(SynopAnalyzerHandlerFactory.class);
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_SYNOP);
private final LocationProvider locationProvider; private final LocationProvider locationProvider;
private final Gson gson; private final Gson gson = new Gson();
private @NonNullByDefault({}) StationDB stationDB; private @Nullable StationDB stationDB;
private @Nullable ServiceRegistration<?> serviceReg; private @Nullable ServiceRegistration<?> serviceReg;
@Activate @Activate
public SynopAnalyzerHandlerFactory(@Reference LocationProvider locationProvider) { public SynopAnalyzerHandlerFactory(@Reference LocationProvider locationProvider) {
this.locationProvider = locationProvider; this.locationProvider = locationProvider;
this.gson = new Gson();
} }
@Override @Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) { public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID); return THING_SYNOP.equals(thingTypeUID);
} }
@Override @Override
protected @Nullable ThingHandler createHandler(Thing thing) { protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID(); return supportsThingType(thing.getThingTypeUID()) ? new SynopAnalyzerHandler(thing, locationProvider, stationDB)
: null;
return thingTypeUID.equals(THING_SYNOP) ? new SynopAnalyzerHandler(thing, locationProvider, stationDB) : null;
} }
@Override @Override
@ -84,14 +80,14 @@ public class SynopAnalyzerHandlerFactory extends BaseThingHandlerFactory {
super.activate(componentContext); super.activate(componentContext);
try (InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("/db/stations.json"); try (InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("/db/stations.json");
Reader reader = new InputStreamReader(is, "UTF-8");) { Reader reader = new InputStreamReader(is, StandardCharsets.UTF_8);) {
stationDB = gson.fromJson(reader, StationDB.class); StationDB stations = gson.fromJson(reader, StationDB.class);
registerDiscoveryService(); registerDiscoveryService(stations);
this.stationDB = stations;
logger.debug("Discovery service for Synop Stations registered."); logger.debug("Discovery service for Synop Stations registered.");
} catch (IOException e) { } catch (IOException e) {
logger.warn("Unable to read synop stations database"); logger.warn("Unable to read synop stations database");
stationDB = new StationDB();
} }
} }
@ -101,8 +97,8 @@ public class SynopAnalyzerHandlerFactory extends BaseThingHandlerFactory {
super.deactivate(componentContext); super.deactivate(componentContext);
} }
private void registerDiscoveryService() { private void registerDiscoveryService(StationDB stations) {
SynopAnalyzerDiscoveryService discoveryService = new SynopAnalyzerDiscoveryService(stationDB, locationProvider); SynopAnalyzerDiscoveryService discoveryService = new SynopAnalyzerDiscoveryService(stations, locationProvider);
serviceReg = bundleContext.registerService(DiscoveryService.class.getName(), discoveryService, serviceReg = bundleContext.registerService(DiscoveryService.class.getName(), discoveryService,
new Hashtable<>()); new Hashtable<>());

View File

@ -28,12 +28,12 @@ import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.measure.quantity.Speed; import javax.measure.quantity.Speed;
import javax.ws.rs.HttpMethod;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.synopanalyser.internal.synop.Overcast; import org.openhab.binding.synopanalyser.internal.synop.Overcast;
import org.openhab.binding.synopanalyser.internal.synop.StationDB; import org.openhab.binding.synopanalyser.internal.synop.StationDB;
import org.openhab.binding.synopanalyser.internal.synop.StationDB.Station;
import org.openhab.binding.synopanalyser.internal.synop.Synop; import org.openhab.binding.synopanalyser.internal.synop.Synop;
import org.openhab.binding.synopanalyser.internal.synop.SynopLand; import org.openhab.binding.synopanalyser.internal.synop.SynopLand;
import org.openhab.binding.synopanalyser.internal.synop.SynopMobile; import org.openhab.binding.synopanalyser.internal.synop.SynopMobile;
@ -77,12 +77,11 @@ public class SynopAnalyzerHandler extends BaseThingHandler {
private final Logger logger = LoggerFactory.getLogger(SynopAnalyzerHandler.class); private final Logger logger = LoggerFactory.getLogger(SynopAnalyzerHandler.class);
private @Nullable ScheduledFuture<?> executionJob; private @Nullable ScheduledFuture<?> executionJob;
// private @NonNullByDefault({}) SynopAnalyzerConfiguration configuration;
private @NonNullByDefault({}) String formattedStationId; private @NonNullByDefault({}) String formattedStationId;
private final LocationProvider locationProvider; private final LocationProvider locationProvider;
private final StationDB stationDB; private final @Nullable StationDB stationDB;
public SynopAnalyzerHandler(Thing thing, LocationProvider locationProvider, StationDB stationDB) { public SynopAnalyzerHandler(Thing thing, LocationProvider locationProvider, @Nullable StationDB stationDB) {
super(thing); super(thing);
this.locationProvider = locationProvider; this.locationProvider = locationProvider;
this.stationDB = stationDB; this.stationDB = stationDB;
@ -95,20 +94,18 @@ public class SynopAnalyzerHandler extends BaseThingHandler {
logger.info("Scheduling Synop update thread to run every {} minute for Station '{}'", logger.info("Scheduling Synop update thread to run every {} minute for Station '{}'",
configuration.refreshInterval, formattedStationId); configuration.refreshInterval, formattedStationId);
if (thing.getProperties().isEmpty()) { StationDB stations = stationDB;
discoverAttributes(configuration.stationId); if (thing.getProperties().isEmpty() && stations != null) {
discoverAttributes(stations, configuration.stationId);
} }
executionJob = scheduler.scheduleWithFixedDelay(this::updateSynopChannels, 0, configuration.refreshInterval, executionJob = scheduler.scheduleWithFixedDelay(this::updateSynopChannels, 0, configuration.refreshInterval,
TimeUnit.MINUTES); TimeUnit.MINUTES);
updateStatus(ThingStatus.UNKNOWN);
} }
protected void discoverAttributes(int stationId) { protected void discoverAttributes(StationDB stations, int stationId) {
final Map<String, String> properties = new HashMap<>(); stations.stations.stream().filter(s -> stationId == s.idOmm).findFirst().ifPresent(s -> {
Map<String, String> properties = new HashMap<>();
Optional<Station> station = stationDB.stations.stream().filter(s -> stationId == s.idOmm).findFirst();
station.ifPresent(s -> {
properties.put("Usual name", s.usualName); properties.put("Usual name", s.usualName);
properties.put("Location", s.getLocation()); properties.put("Location", s.getLocation());
@ -119,9 +116,8 @@ public class SynopAnalyzerHandler extends BaseThingHandler {
properties.put("Distance", new QuantityType<>(distance, SIUnits.METRE).toString()); properties.put("Distance", new QuantityType<>(distance, SIUnits.METRE).toString());
} }
updateProperties(properties);
}); });
updateProperties(properties);
} }
private Optional<Synop> getLastAvailableSynop() { private Optional<Synop> getLastAvailableSynop() {
@ -129,7 +125,7 @@ public class SynopAnalyzerHandler extends BaseThingHandler {
String url = forgeURL(); String url = forgeURL();
try { try {
String answer = HttpUtil.executeUrl("GET", url, REQUEST_TIMEOUT_MS); String answer = HttpUtil.executeUrl(HttpMethod.GET, url, REQUEST_TIMEOUT_MS);
List<String> messages = Arrays.asList(answer.split("\n")); List<String> messages = Arrays.asList(answer.split("\n"));
if (!messages.isEmpty()) { if (!messages.isEmpty()) {
String message = messages.get(messages.size() - 1); String message = messages.get(messages.size() - 1);
@ -159,7 +155,9 @@ public class SynopAnalyzerHandler extends BaseThingHandler {
synop.ifPresent(theSynop -> { synop.ifPresent(theSynop -> {
getThing().getChannels().forEach(channel -> { getThing().getChannels().forEach(channel -> {
String channelId = channel.getUID().getId(); String channelId = channel.getUID().getId();
updateState(channelId, getChannelState(channelId, theSynop)); if (isLinked(channelId)) {
updateState(channelId, getChannelState(channelId, theSynop));
}
}); });
}); });
} }

View File

@ -0,0 +1,44 @@
# binding
binding.synopanalyzer.name = Extension Synop Analyzer
binding.synopanalyzer.description = Synop Analyzer permet de télécharger et interpréter les messages SYNOP.
# thing type
thing-type.synopanalyzer.synopanalyzer.label = Message Synop
thing-type.synopanalyzer.synopanalyzer.description = Décodage du dernier message d'une station Synop.
# channel types
channel-type.synopanalyzer.wind-speed-beaufort.label = Beaufort
channel-type.synopanalyzer.wind-speed-beaufort.description = Force du vent sur l'échelle Beaufort.
channel-type.synopanalyzer.wind-direction.label = Direction du vent
channel-type.synopanalyzer.wind-direction.description = Equivalent cardinal de la direction du vent.
# Only translating those that needs a french adaptation (containing "W")
channel-type.synopanalyzer.wind-direction.state.option.SSW = SSO
channel-type.synopanalyzer.wind-direction.state.option.SW = SO
channel-type.synopanalyzer.wind-direction.state.option.WSW = OSO
channel-type.synopanalyzer.wind-direction.state.option.W = O
channel-type.synopanalyzer.wind-direction.state.option.WNW = ONO
channel-type.synopanalyzer.wind-direction.state.option.NW = NO
channel-type.synopanalyzer.wind-direction.state.option.NNW = NNO
channel-type.synopanalyzer.octa.label = Octa
channel-type.synopanalyzer.octa.description = Evaluation de la couverture nuageuse.
channel-type.synopanalyzer.attenuation-factor.label = Coefficient d'atténuation
channel-type.synopanalyzer.attenuation-factor.description = Atténuation générée par la couverture nuageuse.
channel-type.synopanalyzer.overcast.label = Couverture nuageuse
channel-type.synopanalyzer.overcast.description = Appréciation de la couverture nuageuse.
channel-type.synopanalyzer.overcast.state.option.CLEAR_SKY = Ciel dégagé
channel-type.synopanalyzer.overcast.state.option.CLOUDY = Nuageux
channel-type.synopanalyzer.overcast.state.option.SKY_NOT_VISIBLE = Ciel non visible
channel-type.synopanalyzer.horizontal-visibility.label = Visibilité horizontale
channel-type.synopanalyzer.horizontal-visibility.description = Ordre de grandeur de la visibilité horizontale.
channel-type.synopanalyzer.horizontal-visibility.state.option.LESS_THAN_1 = Moins de 1 km
channel-type.synopanalyzer.horizontal-visibility.state.option.LESS_THAN_10 = Entre 1 et 10 km
channel-type.synopanalyzer.horizontal-visibility.state.option.LESS_THAN_50 = Entre 10 et 50 km
channel-type.synopanalyzer.horizontal-visibility.state.option.MORE_THAN_50 = Plus de 50 km
channel-type.synopanalyzer.time-utc.label = Horodatage
channel-type.synopanalyzer.time-utc.description = Heure d'observation des mesures relevées

View File

@ -6,14 +6,14 @@
<thing-type id="synopanalyzer"> <thing-type id="synopanalyzer">
<label>Synop Message</label> <label>Synop Message</label>
<description>The Synop Analyzer binding decodes Synop messages</description> <description>This is the interpretation of the last message of a given station.</description>
<channels> <channels>
<channel id="temperature" typeId="system.outdoor-temperature"/> <channel id="temperature" typeId="system.outdoor-temperature"/>
<channel id="pressure" typeId="system.barometric-pressure"/> <channel id="pressure" typeId="system.barometric-pressure"/>
<channel id="wind-angle" typeId="system.wind-direction"/> <channel id="wind-angle" typeId="system.wind-direction"/>
<channel id="wind-direction" typeId="wind-direction"/>
<channel id="wind-speed" typeId="system.wind-speed"/> <channel id="wind-speed" typeId="system.wind-speed"/>
<channel id="wind-direction" typeId="wind-direction"/>
<channel id="wind-speed-beaufort" typeId="wind-speed-beaufort"/> <channel id="wind-speed-beaufort" typeId="wind-speed-beaufort"/>
<channel id="overcast" typeId="overcast"/> <channel id="overcast" typeId="overcast"/>
<channel id="octa" typeId="octa"/> <channel id="octa" typeId="octa"/>
@ -75,14 +75,16 @@
<channel-type id="octa"> <channel-type id="octa">
<item-type>Number</item-type> <item-type>Number</item-type>
<label>Octa</label> <label>Octa</label>
<description>Octa</description> <description>Cloud cover estimation.</description>
<category>sun_clouds</category>
<state readOnly="true" pattern="%d/8" min="0" max="8"/> <state readOnly="true" pattern="%d/8" min="0" max="8"/>
</channel-type> </channel-type>
<channel-type id="attenuation-factor"> <channel-type id="attenuation-factor" advanced="true">
<item-type>Number</item-type> <item-type>Number</item-type>
<label>Mitigation Factor</label> <label>Mitigation Factor</label>
<description>Cloud layer mitigation factor</description> <description>Cloud layer mitigation factor</description>
<category>sun_clouds</category>
<state readOnly="true" pattern="%.1f" max="1" min="0"/> <state readOnly="true" pattern="%.1f" max="1" min="0"/>
</channel-type> </channel-type>
@ -90,6 +92,7 @@
<item-type>String</item-type> <item-type>String</item-type>
<label>Overcast</label> <label>Overcast</label>
<description>Overcast</description> <description>Overcast</description>
<category>sun_clouds</category>
<state readOnly="true" pattern="%s"> <state readOnly="true" pattern="%s">
<options> <options>
<option value="CLEAR_SKY">Clear sky</option> <option value="CLEAR_SKY">Clear sky</option>
@ -117,6 +120,7 @@
<item-type>DateTime</item-type> <item-type>DateTime</item-type>
<label>Observation Time</label> <label>Observation Time</label>
<description>Timestamp when data was observed</description> <description>Timestamp when data was observed</description>
<category>time</category>
<state readOnly="true"/> <state readOnly="true"/>
</channel-type> </channel-type>