Skip to content

Commit 9b7d19b

Browse files
authoredMay 22, 2024
[French Govt Energy Data] New binding (openhab#16713)
Signed-off-by: Gaël L'hopital <gael@lhopital.org> Signed-off-by: clinique <gael@lhopital.org>
1 parent 77bd3bf commit 9b7d19b

File tree

18 files changed

+788
-0
lines changed

18 files changed

+788
-0
lines changed
 

‎CODEOWNERS

+1
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@
119119
/bundles/org.openhab.binding.freebox/ @lolodomo
120120
/bundles/org.openhab.binding.freeboxos/ @clinique
121121
/bundles/org.openhab.binding.freecurrency/ @J-N-K
122+
/bundles/org.openhab.binding.frenchgovtenergydata/ @clinique
122123
/bundles/org.openhab.binding.fronius/ @trokohl
123124
/bundles/org.openhab.binding.fsinternetradio/ @paphko
124125
/bundles/org.openhab.binding.ftpupload/ @paulianttila

‎bom/openhab-addons/pom.xml

+5
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,11 @@
586586
<artifactId>org.openhab.binding.freecurrency</artifactId>
587587
<version>${project.version}</version>
588588
</dependency>
589+
<dependency>
590+
<groupId>org.openhab.addons.bundles</groupId>
591+
<artifactId>org.openhab.binding.frenchgovtenergydata</artifactId>
592+
<version>${project.version}</version>
593+
</dependency>
589594
<dependency>
590595
<groupId>org.openhab.addons.bundles</groupId>
591596
<artifactId>org.openhab.binding.fronius</artifactId>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
This content is produced and maintained by the openHAB project.
2+
3+
* Project home: https://www.openhab.org
4+
5+
== Declared Project Licenses
6+
7+
This program and the accompanying materials are made available under the terms
8+
of the Eclipse Public License 2.0 which is available at
9+
https://www.eclipse.org/legal/epl-2.0/.
10+
11+
== Source Code
12+
13+
https://github.com/openhab/openhab-addons
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# French Government Energy Data Binding
2+
3+
This binding provides regulated electricity prices in France.
4+
5+
This can be used to plan energy consumption, for example to calculate the cheapest period for running a dishwasher or charging an EV.
6+
7+
## Supported Things
8+
9+
The binding offers things for the two usual tariff classes (proposed by example by EDF).
10+
11+
- `base`: This is the basic subscription with a fixed kWh price.
12+
- `hphc`: Alternative subscription offering variable price in a given hour set (low hours/high hours).
13+
14+
15+
## Thing Configuration
16+
17+
Things (both `base` and `hphc`) only offers the configuration of the power output of the electrical delivery point (Linky terminal).
18+
19+
| Name | Type | Description | Default | Required |
20+
|-----------------------|---------|---------------------------------------------|---------------|----------|
21+
| puissance | integer | PDL power output (in kVA) | 6 | no |
22+
23+
24+
## Channels
25+
26+
### `base` Tariff Thing
27+
28+
All channels are read-only.
29+
30+
| Channel | Type | Description | Advanced |
31+
|--------------|--------------------|-----------------------------------------|----------|
32+
| fixed-ttc | Number:Currency | Yearly fixed price including taxes | No |
33+
| variable-ttc | Number:EnergyPrice | Energy price in €/kWh including taxes | No |
34+
| tariff-start | DateTime | Beginning date for this tariff | Yes |
35+
| fixed-ht | Number:Currency | Yearly fixed price excluding taxes | Yes |
36+
| variable-ht | Number:EnergyPrice | Energy price in €/kWh excluding taxes | Yes |
37+
38+
39+
### `hphc` Tariff Thing
40+
41+
All channels are read-only.
42+
43+
| Channel | Type | Description | Advanced |
44+
|--------------|--------------------|----------------------------------------------------|----------|
45+
| fixed-ttc | Number:Currency | Yearly fixed price including taxes | No |
46+
| hc-ttc | Number:EnergyPrice | Low hours energy price in €/kWh including taxes | No |
47+
| hp-ttc | Number:EnergyPrice | High hours energy price in €/kWh including taxes | No |
48+
| tariff-start | DateTime | Beginning date for this tariff | Yes |
49+
| fixed-ht | Number:Currency | Yearly fixed price excluding taxes | Yes |
50+
| hc-ht | Number:EnergyPrice | Low hours energy price in €/kWh excluding taxes | Yes |
51+
| hp-ht | Number:EnergyPrice | High hours energy price in €/kWh excluding taxes | Yes |
52+
53+
54+
## Full Example
55+
56+
57+
### Thing Configuration
58+
59+
```java
60+
Thing frenchgovtenergydata:hphc:local "Tarification Actuelle HP/HC" [puissance=9]
61+
```
62+
63+
### Item Configuration
64+
65+
```java
66+
DateTime Tarif_Start { channel="frenchgovtenergydata:hphc:local:tariff-start" }
67+
Number:Currency Abonnement_Annuel {channel="frenchgovtenergydata:hphc:local:fixed-ttc"}
68+
Number:EnergyPrice Prix_Heure_Pleine {channel="frenchgovtenergydata:hphc:local:hp-ttc"}
69+
Number:EnergyPrice Prix_Heure_Creuse {channel="frenchgovtenergydata:hphc:local:hc-ttc"}
70+
```
71+
72+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
5+
<modelVersion>4.0.0</modelVersion>
6+
7+
<parent>
8+
<groupId>org.openhab.addons.bundles</groupId>
9+
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
10+
<version>4.2.0-SNAPSHOT</version>
11+
</parent>
12+
13+
<artifactId>org.openhab.binding.frenchgovtenergydata</artifactId>
14+
15+
<name>openHAB Add-ons :: Bundles :: French Government Energy Data Binding</name>
16+
17+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<features name="org.openhab.binding.frenchgovtenergydata-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
3+
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
4+
5+
<feature name="openhab-binding-frenchgovtenergydata" description="French Government Energy Data Binding" version="${project.version}">
6+
<feature>openhab-runtime-base</feature>
7+
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.frenchgovtenergydata/${project.version}</bundle>
8+
</feature>
9+
</features>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/**
2+
* Copyright (c) 2010-2024 Contributors to the openHAB project
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Eclipse Public License 2.0 which is available at
9+
* http://www.eclipse.org/legal/epl-2.0
10+
*
11+
* SPDX-License-Identifier: EPL-2.0
12+
*/
13+
package org.openhab.binding.frenchgovtenergydata.internal;
14+
15+
import java.util.Currency;
16+
17+
import org.eclipse.jdt.annotation.NonNullByDefault;
18+
import org.openhab.core.thing.ThingTypeUID;
19+
20+
/**
21+
* The {@link FrenchGovtEnergyDataBindingConstants} class defines common constants, which are
22+
* used across the whole binding.
23+
*
24+
* @author Gaël L'hopital - Initial contribution
25+
*/
26+
@NonNullByDefault
27+
public class FrenchGovtEnergyDataBindingConstants {
28+
29+
private static final String BINDING_ID = "frenchgovtenergydata";
30+
31+
// List of all Thing Type UIDs
32+
public static final ThingTypeUID THING_TYPE_BASE = new ThingTypeUID(BINDING_ID, "base");
33+
public static final ThingTypeUID THING_TYPE_HPHC = new ThingTypeUID(BINDING_ID, "hphc");
34+
35+
// List of all Channel ids
36+
public static final String CHANNEL_TARIFF_START = "tariff-start";
37+
public static final String CHANNEL_FIXED_HT = "fixed-ht";
38+
public static final String CHANNEL_FIXED_TTC = "fixed-ttc";
39+
public static final String CHANNEL_VARIABLE_HT = "variable-ht";
40+
public static final String CHANNEL_VARIABLE_TTC = "variable-ttc";
41+
public static final String CHANNEL_HC_HT = "hc-ht";
42+
public static final String CHANNEL_HC_TTC = "hc-ttc";
43+
public static final String CHANNEL_HP_HT = "hp-ht";
44+
public static final String CHANNEL_HP_TTC = "hp-ttc";
45+
46+
public static final Currency CURRENCY_EUR = Currency.getInstance("EUR");
47+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/**
2+
* Copyright (c) 2010-2024 Contributors to the openHAB project
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Eclipse Public License 2.0 which is available at
9+
* http://www.eclipse.org/legal/epl-2.0
10+
*
11+
* SPDX-License-Identifier: EPL-2.0
12+
*/
13+
package org.openhab.binding.frenchgovtenergydata.internal;
14+
15+
import static org.openhab.binding.frenchgovtenergydata.internal.FrenchGovtEnergyDataBindingConstants.*;
16+
17+
import java.util.Set;
18+
19+
import org.eclipse.jdt.annotation.NonNullByDefault;
20+
import org.eclipse.jdt.annotation.Nullable;
21+
import org.openhab.binding.frenchgovtenergydata.internal.handler.BaseTariffHandler;
22+
import org.openhab.binding.frenchgovtenergydata.internal.handler.HpHcTariffHandler;
23+
import org.openhab.core.thing.Thing;
24+
import org.openhab.core.thing.ThingTypeUID;
25+
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
26+
import org.openhab.core.thing.binding.ThingHandler;
27+
import org.openhab.core.thing.binding.ThingHandlerFactory;
28+
import org.osgi.service.component.annotations.Component;
29+
30+
/**
31+
* The {@link FrenchGovtEnergyDataHandlerFactory} is responsible for creating things and thing handlers.
32+
*
33+
* @author Gaël L'hopital - Initial contribution
34+
*/
35+
@NonNullByDefault
36+
@Component(configurationPid = "binding.frenchgovtenergydata", service = ThingHandlerFactory.class)
37+
public class FrenchGovtEnergyDataHandlerFactory extends BaseThingHandlerFactory {
38+
39+
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_BASE, THING_TYPE_HPHC);
40+
41+
@Override
42+
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
43+
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
44+
}
45+
46+
@Override
47+
protected @Nullable ThingHandler createHandler(Thing thing) {
48+
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
49+
50+
return THING_TYPE_BASE.equals(thingTypeUID) ? new BaseTariffHandler(thing)
51+
: THING_TYPE_HPHC.equals(thingTypeUID) ? new HpHcTariffHandler(thing) : null;
52+
}
53+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/**
2+
* Copyright (c) 2010-2024 Contributors to the openHAB project
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Eclipse Public License 2.0 which is available at
9+
* http://www.eclipse.org/legal/epl-2.0
10+
*
11+
* SPDX-License-Identifier: EPL-2.0
12+
*/
13+
package org.openhab.binding.frenchgovtenergydata.internal.dto;
14+
15+
import org.eclipse.jdt.annotation.NonNullByDefault;
16+
17+
/**
18+
* The {@link BaseTariff} holds base price informations
19+
*
20+
* @author Gaël L'hopital - Initial contribution
21+
*/
22+
@NonNullByDefault
23+
public class BaseTariff extends Tariff {
24+
public final double variableHT;
25+
public final double variableTTC;
26+
27+
public BaseTariff(String line) {
28+
super(line, 7);
29+
try {
30+
this.variableHT = Double.parseDouble(values[5]);
31+
this.variableTTC = Double.parseDouble(values[6]);
32+
} catch (NumberFormatException e) {
33+
throw new IllegalArgumentException("Incorrect data in '%s'".formatted(line), e);
34+
}
35+
}
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/**
2+
* Copyright (c) 2010-2024 Contributors to the openHAB project
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Eclipse Public License 2.0 which is available at
9+
* http://www.eclipse.org/legal/epl-2.0
10+
*
11+
* SPDX-License-Identifier: EPL-2.0
12+
*/
13+
package org.openhab.binding.frenchgovtenergydata.internal.dto;
14+
15+
import org.eclipse.jdt.annotation.NonNullByDefault;
16+
17+
/**
18+
* The {@link HpHcTariff} holds HP-HC price informations
19+
*
20+
* @author Gaël L'hopital - Initial contribution
21+
*/
22+
@NonNullByDefault
23+
public class HpHcTariff extends Tariff {
24+
public final double hcHT;
25+
public final double hcTTC;
26+
public final double hpHT;
27+
public final double hpTTC;
28+
29+
public HpHcTariff(String line) {
30+
super(line, 9);
31+
try {
32+
this.hcHT = Double.parseDouble(values[5]);
33+
this.hcTTC = Double.parseDouble(values[6]);
34+
this.hpHT = Double.parseDouble(values[7]);
35+
this.hpTTC = Double.parseDouble(values[8]);
36+
} catch (NumberFormatException e) {
37+
throw new IllegalArgumentException("Incorrect data in '%s'".formatted(line), e);
38+
}
39+
}
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/**
2+
* Copyright (c) 2010-2024 Contributors to the openHAB project
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Eclipse Public License 2.0 which is available at
9+
* http://www.eclipse.org/legal/epl-2.0
10+
*
11+
* SPDX-License-Identifier: EPL-2.0
12+
*/
13+
package org.openhab.binding.frenchgovtenergydata.internal.dto;
14+
15+
import java.time.LocalDate;
16+
import java.time.ZoneOffset;
17+
import java.time.ZonedDateTime;
18+
import java.time.format.DateTimeFormatter;
19+
import java.time.format.DateTimeParseException;
20+
21+
import org.eclipse.jdt.annotation.NonNullByDefault;
22+
import org.eclipse.jdt.annotation.Nullable;
23+
24+
/**
25+
* The {@link Tariff} is the base class holding common information for Tariffs
26+
*
27+
* @author Gaël L'hopital - Initial contribution
28+
*/
29+
@NonNullByDefault
30+
public class Tariff {
31+
protected static final DateTimeFormatter TARIFF_DATE_FORMAT = DateTimeFormatter.ofPattern("dd/MM/yyyy");
32+
33+
protected final String[] values;
34+
public final ZonedDateTime dateDebut;
35+
public final @Nullable ZonedDateTime dateFin;
36+
public final int puissance;
37+
public final double fixeHT;
38+
public final double fixeTTC;
39+
40+
public Tariff(String line, int lenControl) {
41+
this.values = line.replace(',', '.').split(";");
42+
if (values.length == lenControl) {
43+
try {
44+
this.dateDebut = LocalDate.parse(values[0], TARIFF_DATE_FORMAT).atStartOfDay(ZoneOffset.UTC);
45+
this.dateFin = !values[1].isEmpty()
46+
? LocalDate.parse(values[1], TARIFF_DATE_FORMAT).atStartOfDay(ZoneOffset.UTC)
47+
: null;
48+
this.puissance = Integer.parseInt(values[2]);
49+
this.fixeHT = Double.parseDouble(values[3]);
50+
this.fixeTTC = Double.parseDouble(values[4]);
51+
} catch (NumberFormatException | DateTimeParseException e) {
52+
throw new IllegalArgumentException("Incorrect data in '%s'".formatted(line), e);
53+
}
54+
} else {
55+
throw new IllegalArgumentException("Unexpected number of data, %d expected".formatted(lenControl));
56+
}
57+
}
58+
59+
public boolean isActive() {
60+
return dateFin == null;
61+
}
62+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/**
2+
* Copyright (c) 2010-2024 Contributors to the openHAB project
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Eclipse Public License 2.0 which is available at
9+
* http://www.eclipse.org/legal/epl-2.0
10+
*
11+
* SPDX-License-Identifier: EPL-2.0
12+
*/
13+
package org.openhab.binding.frenchgovtenergydata.internal.handler;
14+
15+
import static org.openhab.binding.frenchgovtenergydata.internal.FrenchGovtEnergyDataBindingConstants.*;
16+
17+
import java.util.List;
18+
import java.util.stream.Stream;
19+
20+
import org.eclipse.jdt.annotation.NonNullByDefault;
21+
import org.openhab.binding.frenchgovtenergydata.internal.dto.BaseTariff;
22+
import org.openhab.core.library.types.QuantityType;
23+
import org.openhab.core.library.unit.CurrencyUnits;
24+
import org.openhab.core.thing.Thing;
25+
26+
/**
27+
* The {@link BaseTariffHandler} is responsible for handling commands, which are
28+
* sent to one of the channels.
29+
*
30+
* @author Gaël L'hopital - Initial contribution
31+
*/
32+
@NonNullByDefault
33+
public class BaseTariffHandler extends TariffHandler<BaseTariff> {
34+
private static final String DATASET_ID = "c13d05e5-9e55-4d03-bf7e-042a2ade7e49";
35+
36+
public BaseTariffHandler(Thing thing) {
37+
super(thing, DATASET_ID);
38+
}
39+
40+
@Override
41+
protected Stream<BaseTariff> interpretLines(List<String> lines) {
42+
return lines.stream().map(BaseTariff::new);
43+
}
44+
45+
@Override
46+
protected void updateChannels(BaseTariff tariff) {
47+
super.updateChannels(tariff);
48+
updateState(CHANNEL_VARIABLE_HT, new QuantityType<>(tariff.variableHT, CurrencyUnits.BASE_ENERGY_PRICE));
49+
updateState(CHANNEL_VARIABLE_TTC, new QuantityType<>(tariff.variableTTC, CurrencyUnits.BASE_ENERGY_PRICE));
50+
}
51+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/**
2+
* Copyright (c) 2010-2024 Contributors to the openHAB project
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Eclipse Public License 2.0 which is available at
9+
* http://www.eclipse.org/legal/epl-2.0
10+
*
11+
* SPDX-License-Identifier: EPL-2.0
12+
*/
13+
package org.openhab.binding.frenchgovtenergydata.internal.handler;
14+
15+
import static org.openhab.binding.frenchgovtenergydata.internal.FrenchGovtEnergyDataBindingConstants.*;
16+
17+
import java.util.List;
18+
import java.util.stream.Stream;
19+
20+
import org.eclipse.jdt.annotation.NonNullByDefault;
21+
import org.openhab.binding.frenchgovtenergydata.internal.dto.HpHcTariff;
22+
import org.openhab.core.library.types.QuantityType;
23+
import org.openhab.core.library.unit.CurrencyUnits;
24+
import org.openhab.core.thing.Thing;
25+
26+
/**
27+
* The {@link HpHcTariffHandler} is responsible for handling commands, which are
28+
* sent to one of the channels.
29+
*
30+
* @author Gaël L'hopital - Initial contribution
31+
*/
32+
@NonNullByDefault
33+
public class HpHcTariffHandler extends TariffHandler<HpHcTariff> {
34+
private static final String EMPTY_LINE = ";;;;;;;;";
35+
private static final String DATASET_ID = "f7303b3a-93c7-4242-813d-84919034c416";
36+
37+
public HpHcTariffHandler(Thing thing) {
38+
super(thing, DATASET_ID);
39+
}
40+
41+
@Override
42+
protected Stream<HpHcTariff> interpretLines(List<String> lines) {
43+
return lines.stream().filter(line -> !line.equals(EMPTY_LINE)).map(HpHcTariff::new);
44+
}
45+
46+
@Override
47+
protected void updateChannels(HpHcTariff tariff) {
48+
super.updateChannels(tariff);
49+
updateState(CHANNEL_HP_HT, new QuantityType<>(tariff.hpHT, CurrencyUnits.BASE_ENERGY_PRICE));
50+
updateState(CHANNEL_HP_TTC, new QuantityType<>(tariff.hpTTC, CurrencyUnits.BASE_ENERGY_PRICE));
51+
updateState(CHANNEL_HC_HT, new QuantityType<>(tariff.hcHT, CurrencyUnits.BASE_ENERGY_PRICE));
52+
updateState(CHANNEL_HC_TTC, new QuantityType<>(tariff.hcTTC, CurrencyUnits.BASE_ENERGY_PRICE));
53+
}
54+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
/**
2+
* Copyright (c) 2010-2024 Contributors to the openHAB project
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Eclipse Public License 2.0 which is available at
9+
* http://www.eclipse.org/legal/epl-2.0
10+
*
11+
* SPDX-License-Identifier: EPL-2.0
12+
*/
13+
package org.openhab.binding.frenchgovtenergydata.internal.handler;
14+
15+
import static org.openhab.binding.frenchgovtenergydata.internal.FrenchGovtEnergyDataBindingConstants.*;
16+
17+
import java.io.IOException;
18+
import java.math.BigDecimal;
19+
import java.time.ZonedDateTime;
20+
import java.time.temporal.ChronoUnit;
21+
import java.util.ArrayList;
22+
import java.util.Arrays;
23+
import java.util.List;
24+
import java.util.Optional;
25+
import java.util.concurrent.ScheduledFuture;
26+
import java.util.concurrent.TimeUnit;
27+
import java.util.stream.Collectors;
28+
import java.util.stream.Stream;
29+
30+
import javax.ws.rs.HttpMethod;
31+
32+
import org.eclipse.jdt.annotation.NonNullByDefault;
33+
import org.eclipse.jdt.annotation.Nullable;
34+
import org.openhab.binding.frenchgovtenergydata.internal.dto.Tariff;
35+
import org.openhab.core.io.net.http.HttpUtil;
36+
import org.openhab.core.library.types.DateTimeType;
37+
import org.openhab.core.library.types.QuantityType;
38+
import org.openhab.core.library.unit.CurrencyUnits;
39+
import org.openhab.core.thing.ChannelUID;
40+
import org.openhab.core.thing.Thing;
41+
import org.openhab.core.thing.ThingStatus;
42+
import org.openhab.core.thing.ThingStatusDetail;
43+
import org.openhab.core.thing.ThingUID;
44+
import org.openhab.core.thing.binding.BaseThingHandler;
45+
import org.openhab.core.types.Command;
46+
import org.openhab.core.types.RefreshType;
47+
import org.slf4j.Logger;
48+
import org.slf4j.LoggerFactory;
49+
50+
/**
51+
* The {@link TariffHandler} is the base class for Tariff Things. It takes care of
52+
* update logic and update scheduling once a day.
53+
*
54+
* @author Gaël L'hopital - Initial contribution
55+
*/
56+
@NonNullByDefault
57+
public abstract class TariffHandler<T extends Tariff> extends BaseThingHandler {
58+
private static final String URL = "https://www.data.gouv.fr/fr/datasets/r/%s";
59+
private static final int REFRESH_FIRST_HOUR_OF_DAY = 0;
60+
private static final int REFRESH_FIRST_MINUTE_OF_DAY = 1;
61+
62+
private final Logger logger = LoggerFactory.getLogger(TariffHandler.class);
63+
private final List<T> tariffs = new ArrayList<>();
64+
private final String url;
65+
66+
private Optional<ScheduledFuture<?>> refreshJob = Optional.empty();
67+
private @Nullable String fileCache = null;
68+
private int puissance = 6;
69+
70+
public TariffHandler(Thing thing, String dataset) {
71+
super(thing);
72+
this.url = URL.formatted(dataset);
73+
}
74+
75+
@Override
76+
public void initialize() {
77+
updateStatus(ThingStatus.UNKNOWN);
78+
Object confPower = getConfig().get("puissance");
79+
puissance = confPower != null ? ((BigDecimal) confPower).intValue() : 6;
80+
refreshJob = Optional.of(scheduler.schedule(this::updateData, 1, TimeUnit.SECONDS));
81+
}
82+
83+
@Override
84+
public void dispose() {
85+
refreshJob.ifPresent(job -> job.cancel(true));
86+
refreshJob = Optional.empty();
87+
super.dispose();
88+
}
89+
90+
private @Nullable String readFile() {
91+
@Nullable
92+
String result = null;
93+
try {
94+
result = HttpUtil.executeUrl(HttpMethod.GET, url, 10000);
95+
fileCache = result;
96+
} catch (IOException e) {
97+
// Use the cache if we had an error accessing the cloud resource
98+
result = fileCache;
99+
}
100+
return result;
101+
}
102+
103+
private void updateData() {
104+
ThingUID thingUID = getThing().getUID();
105+
logger.debug("Updating {} channels", thingUID);
106+
107+
@Nullable
108+
String result = readFile();
109+
if (result != null) {
110+
List<String> lines = new ArrayList<>(Arrays.asList(result.split("\r\n")));
111+
lines.remove(0);
112+
113+
List<T> newTariffs = interpretLines(lines).collect(Collectors.toList());
114+
if (!newTariffs.isEmpty()) {
115+
tariffs.clear();
116+
tariffs.addAll(newTariffs);
117+
}
118+
119+
tariffs.stream().filter(t -> t.puissance == puissance).filter(Tariff::isActive).findFirst().ifPresentOrElse(
120+
this::updateChannels,
121+
() -> updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "No active tariff"));
122+
123+
ZonedDateTime now = ZonedDateTime.now();
124+
ZonedDateTime nextUpdate = now.plusDays(1).withHour(REFRESH_FIRST_HOUR_OF_DAY)
125+
.withMinute(REFRESH_FIRST_MINUTE_OF_DAY).truncatedTo(ChronoUnit.MINUTES);
126+
long delay = ChronoUnit.MINUTES.between(now, nextUpdate);
127+
logger.debug("Scheduling next {} update in {} minutes", thingUID, delay);
128+
refreshJob = Optional.of(scheduler.schedule(this::updateData, delay, TimeUnit.MINUTES));
129+
} else {
130+
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
131+
"Unable to access %s".formatted(url));
132+
}
133+
}
134+
135+
protected void updateChannels(T tariff) {
136+
updateStatus(ThingStatus.ONLINE);
137+
updateState(CHANNEL_TARIFF_START, new DateTimeType(tariff.dateDebut));
138+
updateState(CHANNEL_FIXED_HT, new QuantityType<>(tariff.fixeHT, CurrencyUnits.BASE_CURRENCY));
139+
updateState(CHANNEL_FIXED_TTC, new QuantityType<>(tariff.fixeTTC, CurrencyUnits.BASE_CURRENCY));
140+
}
141+
142+
protected abstract Stream<T> interpretLines(List<String> lines);
143+
144+
@Override
145+
public void handleCommand(ChannelUID channelUID, Command command) {
146+
if (RefreshType.REFRESH.equals(command)) {
147+
updateData();
148+
}
149+
}
150+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<addon:addon id="frenchgovtenergydata" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xmlns:addon="https://openhab.org/schemas/addon/v1.0.0"
4+
xsi:schemaLocation="https://openhab.org/schemas/addon/v1.0.0 https://openhab.org/schemas/addon-1.0.0.xsd">
5+
6+
<type>binding</type>
7+
<name>French Government Energy Data Binding</name>
8+
<description>This binding provides French regulated electricity tariffs.</description>
9+
<connection>cloud</connection>
10+
<countries>fr</countries>
11+
12+
</addon:addon>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# add-on
2+
3+
addon.frenchgovtenergydata.name = French Government Energy Data Binding
4+
addon.frenchgovtenergydata.description = This binding provides French regulated electricity tariffs.
5+
6+
# thing types
7+
8+
thing-type.frenchgovtenergydata.base.label = Base Tariff
9+
thing-type.frenchgovtenergydata.base.description = Default Tariff Subscription
10+
thing-type.frenchgovtenergydata.base.channel.fixed-ht.label = Fixed Price HT
11+
thing-type.frenchgovtenergydata.base.channel.fixed-ht.description = Yearly fixed price excluding taxes.
12+
thing-type.frenchgovtenergydata.base.channel.fixed-ttc.label = Fixed Price TTC
13+
thing-type.frenchgovtenergydata.base.channel.fixed-ttc.description = Yearly fixed price including taxes.
14+
thing-type.frenchgovtenergydata.base.channel.variable-ht.label = Variable Price HT
15+
thing-type.frenchgovtenergydata.base.channel.variable-ht.description = Energy price in €/kWh excluding taxes.
16+
thing-type.frenchgovtenergydata.base.channel.variable-ttc.label = Variable Price TTC
17+
thing-type.frenchgovtenergydata.base.channel.variable-ttc.description = Energy price in €/kWh including taxes.
18+
thing-type.frenchgovtenergydata.hphc.label = HP-HC Tariff
19+
thing-type.frenchgovtenergydata.hphc.description = High / Low usage Tariff Subscription
20+
thing-type.frenchgovtenergydata.hphc.channel.fixed-ht.label = Fixed Price HT
21+
thing-type.frenchgovtenergydata.hphc.channel.fixed-ht.description = Yearly fixed price excluding taxes.
22+
thing-type.frenchgovtenergydata.hphc.channel.fixed-ttc.label = Fixed Price TTC
23+
thing-type.frenchgovtenergydata.hphc.channel.fixed-ttc.description = Yearly fixed price including taxes.
24+
thing-type.frenchgovtenergydata.hphc.channel.hc-ht.label = Low Hours Price HT
25+
thing-type.frenchgovtenergydata.hphc.channel.hc-ht.description = Low hours energy price in €/kWh excluding taxes.
26+
thing-type.frenchgovtenergydata.hphc.channel.hc-ttc.label = Low Hours Price TTC
27+
thing-type.frenchgovtenergydata.hphc.channel.hc-ttc.description = Low hours energy price in €/kWh including taxes.
28+
thing-type.frenchgovtenergydata.hphc.channel.hp-ht.label = High Hours Price HT
29+
thing-type.frenchgovtenergydata.hphc.channel.hp-ht.description = High hours energy price in €/kWh excluding taxes.
30+
thing-type.frenchgovtenergydata.hphc.channel.hp-ttc.label = High Hours Price TTC
31+
thing-type.frenchgovtenergydata.hphc.channel.hp-ttc.description = High hours energy price in €/kWh including taxes.
32+
33+
# thing types config
34+
35+
thing-type.config.frenchgovtenergydata.base.puissance.label = Power output
36+
thing-type.config.frenchgovtenergydata.base.puissance.description = PDL power output (in kVA)
37+
thing-type.config.frenchgovtenergydata.hphc.puissance.label = Power Output
38+
thing-type.config.frenchgovtenergydata.hphc.puissance.description = PDL power output (in kVA)
39+
40+
# channel types
41+
42+
channel-type.frenchgovtenergydata.energy-price-ht.label = Variable Price HT
43+
channel-type.frenchgovtenergydata.energy-price-ttc.label = Variable Price TTC
44+
channel-type.frenchgovtenergydata.price-ht.label = Price HT
45+
channel-type.frenchgovtenergydata.price-ttc.label = Price TTC
46+
channel-type.frenchgovtenergydata.timestamp.label = Tariff Start
47+
channel-type.frenchgovtenergydata.timestamp.description = Beginning date for this tariff
48+
channel-type.frenchgovtenergydata.timestamp.state.pattern = %1$tY-%1$tm-%1$td
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<thing:thing-descriptions bindingId="frenchgovtenergydata"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
5+
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
6+
7+
<thing-type id="base">
8+
<label>Base Tariff</label>
9+
<description>Default Tariff Subscription</description>
10+
11+
<channels>
12+
<channel id="tariff-start" typeId="timestamp"/>
13+
<channel id="fixed-ht" typeId="price-ht">
14+
<label>Fixed Price HT</label>
15+
<description>Yearly fixed price excluding taxes.</description>
16+
</channel>
17+
<channel id="fixed-ttc" typeId="price-ttc">
18+
<label>Fixed Price TTC</label>
19+
<description>Yearly fixed price including taxes.</description>
20+
</channel>
21+
<channel id="variable-ht" typeId="energy-price-ht">
22+
<label>Variable Price HT</label>
23+
<description>Energy price in €/kWh excluding taxes.</description>
24+
</channel>
25+
<channel id="variable-ttc" typeId="energy-price-ttc">
26+
<label>Variable Price TTC</label>
27+
<description>Energy price in €/kWh including taxes.</description>
28+
</channel>
29+
</channels>
30+
31+
<config-description>
32+
<parameter name="puissance" type="integer" min="3" max="36">
33+
<default>6</default>
34+
<label>Power output</label>
35+
<description>PDL power output (in kVA)</description>
36+
</parameter>
37+
</config-description>
38+
</thing-type>
39+
40+
<thing-type id="hphc">
41+
<label>HP-HC Tariff</label>
42+
<description>High / Low usage Tariff Subscription</description>
43+
44+
<channels>
45+
<channel id="tariff-start" typeId="timestamp"/>
46+
<channel id="fixed-ht" typeId="price-ht">
47+
<label>Fixed Price HT</label>
48+
<description>Yearly fixed price excluding taxes.</description>
49+
</channel>
50+
<channel id="fixed-ttc" typeId="price-ttc">
51+
<label>Fixed Price TTC</label>
52+
<description>Yearly fixed price including taxes.</description>
53+
</channel>
54+
<channel id="hc-ht" typeId="energy-price-ht">
55+
<label>Low Hours Price HT</label>
56+
<description>Low hours energy price in €/kWh excluding taxes.</description>
57+
</channel>
58+
<channel id="hc-ttc" typeId="energy-price-ttc">
59+
<label>Low Hours Price TTC</label>
60+
<description>Low hours energy price in €/kWh including taxes.</description>
61+
</channel>
62+
<channel id="hp-ht" typeId="energy-price-ht">
63+
<label>High Hours Price HT</label>
64+
<description>High hours energy price in €/kWh excluding taxes.</description>
65+
</channel>
66+
<channel id="hp-ttc" typeId="energy-price-ttc">
67+
<label>High Hours Price TTC</label>
68+
<description>High hours energy price in €/kWh including taxes.</description>
69+
</channel>
70+
</channels>
71+
72+
<config-description>
73+
<parameter name="puissance" type="integer" min="3" max="36">
74+
<default>6</default>
75+
<label>Power Output</label>
76+
<description>PDL power output (in kVA)</description>
77+
</parameter>
78+
</config-description>
79+
</thing-type>
80+
81+
<channel-type id="price-ht" advanced="true">
82+
<item-type>Number:Currency</item-type>
83+
<label>Price HT</label>
84+
<category>Price</category>
85+
<state readOnly="true" pattern="%.2f %unit%"></state>
86+
</channel-type>
87+
88+
<channel-type id="price-ttc">
89+
<item-type>Number:Currency</item-type>
90+
<label>Price TTC</label>
91+
<category>Price</category>
92+
<state readOnly="true" pattern="%.2f %unit%"></state>
93+
</channel-type>
94+
95+
<channel-type id="timestamp" advanced="true">
96+
<item-type>DateTime</item-type>
97+
<label>Tariff Start</label>
98+
<description>Beginning date for this tariff</description>
99+
<category>Time</category>
100+
<state readOnly="true" pattern="%1$tY-%1$tm-%1$td"/>
101+
</channel-type>
102+
103+
<channel-type id="energy-price-ht" advanced="true">
104+
<item-type>Number:EnergyPrice</item-type>
105+
<label>Variable Price HT</label>
106+
<category>Price</category>
107+
<state readOnly="true" pattern="%.4f %unit%"></state>
108+
</channel-type>
109+
110+
<channel-type id="energy-price-ttc">
111+
<item-type>Number:EnergyPrice</item-type>
112+
<label>Variable Price TTC</label>
113+
<category>Price</category>
114+
<state readOnly="true" pattern="%.4f %unit%"></state>
115+
</channel-type>
116+
117+
</thing:thing-descriptions>

‎bundles/pom.xml

+1
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@
151151
<module>org.openhab.binding.freebox</module>
152152
<module>org.openhab.binding.freeboxos</module>
153153
<module>org.openhab.binding.freecurrency</module>
154+
<module>org.openhab.binding.frenchgovtenergydata</module>
154155
<module>org.openhab.binding.fronius</module>
155156
<module>org.openhab.binding.fsinternetradio</module>
156157
<module>org.openhab.binding.ftpupload</module>

0 commit comments

Comments
 (0)
Please sign in to comment.