Skip to content

Commit

Permalink
Add new channel for reduced electricity tax
Browse files Browse the repository at this point in the history
Resolves #15635

Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk>
  • Loading branch information
jlaur committed Sep 20, 2023
1 parent bf1aa3d commit e184f78
Show file tree
Hide file tree
Showing 12 changed files with 227 additions and 18 deletions.
40 changes: 26 additions & 14 deletions bundles/org.openhab.binding.energidataservice/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ To obtain the Global Location Number of your grid company:
| net-tariff | Number | Current net tariff in DKK per kWh. Only available when `gridCompanyGLN` is configured | no |
| system-tariff | Number | Current system tariff in DKK per kWh | no |
| electricity-tax | Number | Current electricity tax in DKK per kWh | no |
| reduced-electricity-tax | Number | Current reduced electricity tax in DKK per kWh. For electric heating customers only | no |
| transmission-net-tariff | Number | Current transmission net tariff in DKK per kWh | no |
| hourly-prices | String | JSON array with hourly prices from 24 hours ago and onward | yes |

Expand Down Expand Up @@ -107,29 +108,38 @@ _Nord Energi Net:_
| start | StartOfDay |
| offset | -P1D |

#### Electricity Tax

The standard channel for electricity tax is `electricity-tax`.
For customers using electricity for heating, a reduced electricity tax rate may apply after consuming the first 4000 kWh within a year.
This reduced rate is made available through channel `reduced-electricity-tax`.
The binding cannot determine or manage rate variations as they depend on metering data.

#### Hourly Prices

The format of the `hourly-prices` JSON array is as follows:

```json
[
{
"hourStart": "2023-01-24T15:00:00Z",
"spotPrice": 1.67076001,
"hourStart": "2023-09-19T18:00:00Z",
"spotPrice": 0.0,
"spotPriceCurrency": "DKK",
"netTariff": 0.432225,
"systemTariff": 0.054000,
"electricityTax": 0.008000,
"transmissionNetTariff": 0.058000
"netTariff": 0.0,
"systemTariff": 0.054,
"electricityTax": 0.697,
"reducedElectricityTax": 0.008,
"transmissionNetTariff": 0.058
},
{
"hourStart": "2023-01-24T16:00:00Z",
"spotPrice": 1.859880005,
"hourStart": "2023-09-19T19:00:00Z",
"spotPrice": -0.00052,
"spotPriceCurrency": "DKK",
"netTariff": 1.05619,
"systemTariff": 0.054000,
"electricityTax": 0.008000,
"transmissionNetTariff": 0.058000
"netTariff": 0.0,
"systemTariff": 0.054,
"electricityTax": 0.697,
"reducedElectricityTax": 0.008,
"transmissionNetTariff": 0.058
}
]
```
Expand Down Expand Up @@ -310,14 +320,15 @@ These elements can be requested:
| NetTariff | Net tariff |
| SystemTariff | System tariff |
| ElectricityTax | Electricity tax |
| ReducedElectricityTax | Reduced electricity tax |
| TransmissionNetTariff | Transmission net tariff |

Using `null` as parameter returns the total prices including all price elements.
Using `null` as parameter returns the total prices including all price elements except `ReducedElectricityTax`.

Example:

```javascript
var priceMap = actions.getPrices("SpotPrice,NetTariff");
var priceMap = actions.getPrices("SpotPrice,NetTariff")
```

