mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
Code review corrections
Signed-off-by: Gaël L'hopital <gael@lhopital.org>
This commit is contained in:
parent
02bf3c1334
commit
080f7a5172
@ -61,7 +61,8 @@ Please check that proposed value is correct according to the place.
|
|||||||
| aq-bulletin-tomorrow | pm10-max | Number:Density | R | Maximum level of PM 10 concentration |
|
| aq-bulletin-tomorrow | pm10-max | Number:Density | R | Maximum level of PM 10 concentration |
|
||||||
| aq-bulletin-tomorrow | pm25-min | Number:Density | R | Minimum level of PM 2.5 concentration |
|
| aq-bulletin-tomorrow | pm25-min | Number:Density | R | Minimum level of PM 2.5 concentration |
|
||||||
| aq-bulletin-tomorrow | pm25-max | Number:Density | R | Maximum level of PM 2.5 concentration |
|
| aq-bulletin-tomorrow | pm25-max | Number:Density | R | Maximum level of PM 2.5 concentration |
|
||||||
| daily | message | String | R | Today's daily general information ||| daily | tomorrow | String | R | Tomorrow's daily general information |
|
| daily | message | String | R | Today's daily general information |
|
||||||
|
| daily | tomorrow | String | R | Tomorrow's daily general information |
|
||||||
|
|
||||||
### `location` Thing Channels
|
### `location` Thing Channels
|
||||||
|
|
||||||
@ -150,19 +151,75 @@ This binding has its own IconProvider and makes available the following list of
|
|||||||
| oh:airparif:willow | Yes | ![](doc/images/willow.svg) |
|
| oh:airparif:willow | Yes | ![](doc/images/willow.svg) |
|
||||||
| oh:airparif:wormwood | Yes | ![](doc/images/wormwood.svg) |
|
| oh:airparif:wormwood | Yes | ![](doc/images/wormwood.svg) |
|
||||||
|
|
||||||
|
|
||||||
## Full Examplee
|
## Full Examplee
|
||||||
|
|
||||||
### Thing Configurationn
|
### Thing Configurationn
|
||||||
|
|
||||||
```jav
|
```java
|
||||||
Bridge airparif:api:local "AirParif" [ apikey="xxxxx-dddd-cccc-4321-zzzzzzzzzzzzz" ] {
|
Bridge airparif:api:local "AirParif" [ apikey="xxxxx-dddd-cccc-4321-zzzzzzzzzzzzz" ] {
|
||||||
location yvelines "Yvelines" [ department="78", location="52.639,1.8284" ]
|
location 78 "Yvelines" [ department="78", location="52.639,1.8284" ]
|
||||||
}a
|
}
|
||||||
```
|
```
|
||||||
### Item Configurationn
|
### Item Configurationn
|
||||||
|
|
||||||
```java
|
```java
|
||||||
Example item configuration goes here.
|
String AirParifPollensComment "Situation" {channel="airparif:api:local:pollens#comment"}
|
||||||
|
DateTime AirParifPollensBeginValidity "Begin validity" {channel="airparif:api:local:pollens#begin-validity"}
|
||||||
|
DateTime AirParifPollensEndValidity "End validity" {channel="airparif:api:local:pollens#end-validity"}
|
||||||
|
String AirParifAqBulletinComment "Message" {channel="airparif:api:local:aq-bulletin#comment"}
|
||||||
|
Number:Density AirParifAqBulletinNo2Min "No2 min" {channel="airparif:api:local:aq-bulletin#no2-min"}
|
||||||
|
Number:Density AirParifAqBulletinNo2Max "No2 max" {channel="airparif:api:local:aq-bulletin#no2-max"}
|
||||||
|
Number:Density AirParifAqBulletinO3Min "O3 min" {channel="airparif:api:local:aq-bulletin#o3-min"}
|
||||||
|
Number:Density AirParifAqBulletinO3Max "O3 max" {channel="airparif:api:local:aq-bulletin#o3-max"}
|
||||||
|
Number:Density AirParifAqBulletinPm10Min "Pm 10 min" {channel="airparif:api:local:aq-bulletin#pm10-min"}
|
||||||
|
Number:Density AirParifAqBulletinPm10Max "Pm 10 max" {channel="airparif:api:local:aq-bulletin#pm10-max"}
|
||||||
|
Number:Density AirParifAqBulletinPm25Min "Pm 2.5 min" {channel="airparif:api:local:aq-bulletin#pm25-min"}
|
||||||
|
Number:Density AirParifAqBulletinPm25Max "Pm 2.5 max" {channel="airparif:api:local:aq-bulletin#pm25-max"}
|
||||||
|
String AirParifAqBulletinTomorrowComment "Message" {channel="airparif:api:local:aq-bulletin-tomorrow#comment"}
|
||||||
|
Number:Density AirParifAqBulletinTomorrowNo2Min "No2 min" {channel="airparif:api:local:aq-bulletin-tomorrow#no2-min"}
|
||||||
|
Number:Density AirParifAqBulletinTomorrowNo2Max "No2 max" {channel="airparif:api:local:aq-bulletin-tomorrow#no2-max"}
|
||||||
|
Number:Density AirParifAqBulletinTomorrowO3Min "O3 min" {channel="airparif:api:local:aq-bulletin-tomorrow#o3-min"}
|
||||||
|
Number:Density AirParifAqBulletinTomorrowO3Max "O3 max" {channel="airparif:api:local:aq-bulletin-tomorrow#o3-max"}
|
||||||
|
Number:Density AirParifAqBulletinTomorrowPm10Min "Pm 10 min" {channel="airparif:api:local:aq-bulletin-tomorrow#pm10-min"}
|
||||||
|
Number:Density AirParifAqBulletinTomorrowPm10Max "Pm 10 max" {channel="airparif:api:local:aq-bulletin-tomorrow#pm10-max"}
|
||||||
|
Number:Density AirParifAqBulletinTomorrowPm25Min "Pm 2.5 min" {channel="airparif:api:local:aq-bulletin-tomorrow#pm25-min"}
|
||||||
|
Number:Density AirParifAqBulletinTomorrowPm25Max "Pm 2.5 max" {channel="airparif:api:local:aq-bulletin-tomorrow#pm25-max"}
|
||||||
|
String AirParifDailyMessage "Message" {channel="airparif:api:local:daily#message"}
|
||||||
|
String AirParifDailyTomorrow "Tomorrow" {channel="airparif:api:local:daily#tomorrow"}
|
||||||
|
|
||||||
|
Number Yvelines_Pollens_Cypress "Cypress" {channel="airparif:location:local:78:pollens#cypress"}
|
||||||
|
Number Yvelines_Pollens_Hazel "Hazel level" {channel="airparif:location:local:78:pollens#hazel"}
|
||||||
|
Number Yvelines_Pollens_Alder "Alder" {channel="airparif:location:local:78:pollens#alder"}
|
||||||
|
Number Yvelines_Pollens_Poplar "Poplar" {channel="airparif:location:local:78:pollens#poplar"}
|
||||||
|
Number Yvelines_Pollens_Willow "Willow" {channel="airparif:location:local:78:pollens#willow"}
|
||||||
|
Number Yvelines_Pollens_Ash "Ash" {channel="airparif:location:local:78:pollens#ash"}
|
||||||
|
Number Yvelines_Pollens_Hornbeam "Hornbeam" {channel="airparif:location:local:78:pollens#hornbeam"}
|
||||||
|
Number Yvelines_Pollens_Birch "Birch level" {channel="airparif:location:local:78:pollens#birch"}
|
||||||
|
Number Yvelines_Pollens_Plane "Plane" {channel="airparif:location:local:78:pollens#plane"}
|
||||||
|
Number Yvelines_Pollens_Oak "Oak" {channel="airparif:location:local:78:pollens#oak"}
|
||||||
|
Number Yvelines_Pollens_Olive "Olive" {channel="airparif:location:local:78:pollens#olive"}
|
||||||
|
Number Yvelines_Pollens_Linden "Linden" {channel="airparif:location:local:78:pollens#linden"}
|
||||||
|
Number Yvelines_Pollens_Chestnut "Chestnut" {channel="airparif:location:local:78:pollens#chestnut"}
|
||||||
|
Number Yvelines_Pollens_Rumex "Rumex" {channel="airparif:location:local:78:pollens#rumex"}
|
||||||
|
Number Yvelines_Pollens_Grasses "Grasses" {channel="airparif:location:local:78:pollens#grasses"}
|
||||||
|
Number Yvelines_Pollens_Plantain "Plantain" {channel="airparif:location:local:78:pollens#plantain"}
|
||||||
|
Number Yvelines_Pollens_Urticaceae "Urticacea" {channel="airparif:location:local:78:pollens#urticaceae"}
|
||||||
|
Number Yvelines_Pollens_Wormwood "Wormwood" {channel="airparif:location:local:78:pollens#wormwood"}
|
||||||
|
Number Yvelines_Pollens_Ragweed "Ragweed" {channel="airparif:location:local:78:pollens#ragweed"}
|
||||||
|
String Yvelines_Indice_Message "Message" {channel="airparif:location:local:78:indice#message"}
|
||||||
|
DateTime Yvelines_Indice_Timestamp "Timestamp" {channel="airparif:location:local:78:indice#timestamp"}
|
||||||
|
Number Yvelines_Indice_Alert "Index" {channel="airparif:location:local:78:indice#alert"}
|
||||||
|
String Yvelines_O3_Message "Message" {channel="airparif:location:local:78:o3#message"}
|
||||||
|
Number:Density Yvelines_O3_Value "Concentration" {channel="airparif:location:local:78:o3#value"}
|
||||||
|
Number Yvelines_O3_Alert "Alert level" {channel="airparif:location:local:78:o3#alert"}
|
||||||
|
String Yvelines_No2_Message "Message" {channel="airparif:location:local:78:no2#message"}
|
||||||
|
Number:Density Yvelines_No2_Value "Concentration" {channel="airparif:location:local:78:no2#value"}
|
||||||
|
Number Yvelines_No2_Alert "Alert level" {channel="airparif:location:local:78:no2#alert"}
|
||||||
|
String Yvelines_Pm25_Message "Message" {channel="airparif:location:local:78:pm25#message"}
|
||||||
|
Number:Density Yvelines_Pm25_Value "Concentration" {channel="airparif:location:local:78:pm25#value"}
|
||||||
|
Number Yvelines_Pm25_Alert "Alert level" {channel="airparif:location:local:78:pm25#alert"}
|
||||||
|
String Yvelines_Pm10_Message "Message" {channel="airparif:location:local:78:pm10#message"}
|
||||||
|
Number:Density Yvelines_Pm10_Value "Concentration" {channel="airparif:location:local:78:pm10#value"}
|
||||||
|
Number Yvelines_Pm10_Alert "Alert level" {channel="airparif:location:local:78:pm10#alert"}
|
||||||
``
|
``
|
||||||
|
|
||||||
|
@ -16,7 +16,6 @@ import static org.openhab.binding.airparif.internal.AirParifBindingConstants.*;
|
|||||||
|
|
||||||
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.eclipse.jetty.client.HttpClient;
|
|
||||||
import org.openhab.binding.airparif.internal.deserialization.AirParifDeserializer;
|
import org.openhab.binding.airparif.internal.deserialization.AirParifDeserializer;
|
||||||
import org.openhab.binding.airparif.internal.handler.AirParifBridgeHandler;
|
import org.openhab.binding.airparif.internal.handler.AirParifBridgeHandler;
|
||||||
import org.openhab.binding.airparif.internal.handler.LocationHandler;
|
import org.openhab.binding.airparif.internal.handler.LocationHandler;
|
||||||
@ -40,12 +39,12 @@ import org.osgi.service.component.annotations.Reference;
|
|||||||
@Component(configurationPid = "binding.airparif", service = ThingHandlerFactory.class)
|
@Component(configurationPid = "binding.airparif", service = ThingHandlerFactory.class)
|
||||||
public class AirParifHandlerFactory extends BaseThingHandlerFactory {
|
public class AirParifHandlerFactory extends BaseThingHandlerFactory {
|
||||||
private final AirParifDeserializer deserializer;
|
private final AirParifDeserializer deserializer;
|
||||||
private final HttpClient httpClient;
|
private final HttpClientFactory httpClientFactory;
|
||||||
|
|
||||||
@Activate
|
@Activate
|
||||||
public AirParifHandlerFactory(final @Reference HttpClientFactory httpClientFactory,
|
public AirParifHandlerFactory(final @Reference HttpClientFactory httpClientFactory,
|
||||||
final @Reference AirParifDeserializer deserializer) {
|
final @Reference AirParifDeserializer deserializer) {
|
||||||
this.httpClient = httpClientFactory.getCommonHttpClient();
|
this.httpClientFactory = httpClientFactory;
|
||||||
this.deserializer = deserializer;
|
this.deserializer = deserializer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,7 +58,7 @@ public class AirParifHandlerFactory extends BaseThingHandlerFactory {
|
|||||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||||
|
|
||||||
return APIBRIDGE_THING_TYPE.equals(thingTypeUID)
|
return APIBRIDGE_THING_TYPE.equals(thingTypeUID)
|
||||||
? new AirParifBridgeHandler((Bridge) thing, httpClient, deserializer)
|
? new AirParifBridgeHandler((Bridge) thing, httpClientFactory.getCommonHttpClient(), deserializer)
|
||||||
: LOCATION_THING_TYPE.equals(thingTypeUID) ? new LocationHandler(thing) : null;
|
: LOCATION_THING_TYPE.equals(thingTypeUID) ? new LocationHandler(thing) : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,10 +41,6 @@ public class AirParifApi {
|
|||||||
private static final UriBuilder POLLENS_BUILDER = AIRPARIF_BUILDER.clone().path("pollens");
|
private static final UriBuilder POLLENS_BUILDER = AIRPARIF_BUILDER.clone().path("pollens");
|
||||||
public static final URI POLLENS_URI = POLLENS_BUILDER.clone().path("bulletin").build();
|
public static final URI POLLENS_URI = POLLENS_BUILDER.clone().path("bulletin").build();
|
||||||
|
|
||||||
// Poor interest, only returns highest risk level for the dept.
|
|
||||||
// public static final UriBuilder POLLENS_DEPT_BUILDER = POLLENS_BUILDER.clone().path("departement");
|
|
||||||
// public static final URI PREV_COLORS_URI = INDICES_BUILDER.clone().path("couleurs").build();
|
|
||||||
|
|
||||||
public enum Scope {
|
public enum Scope {
|
||||||
@SerializedName("Cartes et résultats Hor'Air")
|
@SerializedName("Cartes et résultats Hor'Air")
|
||||||
MAPS,
|
MAPS,
|
||||||
|
@ -13,9 +13,9 @@
|
|||||||
package org.openhab.binding.airparif.internal.api;
|
package org.openhab.binding.airparif.internal.api;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
import java.time.Instant;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
import java.time.ZonedDateTime;
|
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -54,7 +54,7 @@ public class AirParifDto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public record KeyInfo(//
|
public record KeyInfo(//
|
||||||
ZonedDateTime expiration, //
|
Instant expiration, //
|
||||||
@SerializedName("droits") Set<Scope> scopes) {
|
@SerializedName("droits") Set<Scope> scopes) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,33 +135,33 @@ public class AirParifDto {
|
|||||||
private static ZoneId DEFAULT_ZONE = ZoneId.of("Europe/Paris");
|
private static ZoneId DEFAULT_ZONE = ZoneId.of("Europe/Paris");
|
||||||
|
|
||||||
public List<Pollens> data = List.of();
|
public List<Pollens> data = List.of();
|
||||||
private @Nullable ZonedDateTime beginValidity;
|
private @Nullable Instant beginValidity;
|
||||||
private @Nullable ZonedDateTime endValidity;
|
private @Nullable Instant endValidity;
|
||||||
|
|
||||||
public Optional<Pollens> getData() {
|
public Optional<Pollens> getData() {
|
||||||
return Optional.ofNullable(data.isEmpty() ? null : data.get(0));
|
return Optional.ofNullable(data.isEmpty() ? null : data.get(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<ZonedDateTime> getValidities() {
|
private Set<Instant> getValidities() {
|
||||||
Set<ZonedDateTime> validities = new TreeSet<>();
|
Set<Instant> validities = new TreeSet<>();
|
||||||
getData().ifPresent(pollens -> {
|
getData().ifPresent(pollens -> {
|
||||||
Matcher matcher = PATTERN.matcher(pollens.periode);
|
Matcher matcher = PATTERN.matcher(pollens.periode);
|
||||||
while (matcher.find()) {
|
while (matcher.find()) {
|
||||||
validities.add(LocalDate.parse(matcher.group(), FORMATTER).atStartOfDay(DEFAULT_ZONE));
|
validities.add(LocalDate.parse(matcher.group(), FORMATTER).atStartOfDay(DEFAULT_ZONE).toInstant());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return validities;
|
return validities;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<ZonedDateTime> getBeginValidity() {
|
public Optional<Instant> getBeginValidity() {
|
||||||
if (beginValidity == null) {
|
if (beginValidity == null) {
|
||||||
beginValidity = getValidities().iterator().next();
|
beginValidity = getValidities().iterator().next();
|
||||||
}
|
}
|
||||||
return Optional.ofNullable(beginValidity);
|
return Optional.ofNullable(beginValidity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<ZonedDateTime> getEndValidity() {
|
public Optional<Instant> getEndValidity() {
|
||||||
if (endValidity == null) {
|
if (endValidity == null) {
|
||||||
endValidity = getValidities().stream().reduce((prev, next) -> next).orElse(null);
|
endValidity = getValidities().stream().reduce((prev, next) -> next).orElse(null);
|
||||||
}
|
}
|
||||||
@ -170,7 +170,7 @@ public class AirParifDto {
|
|||||||
|
|
||||||
public Duration getValidityDuration() {
|
public Duration getValidityDuration() {
|
||||||
return Objects.requireNonNull(getEndValidity().map(end -> {
|
return Objects.requireNonNull(getEndValidity().map(end -> {
|
||||||
Duration duration = Duration.between(ZonedDateTime.now().withZoneSameInstant(end.getZone()), end);
|
Duration duration = Duration.between(Instant.now(), end);
|
||||||
return duration.isNegative() ? Duration.ZERO : duration;
|
return duration.isNegative() ? Duration.ZERO : duration;
|
||||||
}).orElse(Duration.ZERO));
|
}).orElse(Duration.ZERO));
|
||||||
}
|
}
|
||||||
@ -197,7 +197,7 @@ public class AirParifDto {
|
|||||||
|
|
||||||
public record Concentration(//
|
public record Concentration(//
|
||||||
@SerializedName("polluant") Pollutant pollutant, //
|
@SerializedName("polluant") Pollutant pollutant, //
|
||||||
ZonedDateTime date, //
|
Instant date, //
|
||||||
@SerializedName("valeurs") double[] values, //
|
@SerializedName("valeurs") double[] values, //
|
||||||
@Nullable Message message) {
|
@Nullable Message message) {
|
||||||
|
|
||||||
@ -224,7 +224,7 @@ public class AirParifDto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public record Route(//
|
public record Route(//
|
||||||
@SerializedName("dateRequise") ZonedDateTime requestedDate, //
|
@SerializedName("dateRequise") Instant requestedDate, //
|
||||||
double[][] longlats, //
|
double[][] longlats, //
|
||||||
@SerializedName("resultats") List<Concentration> concentrations, //
|
@SerializedName("resultats") List<Concentration> concentrations, //
|
||||||
@Nullable Message[] messages) {
|
@Nullable Message[] messages) {
|
||||||
|
@ -12,18 +12,16 @@
|
|||||||
*/
|
*/
|
||||||
package org.openhab.binding.airparif.internal.deserialization;
|
package org.openhab.binding.airparif.internal.deserialization;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.ZonedDateTime;
|
|
||||||
|
|
||||||
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.airparif.internal.AirParifException;
|
import org.openhab.binding.airparif.internal.AirParifException;
|
||||||
import org.openhab.binding.airparif.internal.api.AirParifDto.PollutantConcentration;
|
import org.openhab.binding.airparif.internal.api.AirParifDto.PollutantConcentration;
|
||||||
import org.openhab.binding.airparif.internal.api.PollenAlertLevel;
|
import org.openhab.binding.airparif.internal.api.PollenAlertLevel;
|
||||||
import org.openhab.core.i18n.TimeZoneProvider;
|
|
||||||
import org.osgi.service.component.annotations.Activate;
|
import org.osgi.service.component.annotations.Activate;
|
||||||
import org.osgi.service.component.annotations.Component;
|
import org.osgi.service.component.annotations.Component;
|
||||||
import org.osgi.service.component.annotations.Reference;
|
|
||||||
|
|
||||||
import com.google.gson.FieldNamingPolicy;
|
import com.google.gson.FieldNamingPolicy;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
@ -42,7 +40,7 @@ public class AirParifDeserializer {
|
|||||||
private final Gson gson;
|
private final Gson gson;
|
||||||
|
|
||||||
@Activate
|
@Activate
|
||||||
public AirParifDeserializer(final @Reference TimeZoneProvider timeZoneProvider) {
|
public AirParifDeserializer() {
|
||||||
gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.IDENTITY)
|
gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.IDENTITY)
|
||||||
.registerTypeAdapter(PollenAlertLevel.class, new PollenAlertLevelDeserializer())
|
.registerTypeAdapter(PollenAlertLevel.class, new PollenAlertLevelDeserializer())
|
||||||
.registerTypeAdapterFactory(new StrictEnumTypeAdapterFactory())
|
.registerTypeAdapterFactory(new StrictEnumTypeAdapterFactory())
|
||||||
@ -50,10 +48,10 @@ public class AirParifDeserializer {
|
|||||||
.registerTypeAdapter(LocalDate.class,
|
.registerTypeAdapter(LocalDate.class,
|
||||||
(JsonDeserializer<LocalDate>) (json, type, context) -> LocalDate
|
(JsonDeserializer<LocalDate>) (json, type, context) -> LocalDate
|
||||||
.parse(json.getAsJsonPrimitive().getAsString()))
|
.parse(json.getAsJsonPrimitive().getAsString()))
|
||||||
.registerTypeAdapter(ZonedDateTime.class, (JsonDeserializer<ZonedDateTime>) (json, type, context) -> {
|
.registerTypeAdapter(Instant.class, (JsonDeserializer<Instant>) (json, type, context) -> {
|
||||||
String string = json.getAsJsonPrimitive().getAsString();
|
String string = json.getAsJsonPrimitive().getAsString();
|
||||||
string += string.contains("+") ? "" : "Z";
|
string += string.contains("+") ? "" : "Z";
|
||||||
return ZonedDateTime.parse(string).withZoneSameInstant(timeZoneProvider.getTimeZone());
|
return Instant.parse(string);
|
||||||
}).create();
|
}).create();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,8 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
|
|||||||
import org.eclipse.jdt.annotation.Nullable;
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
import org.openhab.binding.airparif.internal.api.AirParifDto.PollutantConcentration;
|
import org.openhab.binding.airparif.internal.api.AirParifDto.PollutantConcentration;
|
||||||
import org.openhab.binding.airparif.internal.api.Pollutant;
|
import org.openhab.binding.airparif.internal.api.Pollutant;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import com.google.gson.JsonArray;
|
import com.google.gson.JsonArray;
|
||||||
import com.google.gson.JsonDeserializationContext;
|
import com.google.gson.JsonDeserializationContext;
|
||||||
@ -32,6 +34,7 @@ import com.google.gson.JsonSyntaxException;
|
|||||||
*/
|
*/
|
||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
class PollutantConcentrationDeserializer implements JsonDeserializer<PollutantConcentration> {
|
class PollutantConcentrationDeserializer implements JsonDeserializer<PollutantConcentration> {
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(PollutantConcentrationDeserializer.class);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable PollutantConcentration deserialize(JsonElement json, Type clazz,
|
public @Nullable PollutantConcentration deserialize(JsonElement json, Type clazz,
|
||||||
@ -44,7 +47,7 @@ class PollutantConcentrationDeserializer implements JsonDeserializer<PollutantCo
|
|||||||
try {
|
try {
|
||||||
result = new PollutantConcentration(pollutant, array.get(1).getAsInt(), array.get(2).getAsInt());
|
result = new PollutantConcentration(pollutant, array.get(1).getAsInt(), array.get(2).getAsInt());
|
||||||
} catch (JsonSyntaxException ignore) {
|
} catch (JsonSyntaxException ignore) {
|
||||||
// result will remain null
|
logger.debug("Error deserializing PollutantConcentration: {}", json.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -64,7 +64,7 @@ public class AirParifDiscoveryService extends AbstractThingHandlerDiscoveryServi
|
|||||||
LocationProvider localLocation = locationProvider;
|
LocationProvider localLocation = locationProvider;
|
||||||
PointType location = localLocation != null ? localLocation.getLocation() : null;
|
PointType location = localLocation != null ? localLocation.getLocation() : null;
|
||||||
if (location == null) {
|
if (location == null) {
|
||||||
logger.debug("LocationProvider.getLocation() is not set -> Will not provide any discovery results");
|
logger.warn("LocationProvider.getLocation() is not set -> Will not provide any discovery results");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,8 +82,6 @@ public class AirParifDiscoveryService extends AbstractThingHandlerDiscoveryServi
|
|||||||
.withProperty(LocationConfiguration.LOCATION, serverLocation.toFullString())//
|
.withProperty(LocationConfiguration.LOCATION, serverLocation.toFullString())//
|
||||||
.withRepresentationProperty(LocationConfiguration.DEPARTMENT) //
|
.withRepresentationProperty(LocationConfiguration.DEPARTMENT) //
|
||||||
.withBridge(bridgeUID).build()));
|
.withBridge(bridgeUID).build()));
|
||||||
} else {
|
|
||||||
logger.info("No department could be discovered matching server location");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -117,6 +117,7 @@ public class AirParifBridgeHandler extends BaseBridgeHandler implements HandlerU
|
|||||||
"@text/offline.config-error-unknown-apikey");
|
"@text/offline.config-error-unknown-apikey");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
updateStatus(ThingStatus.UNKNOWN);
|
||||||
scheduler.execute(this::initiateConnexion);
|
scheduler.execute(this::initiateConnexion);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,7 +197,7 @@ public class AirParifBridgeHandler extends BaseBridgeHandler implements HandlerU
|
|||||||
thing.setProperty("api-version", version.version());
|
thing.setProperty("api-version", version.version());
|
||||||
thing.setProperty("key-expiration", keyInfo.expiration().toString());
|
thing.setProperty("key-expiration", keyInfo.expiration().toString());
|
||||||
thing.setProperty("scopes", keyInfo.scopes().stream().map(e -> e.name()).collect(Collectors.joining(",")));
|
thing.setProperty("scopes", keyInfo.scopes().stream().map(e -> e.name()).collect(Collectors.joining(",")));
|
||||||
logger.info("The api key is valid until {}", keyInfo.expiration().toString());
|
logger.debug("The api key is valid until {}", keyInfo.expiration().toString());
|
||||||
updateStatus(ThingStatus.ONLINE);
|
updateStatus(ThingStatus.ONLINE);
|
||||||
|
|
||||||
ThingUID thingUID = thing.getUID();
|
ThingUID thingUID = thing.getUID();
|
||||||
@ -258,10 +259,8 @@ public class AirParifBridgeHandler extends BaseBridgeHandler implements HandlerU
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
ZonedDateTime tomorrowMorning = ZonedDateTime.now().plusDays(1).truncatedTo(ChronoUnit.DAYS).plusMinutes(1);
|
|
||||||
logger.debug("Rescheduling daily air quality bulletin job tomorrow morning");
|
logger.debug("Rescheduling daily air quality bulletin job tomorrow morning");
|
||||||
schedule(AQ_JOB, () -> updateDailyAQBulletin(todayGroupUID, tomorrowGroupUID),
|
schedule(AQ_JOB, () -> updateDailyAQBulletin(todayGroupUID, tomorrowGroupUID), untilTomorrowMorning());
|
||||||
Duration.between(ZonedDateTime.now(), tomorrowMorning));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateEpisode(ChannelGroupUID dailyGroupUID) {
|
private void updateEpisode(ChannelGroupUID dailyGroupUID) {
|
||||||
@ -278,9 +277,12 @@ public class AirParifBridgeHandler extends BaseBridgeHandler implements HandlerU
|
|||||||
updateState(new ChannelUID(dailyGroupUID, CHANNEL_MESSAGE), new StringType(episode.message().fr()));
|
updateState(new ChannelUID(dailyGroupUID, CHANNEL_MESSAGE), new StringType(episode.message().fr()));
|
||||||
updateState(new ChannelUID(dailyGroupUID, CHANNEL_TOMORROW), new StringType(episode.message().fr()));
|
updateState(new ChannelUID(dailyGroupUID, CHANNEL_TOMORROW), new StringType(episode.message().fr()));
|
||||||
|
|
||||||
ZonedDateTime tomorrowMorning = ZonedDateTime.now().plusDays(1).truncatedTo(ChronoUnit.DAYS).plusMinutes(1);
|
schedule(EPISODE_JOB, () -> updateEpisode(dailyGroupUID), untilTomorrowMorning());
|
||||||
schedule(EPISODE_JOB, () -> updateEpisode(dailyGroupUID),
|
}
|
||||||
Duration.between(ZonedDateTime.now(), tomorrowMorning));
|
|
||||||
|
private Duration untilTomorrowMorning() {
|
||||||
|
return Duration.between(ZonedDateTime.now(),
|
||||||
|
ZonedDateTime.now().plusDays(1).truncatedTo(ChronoUnit.DAYS).plusMinutes(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
public @Nullable Route getConcentrations(String location) {
|
public @Nullable Route getConcentrations(String location) {
|
||||||
|
@ -62,12 +62,12 @@ public interface HandlerUtils {
|
|||||||
|
|
||||||
default void schedule(String jobName, Runnable job, Duration duration) {
|
default void schedule(String jobName, Runnable job, Duration duration) {
|
||||||
ScheduledFuture<?> result = getJobs().remove(jobName);
|
ScheduledFuture<?> result = getJobs().remove(jobName);
|
||||||
String operation = "Scheduling";
|
|
||||||
|
getLogger().debug("{} {} in {}", result != null ? "Rescheduled" : "Scheduling", jobName, duration);
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
operation = "Rescheduled";
|
|
||||||
cancelFuture(result);
|
cancelFuture(result);
|
||||||
}
|
}
|
||||||
getLogger().info("{} {} in {}", operation, jobName, duration);
|
|
||||||
getJobs().put(jobName, getScheduler().schedule(job, duration.getSeconds(), TimeUnit.SECONDS));
|
getJobs().put(jobName, getScheduler().schedule(job, duration.getSeconds(), TimeUnit.SECONDS));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,10 +11,10 @@ thing-type.airparif.api.group.aq-bulletin.label = Today's Air Quality Bulletin
|
|||||||
thing-type.airparif.api.group.aq-bulletin-tomorrow.label = Tomorrow's Air Quality Bulletin
|
thing-type.airparif.api.group.aq-bulletin-tomorrow.label = Tomorrow's Air Quality Bulletin
|
||||||
thing-type.airparif.location.label = Department Report
|
thing-type.airparif.location.label = Department Report
|
||||||
thing-type.airparif.location.description = AirParif air quality report for the given location
|
thing-type.airparif.location.description = AirParif air quality report for the given location
|
||||||
thing-type.airparif.location.group.no2.label = NO2 Concentration Information
|
thing-type.airparif.location.group.no2.label = NO2 Concentration
|
||||||
thing-type.airparif.location.group.o3.label = Ozone Concentration Information
|
thing-type.airparif.location.group.o3.label = Ozone Concentration
|
||||||
thing-type.airparif.location.group.pm10.label = PM10 Concentration Information
|
thing-type.airparif.location.group.pm10.label = PM10 Concentration
|
||||||
thing-type.airparif.location.group.pm25.label = PM2.5 Concentration Information
|
thing-type.airparif.location.group.pm25.label = PM2.5 Concentration
|
||||||
|
|
||||||
# thing types config
|
# thing types config
|
||||||
|
|
||||||
@ -60,12 +60,12 @@ channel-group-type.airparif.bridge-pollens.channel.comment.label = Situation
|
|||||||
channel-group-type.airparif.bridge-pollens.channel.comment.description = Current pollens situation
|
channel-group-type.airparif.bridge-pollens.channel.comment.description = Current pollens situation
|
||||||
channel-group-type.airparif.bridge-pollens.channel.end-validity.label = End Validity
|
channel-group-type.airparif.bridge-pollens.channel.end-validity.label = End Validity
|
||||||
channel-group-type.airparif.bridge-pollens.channel.end-validity.description = Bulletin validity end
|
channel-group-type.airparif.bridge-pollens.channel.end-validity.description = Bulletin validity end
|
||||||
channel-group-type.airparif.daily.label = Daily information for the region
|
channel-group-type.airparif.daily.label = Daily Region Information
|
||||||
channel-group-type.airparif.daily.channel.message.label = Message
|
channel-group-type.airparif.daily.channel.message.label = Message
|
||||||
channel-group-type.airparif.daily.channel.message.description = Today's daily general information
|
channel-group-type.airparif.daily.channel.message.description = Today's daily general information
|
||||||
channel-group-type.airparif.daily.channel.tomorrow.label = Tomorrow
|
channel-group-type.airparif.daily.channel.tomorrow.label = Tomorrow
|
||||||
channel-group-type.airparif.daily.channel.tomorrow.description = Tomorrow's daily general information
|
channel-group-type.airparif.daily.channel.tomorrow.description = Tomorrow's daily general information
|
||||||
channel-group-type.airparif.dept-pollens.label = Pollen information for the department
|
channel-group-type.airparif.dept-pollens.label = Department Pollen Information
|
||||||
channel-group-type.airparif.pollutant-mpc.label = Pollutant Concentration Information
|
channel-group-type.airparif.pollutant-mpc.label = Pollutant Concentration Information
|
||||||
channel-group-type.airparif.pollutant-mpc.channel.alert.label = Alert Level
|
channel-group-type.airparif.pollutant-mpc.channel.alert.label = Alert Level
|
||||||
channel-group-type.airparif.pollutant-mpc.channel.alert.description = Alert Level associated to pollutant concentration
|
channel-group-type.airparif.pollutant-mpc.channel.alert.description = Alert Level associated to pollutant concentration
|
||||||
|
@ -101,7 +101,7 @@
|
|||||||
</channel-group-type>
|
</channel-group-type>
|
||||||
|
|
||||||
<channel-group-type id="daily">
|
<channel-group-type id="daily">
|
||||||
<label>Daily information for the region</label>
|
<label>Daily Region Information</label>
|
||||||
<channels>
|
<channels>
|
||||||
<channel id="message" typeId="comment">
|
<channel id="message" typeId="comment">
|
||||||
<label>Message</label>
|
<label>Message</label>
|
||||||
@ -115,7 +115,7 @@
|
|||||||
</channel-group-type>
|
</channel-group-type>
|
||||||
|
|
||||||
<channel-group-type id="dept-pollens">
|
<channel-group-type id="dept-pollens">
|
||||||
<label>Pollen information for the department</label>
|
<label>Department Pollen Information</label>
|
||||||
<channels>
|
<channels>
|
||||||
<channel id="cypress" typeId="cypress-level"/>
|
<channel id="cypress" typeId="cypress-level"/>
|
||||||
<channel id="hazel" typeId="hazel-level"/>
|
<channel id="hazel" typeId="hazel-level"/>
|
||||||
|
@ -16,16 +16,16 @@
|
|||||||
<channel-group id="pollens" typeId="dept-pollens"/>
|
<channel-group id="pollens" typeId="dept-pollens"/>
|
||||||
<channel-group id="indice" typeId="pollutant-ndx"/>
|
<channel-group id="indice" typeId="pollutant-ndx"/>
|
||||||
<channel-group id="o3" typeId="pollutant-mpc">
|
<channel-group id="o3" typeId="pollutant-mpc">
|
||||||
<label>Ozone Concentration Information</label>
|
<label>Ozone Concentration</label>
|
||||||
</channel-group>
|
</channel-group>
|
||||||
<channel-group id="no2" typeId="pollutant-mpc">
|
<channel-group id="no2" typeId="pollutant-mpc">
|
||||||
<label>NO2 Concentration Information</label>
|
<label>NO2 Concentration</label>
|
||||||
</channel-group>
|
</channel-group>
|
||||||
<channel-group id="pm25" typeId="pollutant-mpc">
|
<channel-group id="pm25" typeId="pollutant-mpc">
|
||||||
<label>PM2.5 Concentration Information</label>
|
<label>PM2.5 Concentration</label>
|
||||||
</channel-group>
|
</channel-group>
|
||||||
<channel-group id="pm10" typeId="pollutant-mpc">
|
<channel-group id="pm10" typeId="pollutant-mpc">
|
||||||
<label>PM10 Concentration Information</label>
|
<label>PM10 Concentration</label>
|
||||||
</channel-group>
|
</channel-group>
|
||||||
</channel-groups>
|
</channel-groups>
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user