## Full Example
Expand All @@ -339,6 +350,7 @@ Number SpotPrice "Current Spot Price" <price> (TotalPrice) { channel="energidata
Number NetTariff "Current Net Tariff" <price> (TotalPrice) { channel="energidataservice:service:energidataservice:electricity#net-tariff" [profile="transform:VAT"] }
Number SystemTariff "Current System Tariff" <price> (TotalPrice) { channel="energidataservice:service:energidataservice:electricity#system-tariff" [profile="transform:VAT"] }
Number ElectricityTax "Current Electricity Tax" <price> (TotalPrice) { channel="energidataservice:service:energidataservice:electricity#electricity-tax" [profile="transform:VAT"] }
Number ReducedElectricityTax "Current Reduced Electricity Tax" <price> (TotalPrice) { channel="energidataservice:service:energidataservice:electricity#reduced-electricity-tax" [profile="transform:VAT"] }
Number TransmissionNetTariff "Current Transmission Tariff" <price> (TotalPrice) { channel="energidataservice:service:energidataservice:electricity#transmission-net-tariff" [profile="transform:VAT"] }
String HourlyPrices "Hourly Prices" <price> { channel="energidataservice:service:energidataservice:electricity#hourly-prices" }
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,14 @@ public class CacheManager {
private Collection<DatahubPricelistRecord> netTariffRecords = new ArrayList<>();
private Collection<DatahubPricelistRecord> systemTariffRecords = new ArrayList<>();
private Collection<DatahubPricelistRecord> electricityTaxRecords = new ArrayList<>();
private Collection<DatahubPricelistRecord> reducedElectricityTaxRecords = new ArrayList<>();
private Collection<DatahubPricelistRecord> transmissionNetTariffRecords = new ArrayList<>();

private Map<Instant, BigDecimal> spotPriceMap = new ConcurrentHashMap<>(SPOT_PRICE_MAX_CACHE_SIZE);
private Map<Instant, BigDecimal> netTariffMap = new ConcurrentHashMap<>(TARIFF_MAX_CACHE_SIZE);
private Map<Instant, BigDecimal> systemTariffMap = new ConcurrentHashMap<>(TARIFF_MAX_CACHE_SIZE);
private Map<Instant, BigDecimal> electricityTaxMap = new ConcurrentHashMap<>(TARIFF_MAX_CACHE_SIZE);
private Map<Instant, BigDecimal> reducedElectricityTaxMap = new ConcurrentHashMap<>(TARIFF_MAX_CACHE_SIZE);
private Map<Instant, BigDecimal> transmissionNetTariffMap = new ConcurrentHashMap<>(TARIFF_MAX_CACHE_SIZE);

public CacheManager() {
Expand All @@ -76,12 +78,14 @@ public void clear() {
netTariffRecords.clear();
systemTariffRecords.clear();
electricityTaxRecords.clear();
reducedElectricityTaxRecords.clear();
transmissionNetTariffRecords.clear();

spotPriceMap.clear();
netTariffMap.clear();
systemTariffMap.clear();
electricityTaxMap.clear();
reducedElectricityTaxMap.clear();
transmissionNetTariffMap.clear();
}

Expand Down Expand Up @@ -133,6 +137,17 @@ public void putElectricityTaxes(Collection<DatahubPricelistRecord> records) {
updateElectricityTaxes();
}

/**
* Replace current "raw"/unprocessed reduced electricity tax records in cache.
* Map of hourly taxes will be updated automatically.
*
* @param records to cache
*/
public void putReducedElectricityTaxes(Collection<DatahubPricelistRecord> records) {
putDatahubRecords(reducedElectricityTaxRecords, records);
updateReducedElectricityTaxes();
}

/**
* Replace current "raw"/unprocessed transmission net tariff records in cache.
* Map of hourly tariffs will be updated automatically.
Expand Down Expand Up @@ -177,6 +192,14 @@ public void updateElectricityTaxes() {
cleanup();
}

/**
* Update map of reduced electricity taxes from internal cache.
*/
public void updateReducedElectricityTaxes() {
reducedElectricityTaxMap = priceListParser.toHourly(reducedElectricityTaxRecords);
cleanup();
}

/**
* Update map of hourly transmission net tariffs from internal cache.
*/
Expand Down Expand Up @@ -297,6 +320,34 @@ public Map<Instant, BigDecimal> getElectricityTaxes() {
return new HashMap<Instant, BigDecimal>(electricityTaxMap);
}

/**
* Get current reduced electricity tax.
*
* @return reduced electricity tax currently valid
*/
public @Nullable BigDecimal getReducedElectricityTax() {
return getReducedElectricityTax(Instant.now(clock));
}

/**
* Get reduced electricity tax valid at provided instant.
*
* @param time {@link Instant} for which to get the reduced electricity tax
* @return reduced electricity tax at given time or null if not available
*/
public @Nullable BigDecimal getReducedElectricityTax(Instant time) {
return reducedElectricityTaxMap.get(getHourStart(time));
}

/**
* Get map of all cached reduced electricity taxes.
*
* @return reduced electricity taxes currently available, {@link #NUMBER_OF_HISTORIC_HOURS} back
*/
public Map<Instant, BigDecimal> getReducedElectricityTaxes() {
return new HashMap<Instant, BigDecimal>(reducedElectricityTaxMap);
}

/**
* Get current transmission net tariff.
*
Expand Down Expand Up @@ -399,6 +450,15 @@ public boolean areElectricityTaxesValidTomorrow() {
return isValidNextDay(electricityTaxRecords);
}

/**
* Check if we have "raw" reduced electricity tax records cached which are valid tomorrow.
*
* @return true if reduced electricity tax records for tomorrow are cached
*/
public boolean areReducedElectricityTaxesValidTomorrow() {
return isValidNextDay(reducedElectricityTaxRecords);
}

/**
* Check if we have "raw" transmission net tariff records cached which are valid tomorrow.
*
Expand All @@ -418,6 +478,7 @@ public void cleanup() {
netTariffMap.entrySet().removeIf(entry -> entry.getKey().isBefore(firstHourStart));
systemTariffMap.entrySet().removeIf(entry -> entry.getKey().isBefore(firstHourStart));
electricityTaxMap.entrySet().removeIf(entry -> entry.getKey().isBefore(firstHourStart));
reducedElectricityTaxMap.entrySet().removeIf(entry -> entry.getKey().isBefore(firstHourStart));
transmissionNetTariffMap.entrySet().removeIf(entry -> entry.getKey().isBefore(firstHourStart));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,16 @@ public class EnergiDataServiceBindingConstants {
+ "system-tariff";
public static final String CHANNEL_ELECTRICITY_TAX = CHANNEL_GROUP_ELECTRICITY + ChannelUID.CHANNEL_GROUP_SEPARATOR
+ "electricity-tax";
public static final String CHANNEL_REDUCED_ELECTRICITY_TAX = CHANNEL_GROUP_ELECTRICITY
+ ChannelUID.CHANNEL_GROUP_SEPARATOR + "reduced-electricity-tax";
public static final String CHANNEL_TRANSMISSION_NET_TARIFF = CHANNEL_GROUP_ELECTRICITY
+ ChannelUID.CHANNEL_GROUP_SEPARATOR + "transmission-net-tariff";
public static final String CHANNEL_HOURLY_PRICES = CHANNEL_GROUP_ELECTRICITY + ChannelUID.CHANNEL_GROUP_SEPARATOR
+ "hourly-prices";

public static final Set<String> ELECTRICITY_CHANNELS = Set.of(CHANNEL_SPOT_PRICE, CHANNEL_NET_TARIFF,
CHANNEL_SYSTEM_TARIFF, CHANNEL_ELECTRICITY_TAX, CHANNEL_TRANSMISSION_NET_TARIFF, CHANNEL_HOURLY_PRICES);
CHANNEL_SYSTEM_TARIFF, CHANNEL_ELECTRICITY_TAX, CHANNEL_REDUCED_ELECTRICITY_TAX,
CHANNEL_TRANSMISSION_NET_TARIFF, CHANNEL_HOURLY_PRICES);

// List of all properties
public static final String PROPERTY_REMAINING_CALLS = "remainingCalls";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ private enum PriceElement {
NET_TARIFF("nettariff"),
SYSTEM_TARIFF("systemtariff"),
ELECTRICITY_TAX("electricitytax"),
REDUCED_ELECTRICITY_TAX("reducedElectricitytax"),
TRANSMISSION_NET_TARIFF("transmissionnettariff");

private static final Map<String, PriceElement> NAME_MAP = Stream.of(values())
Expand Down Expand Up @@ -93,7 +94,8 @@ public static PriceElement fromString(final String name) {

@RuleAction(label = "@text/action.get-prices.label", description = "@text/action.get-prices.description")
public @ActionOutput(name = "prices", type = "java.util.Map<java.time.Instant, java.math.BigDecimal>") Map<Instant, BigDecimal> getPrices() {
return getPrices(Arrays.stream(PriceElement.values()).collect(Collectors.toSet()));
return getPrices(Arrays.stream(PriceElement.values())
.filter(element -> element != PriceElement.REDUCED_ELECTRICITY_TAX).collect(Collectors.toSet()));
}

@RuleAction(label = "@text/action.get-prices.label", description = "@text/action.get-prices.description")
Expand Down Expand Up @@ -250,6 +252,11 @@ private Map<Instant, BigDecimal> getPrices(Set<PriceElement> priceElements) {
mergeMaps(prices, electricityTaxMap, !spotPricesRequired);
}

if (priceElements.contains(PriceElement.REDUCED_ELECTRICITY_TAX)) {
Map<Instant, BigDecimal> reducedElectricityTaxMap = handler.getReducedElectricityTaxes();
mergeMaps(prices, reducedElectricityTaxMap, !spotPricesRequired);
}

if (priceElements.contains(PriceElement.TRANSMISSION_NET_TARIFF)) {
Map<Instant, BigDecimal> transmissionNetTariffMap = handler.getTransmissionNetTariffs();
mergeMaps(prices, transmissionNetTariffMap, !spotPricesRequired);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ public class DatahubTariffFilterFactory {
private static final String NOTE_NET_TARIFF_C_FLEX_HOUR = NOTE_NET_TARIFF_C_FLEX + " - time";
private static final String NOTE_SYSTEM_TARIFF = "Systemtarif";
private static final String NOTE_ELECTRICITY_TAX = "Elafgift";
private static final String NOTE_REDUCED_ELECTRICITY_TAX = "Reduceret elafgift";
private static final String NOTE_TRANSMISSION_NET_TARIFF = "Transmissions nettarif";

public static final LocalDate N1_CUTOFF_DATE = LocalDate.of(2023, 1, 1);
Expand Down Expand Up @@ -165,6 +166,11 @@ public static DatahubTariffFilter getElectricityTax() {
DateQueryParameter.of(ENERGINET_CUTOFF_DATE));
}

public static DatahubTariffFilter getReducedElectricityTax() {
return new DatahubTariffFilter(Set.of(), Set.of(NOTE_REDUCED_ELECTRICITY_TAX),
DateQueryParameter.of(LocalDate.of(2021, 2, 1)));
}

public static DatahubTariffFilter getTransmissionNetTariff() {
return new DatahubTariffFilter(Set.of(), Set.of(NOTE_TRANSMISSION_NET_TARIFF),
DateQueryParameter.of(ENERGINET_CUTOFF_DATE));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public class EnergiDataServiceHandler extends BaseThingHandler {

private record Price(String hourStart, BigDecimal spotPrice, String spotPriceCurrency,
@Nullable BigDecimal netTariff, @Nullable BigDecimal systemTariff, @Nullable BigDecimal electricityTax,
@Nullable BigDecimal transmissionNetTariff) {
@Nullable BigDecimal reducedElectricityTax, @Nullable BigDecimal transmissionNetTariff) {
}

public EnergiDataServiceHandler(Thing thing, HttpClient httpClient, TimeZoneProvider timeZoneProvider) {
Expand Down Expand Up @@ -182,6 +182,10 @@ private void refreshElectricityPrices() {
downloadElectricityTaxes();
}

if (isLinked(CHANNEL_REDUCED_ELECTRICITY_TAX) || isLinked(CHANNEL_HOURLY_PRICES)) {
downloadReducedElectricityTaxes();
}

if (isLinked(CHANNEL_TRANSMISSION_NET_TARIFF) || isLinked(CHANNEL_HOURLY_PRICES)) {
downloadTransmissionNetTariffs();
}
Expand Down Expand Up @@ -281,6 +285,20 @@ private void downloadElectricityTaxes() throws InterruptedException, DataService
}
}

private void downloadReducedElectricityTaxes() throws InterruptedException, DataServiceException {
GlobalLocationNumber globalLocationNumber = config.getEnerginetGLN();
if (globalLocationNumber.isEmpty()) {
return;
}
if (cacheManager.areReducedElectricityTaxesValidTomorrow()) {
logger.debug("Cached reduced electricity taxes still valid, skipping download.");
cacheManager.updateReducedElectricityTaxes();
} else {
cacheManager.putReducedElectricityTaxes(
downloadPriceLists(globalLocationNumber, DatahubTariffFilterFactory.getReducedElectricityTax()));
}
}

private void downloadTransmissionNetTariffs() throws InterruptedException, DataServiceException {
GlobalLocationNumber globalLocationNumber = config.getEnerginetGLN();
if (globalLocationNumber.isEmpty()) {
Expand Down Expand Up @@ -343,6 +361,7 @@ private void updatePrices() {
updateCurrentTariff(CHANNEL_NET_TARIFF, cacheManager.getNetTariff());
updateCurrentTariff(CHANNEL_SYSTEM_TARIFF, cacheManager.getSystemTariff());
updateCurrentTariff(CHANNEL_ELECTRICITY_TAX, cacheManager.getElectricityTax());
updateCurrentTariff(CHANNEL_REDUCED_ELECTRICITY_TAX, cacheManager.getReducedElectricityTax());
updateCurrentTariff(CHANNEL_TRANSMISSION_NET_TARIFF, cacheManager.getTransmissionNetTariff());
updateHourlyPrices();

Expand Down Expand Up @@ -379,9 +398,10 @@ private void updateHourlyPrices() {
BigDecimal netTariff = cacheManager.getNetTariff(hourStart);
BigDecimal systemTariff = cacheManager.getSystemTariff(hourStart);
BigDecimal electricityTax = cacheManager.getElectricityTax(hourStart);
BigDecimal reducedElectricityTax = cacheManager.getReducedElectricityTax(hourStart);
BigDecimal transmissionNetTariff = cacheManager.getTransmissionNetTariff(hourStart);
targetPrices[i++] = new Price(hourStart.toString(), sourcePrice.getValue(), config.currencyCode, netTariff,
systemTariff, electricityTax, transmissionNetTariff);
systemTariff, electricityTax, reducedElectricityTax, transmissionNetTariff);
}
updateState(CHANNEL_HOURLY_PRICES, new StringType(gson.toJson(targetPrices)));
}
Expand Down Expand Up @@ -483,6 +503,28 @@ public Map<Instant, BigDecimal> getElectricityTaxes() {
return cacheManager.getElectricityTaxes();
}

/**
* Get cached reduced electricity taxes or try once to download them if not cached
* (usually if no items are linked).
*
* @return Map of future reduced electricity taxes
*/
public Map<Instant, BigDecimal> getReducedElectricityTaxes() {
try {
downloadReducedElectricityTaxes();
} catch (DataServiceException e) {
if (logger.isDebugEnabled()) {
logger.warn("Error retrieving reduced electricity taxes", e);
} else {
logger.warn("Error retrieving reduced electricity taxes: {}", e.getMessage());
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}

return cacheManager.getReducedElectricityTaxes();
}

/**
* Return cached transmission net tariffs or try once to download them if not cached
* (usually if no items are linked).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ channel-group-type.energidataservice.electricity.channel.electricity-tax.label =
channel-group-type.energidataservice.electricity.channel.electricity-tax.description = Current electricity tax in DKK per kWh.
channel-group-type.energidataservice.electricity.channel.net-tariff.label = Net Tariff
channel-group-type.energidataservice.electricity.channel.net-tariff.description = Current net tariff in DKK per kWh.
channel-group-type.energidataservice.electricity.channel.reduced-electricity-tax.label = Reduced Electricity Tax
channel-group-type.energidataservice.electricity.channel.reduced-electricity-tax.description = Current reduced electricity tax in DKK per kWh. For electric heating customers only.
channel-group-type.energidataservice.electricity.channel.spot-price.label = Spot Price
channel-group-type.energidataservice.electricity.channel.spot-price.description = Current spot price in DKK or EUR per kWh.
channel-group-type.energidataservice.electricity.channel.system-tariff.label = System Tariff
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@
<label>Electricity Tax</label>
<description>Current electricity tax in DKK per kWh.</description>
</channel>
<channel id="reduced-electricity-tax" typeId="datahub-price">
<label>Reduced Electricity Tax</label>
<description>Current reduced electricity tax in DKK per kWh. For electric heating customers only.</description>
</channel>
<channel id="transmission-net-tariff" typeId="datahub-price">
<label>Transmission Net Tariff</label>
<description>Current transmission net tariff in DKK per kWh.</description>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
<channel-group id="electricity" typeId="electricity"/>
</channel-groups>

<properties>
<property name="thingTypeVersion">1</property>
</properties>

<config-description-ref uri="thing-type:energidataservice:service"/>
</thing-type>

Expand Down
Loading

0 comments on commit e184f78

Please sign in to comment.