mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
Introduce console command for history persistence (#16656)
Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk>
This commit is contained in:
parent
be7c6bbcdb
commit
321fde5486
@ -90,6 +90,19 @@ The recommended persistence strategy is `forecast`, as it ensures a clean histor
|
|||||||
Prices from the past 24 hours and all forthcoming prices will be stored.
|
Prices from the past 24 hours and all forthcoming prices will be stored.
|
||||||
Any changes that impact published prices (e.g. selecting or deselecting VAT Profile) will result in the replacement of persisted prices within this period.
|
Any changes that impact published prices (e.g. selecting or deselecting VAT Profile) will result in the replacement of persisted prices within this period.
|
||||||
|
|
||||||
|
##### Manually Persisting History
|
||||||
|
|
||||||
|
During extended service interruptions, data unavailability, or openHAB downtime, historic prices may be absent from persistence.
|
||||||
|
A console command is provided to fill gaps: `energidataservice update [SpotPrice|GridTariff|SystemTariff|TransmissionGridTariff|ElectricityTax|ReducedElectricitytax] <StartDate> [<EndDate>]`.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
energidataservice update spotprice 2024-04-12 2024-04-14
|
||||||
|
```
|
||||||
|
|
||||||
|
This can also be useful for retrospectively changing the [VAT profile](https://www.openhab.org/addons/transformations/vat/).
|
||||||
|
|
||||||
#### Grid Tariff
|
#### Grid Tariff
|
||||||
|
|
||||||
Discounts are automatically taken into account for channel `grid-tariff` so that it represents the actual price.
|
Discounts are automatically taken into account for channel `grid-tariff` so that it represents the actual price.
|
||||||
|
@ -106,7 +106,7 @@ public class ApiController {
|
|||||||
* @throws DataServiceException
|
* @throws DataServiceException
|
||||||
*/
|
*/
|
||||||
public ElspotpriceRecord[] getSpotPrices(String priceArea, Currency currency, DateQueryParameter start,
|
public ElspotpriceRecord[] getSpotPrices(String priceArea, Currency currency, DateQueryParameter start,
|
||||||
Map<String, String> properties) throws InterruptedException, DataServiceException {
|
DateQueryParameter end, Map<String, String> properties) throws InterruptedException, DataServiceException {
|
||||||
if (!SUPPORTED_CURRENCIES.contains(currency)) {
|
if (!SUPPORTED_CURRENCIES.contains(currency)) {
|
||||||
throw new IllegalArgumentException("Invalid currency " + currency.getCurrencyCode());
|
throw new IllegalArgumentException("Invalid currency " + currency.getCurrencyCode());
|
||||||
}
|
}
|
||||||
@ -119,6 +119,10 @@ public class ApiController {
|
|||||||
.agent(userAgent) //
|
.agent(userAgent) //
|
||||||
.method(HttpMethod.GET);
|
.method(HttpMethod.GET);
|
||||||
|
|
||||||
|
if (!end.isEmpty()) {
|
||||||
|
request = request.param("end", end.toString());
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String responseContent = sendRequest(request, properties);
|
String responseContent = sendRequest(request, properties);
|
||||||
ElspotpriceRecords records = gson.fromJson(responseContent, ElspotpriceRecords.class);
|
ElspotpriceRecords records = gson.fromJson(responseContent, ElspotpriceRecords.class);
|
||||||
@ -209,9 +213,14 @@ public class ApiController {
|
|||||||
.agent(userAgent) //
|
.agent(userAgent) //
|
||||||
.method(HttpMethod.GET);
|
.method(HttpMethod.GET);
|
||||||
|
|
||||||
DateQueryParameter dateQueryParameter = tariffFilter.getDateQueryParameter();
|
DateQueryParameter start = tariffFilter.getStart();
|
||||||
if (!dateQueryParameter.isEmpty()) {
|
if (!start.isEmpty()) {
|
||||||
request = request.param("start", dateQueryParameter.toString());
|
request = request.param("start", start.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
DateQueryParameter end = tariffFilter.getEnd();
|
||||||
|
if (!end.isEmpty()) {
|
||||||
|
request = request.param("end", end.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -31,7 +31,7 @@ import org.openhab.core.thing.ThingTypeUID;
|
|||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
public class EnergiDataServiceBindingConstants {
|
public class EnergiDataServiceBindingConstants {
|
||||||
|
|
||||||
private static final String BINDING_ID = "energidataservice";
|
public static final String BINDING_ID = "energidataservice";
|
||||||
|
|
||||||
// List of all Thing Type UIDs
|
// List of all Thing Type UIDs
|
||||||
public static final ThingTypeUID THING_TYPE_SERVICE = new ThingTypeUID(BINDING_ID, "service");
|
public static final ThingTypeUID THING_TYPE_SERVICE = new ThingTypeUID(BINDING_ID, "service");
|
||||||
|
@ -0,0 +1,70 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.energidataservice.internal;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link PriceComponent} represents the different components making up the total electricity price.
|
||||||
|
*
|
||||||
|
* @author Jacob Laursen - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public enum PriceComponent {
|
||||||
|
SPOT_PRICE("SpotPrice", null),
|
||||||
|
GRID_TARIFF("GridTariff", DatahubTariff.GRID_TARIFF),
|
||||||
|
SYSTEM_TARIFF("SystemTariff", DatahubTariff.SYSTEM_TARIFF),
|
||||||
|
TRANSMISSION_GRID_TARIFF("TransmissionGridTariff", DatahubTariff.TRANSMISSION_GRID_TARIFF),
|
||||||
|
ELECTRICITY_TAX("ElectricityTax", DatahubTariff.ELECTRICITY_TAX),
|
||||||
|
REDUCED_ELECTRICITY_TAX("ReducedElectricityTax", DatahubTariff.REDUCED_ELECTRICITY_TAX);
|
||||||
|
|
||||||
|
private static final Map<String, PriceComponent> NAME_MAP = Stream.of(values())
|
||||||
|
.collect(Collectors.toMap(PriceComponent::toLowerCaseString, Function.identity()));
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
private @Nullable DatahubTariff datahubTariff;
|
||||||
|
|
||||||
|
private PriceComponent(String name, @Nullable DatahubTariff datahubTariff) {
|
||||||
|
this.name = name;
|
||||||
|
this.datahubTariff = datahubTariff;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String toLowerCaseString() {
|
||||||
|
return name.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PriceComponent fromString(final String name) {
|
||||||
|
PriceComponent myEnum = NAME_MAP.get(name.toLowerCase());
|
||||||
|
if (null == myEnum) {
|
||||||
|
throw new IllegalArgumentException(String.format("'%s' has no corresponding value. Accepted values: %s",
|
||||||
|
name, Arrays.asList(values())));
|
||||||
|
}
|
||||||
|
return myEnum;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable DatahubTariff getDatahubTariff() {
|
||||||
|
return datahubTariff;
|
||||||
|
}
|
||||||
|
}
|
@ -47,9 +47,19 @@ public class PriceListParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Map<Instant, BigDecimal> toHourly(Collection<DatahubPricelistRecord> records) {
|
public Map<Instant, BigDecimal> toHourly(Collection<DatahubPricelistRecord> records) {
|
||||||
|
Instant firstHourStart = Instant.now(clock).minus(CacheManager.NUMBER_OF_HISTORIC_HOURS, ChronoUnit.HOURS)
|
||||||
|
.truncatedTo(ChronoUnit.HOURS);
|
||||||
|
Instant lastHourStart = Instant.now(clock).truncatedTo(ChronoUnit.HOURS).plus(2, ChronoUnit.DAYS)
|
||||||
|
.truncatedTo(ChronoUnit.DAYS);
|
||||||
|
|
||||||
|
return toHourly(records, firstHourStart, lastHourStart);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<Instant, BigDecimal> toHourly(Collection<DatahubPricelistRecord> records, Instant firstHourStart,
|
||||||
|
Instant lastHourStart) {
|
||||||
Map<Instant, BigDecimal> totalMap = new ConcurrentHashMap<>(CacheManager.TARIFF_MAX_CACHE_SIZE);
|
Map<Instant, BigDecimal> totalMap = new ConcurrentHashMap<>(CacheManager.TARIFF_MAX_CACHE_SIZE);
|
||||||
records.stream().map(record -> record.chargeTypeCode()).distinct().forEach(chargeTypeCode -> {
|
records.stream().map(record -> record.chargeTypeCode()).distinct().forEach(chargeTypeCode -> {
|
||||||
Map<Instant, BigDecimal> currentMap = toHourly(records, chargeTypeCode);
|
Map<Instant, BigDecimal> currentMap = toHourly(records, chargeTypeCode, firstHourStart, lastHourStart);
|
||||||
for (Entry<Instant, BigDecimal> current : currentMap.entrySet()) {
|
for (Entry<Instant, BigDecimal> current : currentMap.entrySet()) {
|
||||||
BigDecimal total = totalMap.get(current.getKey());
|
BigDecimal total = totalMap.get(current.getKey());
|
||||||
if (total == null) {
|
if (total == null) {
|
||||||
@ -62,14 +72,10 @@ public class PriceListParser {
|
|||||||
return totalMap;
|
return totalMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<Instant, BigDecimal> toHourly(Collection<DatahubPricelistRecord> records, String chargeTypeCode) {
|
private Map<Instant, BigDecimal> toHourly(Collection<DatahubPricelistRecord> records, String chargeTypeCode,
|
||||||
|
Instant firstHourStart, Instant lastHourStart) {
|
||||||
Map<Instant, BigDecimal> tariffMap = new ConcurrentHashMap<>(CacheManager.TARIFF_MAX_CACHE_SIZE);
|
Map<Instant, BigDecimal> tariffMap = new ConcurrentHashMap<>(CacheManager.TARIFF_MAX_CACHE_SIZE);
|
||||||
|
|
||||||
Instant firstHourStart = Instant.now(clock).minus(CacheManager.NUMBER_OF_HISTORIC_HOURS, ChronoUnit.HOURS)
|
|
||||||
.truncatedTo(ChronoUnit.HOURS);
|
|
||||||
Instant lastHourStart = Instant.now(clock).truncatedTo(ChronoUnit.HOURS).plus(2, ChronoUnit.DAYS)
|
|
||||||
.truncatedTo(ChronoUnit.DAYS);
|
|
||||||
|
|
||||||
LocalDateTime previousValidFrom = LocalDateTime.MAX;
|
LocalDateTime previousValidFrom = LocalDateTime.MAX;
|
||||||
LocalDateTime previousValidTo = LocalDateTime.MIN;
|
LocalDateTime previousValidTo = LocalDateTime.MIN;
|
||||||
Map<LocalTime, BigDecimal> tariffs = Map.of();
|
Map<LocalTime, BigDecimal> tariffs = Map.of();
|
||||||
|
@ -24,9 +24,7 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import javax.measure.quantity.Energy;
|
import javax.measure.quantity.Energy;
|
||||||
import javax.measure.quantity.Power;
|
import javax.measure.quantity.Power;
|
||||||
@ -35,6 +33,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
|
|||||||
import org.eclipse.jdt.annotation.Nullable;
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
import org.openhab.binding.energidataservice.internal.DatahubTariff;
|
import org.openhab.binding.energidataservice.internal.DatahubTariff;
|
||||||
import org.openhab.binding.energidataservice.internal.PriceCalculator;
|
import org.openhab.binding.energidataservice.internal.PriceCalculator;
|
||||||
|
import org.openhab.binding.energidataservice.internal.PriceComponent;
|
||||||
import org.openhab.binding.energidataservice.internal.exception.MissingPriceException;
|
import org.openhab.binding.energidataservice.internal.exception.MissingPriceException;
|
||||||
import org.openhab.binding.energidataservice.internal.handler.EnergiDataServiceHandler;
|
import org.openhab.binding.energidataservice.internal.handler.EnergiDataServiceHandler;
|
||||||
import org.openhab.core.automation.annotation.ActionInput;
|
import org.openhab.core.automation.annotation.ActionInput;
|
||||||
@ -64,44 +63,6 @@ public class EnergiDataServiceActions implements ThingActions {
|
|||||||
|
|
||||||
private @Nullable EnergiDataServiceHandler handler;
|
private @Nullable EnergiDataServiceHandler handler;
|
||||||
|
|
||||||
private enum PriceComponent {
|
|
||||||
SPOT_PRICE("spotprice", null),
|
|
||||||
GRID_TARIFF("gridtariff", DatahubTariff.GRID_TARIFF),
|
|
||||||
SYSTEM_TARIFF("systemtariff", DatahubTariff.SYSTEM_TARIFF),
|
|
||||||
TRANSMISSION_GRID_TARIFF("transmissiongridtariff", DatahubTariff.TRANSMISSION_GRID_TARIFF),
|
|
||||||
ELECTRICITY_TAX("electricitytax", DatahubTariff.ELECTRICITY_TAX),
|
|
||||||
REDUCED_ELECTRICITY_TAX("reducedelectricitytax", DatahubTariff.REDUCED_ELECTRICITY_TAX);
|
|
||||||
|
|
||||||
private static final Map<String, PriceComponent> NAME_MAP = Stream.of(values())
|
|
||||||
.collect(Collectors.toMap(PriceComponent::toString, Function.identity()));
|
|
||||||
|
|
||||||
private String name;
|
|
||||||
private @Nullable DatahubTariff datahubTariff;
|
|
||||||
|
|
||||||
private PriceComponent(String name, @Nullable DatahubTariff datahubTariff) {
|
|
||||||
this.name = name;
|
|
||||||
this.datahubTariff = datahubTariff;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static PriceComponent fromString(final String name) {
|
|
||||||
PriceComponent myEnum = NAME_MAP.get(name.toLowerCase());
|
|
||||||
if (null == myEnum) {
|
|
||||||
throw new IllegalArgumentException(String.format("'%s' has no corresponding value. Accepted values: %s",
|
|
||||||
name, Arrays.asList(values())));
|
|
||||||
}
|
|
||||||
return myEnum;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @Nullable DatahubTariff getDatahubTariff() {
|
|
||||||
return datahubTariff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@RuleAction(label = "@text/action.get-prices.label", description = "@text/action.get-prices.description")
|
@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() {
|
public @ActionOutput(name = "prices", type = "java.util.Map<java.time.Instant, java.math.BigDecimal>") Map<Instant, BigDecimal> getPrices() {
|
||||||
EnergiDataServiceHandler handler = this.handler;
|
EnergiDataServiceHandler handler = this.handler;
|
||||||
|
@ -27,21 +27,31 @@ public class DatahubTariffFilter {
|
|||||||
|
|
||||||
private final Set<ChargeTypeCode> chargeTypeCodes;
|
private final Set<ChargeTypeCode> chargeTypeCodes;
|
||||||
private final Set<String> notes;
|
private final Set<String> notes;
|
||||||
private final DateQueryParameter dateQueryParameter;
|
private final DateQueryParameter start;
|
||||||
|
private final DateQueryParameter end;
|
||||||
|
|
||||||
public DatahubTariffFilter(DatahubTariffFilter filter, DateQueryParameter dateQueryParameter) {
|
public DatahubTariffFilter(DatahubTariffFilter filter, DateQueryParameter start) {
|
||||||
this(filter.chargeTypeCodes, filter.notes, dateQueryParameter);
|
this(filter, start, DateQueryParameter.EMPTY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DatahubTariffFilter(DatahubTariffFilter filter, DateQueryParameter start, DateQueryParameter end) {
|
||||||
|
this(filter.chargeTypeCodes, filter.notes, start, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DatahubTariffFilter(Set<ChargeTypeCode> chargeTypeCodes, Set<String> notes) {
|
public DatahubTariffFilter(Set<ChargeTypeCode> chargeTypeCodes, Set<String> notes) {
|
||||||
this(chargeTypeCodes, notes, DateQueryParameter.EMPTY);
|
this(chargeTypeCodes, notes, DateQueryParameter.EMPTY);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DatahubTariffFilter(Set<ChargeTypeCode> chargeTypeCodes, Set<String> notes,
|
public DatahubTariffFilter(Set<ChargeTypeCode> chargeTypeCodes, Set<String> notes, DateQueryParameter start) {
|
||||||
DateQueryParameter dateQueryParameter) {
|
this(chargeTypeCodes, notes, start, DateQueryParameter.EMPTY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DatahubTariffFilter(Set<ChargeTypeCode> chargeTypeCodes, Set<String> notes, DateQueryParameter start,
|
||||||
|
DateQueryParameter end) {
|
||||||
this.chargeTypeCodes = chargeTypeCodes;
|
this.chargeTypeCodes = chargeTypeCodes;
|
||||||
this.notes = notes;
|
this.notes = notes;
|
||||||
this.dateQueryParameter = dateQueryParameter;
|
this.start = start;
|
||||||
|
this.end = end;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Collection<String> getChargeTypeCodesAsStrings() {
|
public Collection<String> getChargeTypeCodesAsStrings() {
|
||||||
@ -52,7 +62,11 @@ public class DatahubTariffFilter {
|
|||||||
return notes;
|
return notes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DateQueryParameter getDateQueryParameter() {
|
public DateQueryParameter getStart() {
|
||||||
return dateQueryParameter;
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateQueryParameter getEnd() {
|
||||||
|
return end;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,6 +72,14 @@ public class DateQueryParameter {
|
|||||||
return this == EMPTY;
|
return this == EMPTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public @Nullable DateQueryParameterType getDateType() {
|
||||||
|
return dateType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable LocalDate getDate() {
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
|
||||||
public static DateQueryParameter of(LocalDate localDate) {
|
public static DateQueryParameter of(LocalDate localDate) {
|
||||||
return new DateQueryParameter(localDate);
|
return new DateQueryParameter(localDate);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,173 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.energidataservice.internal.console;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.format.DateTimeParseException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.binding.energidataservice.internal.DatahubTariff;
|
||||||
|
import org.openhab.binding.energidataservice.internal.EnergiDataServiceBindingConstants;
|
||||||
|
import org.openhab.binding.energidataservice.internal.PriceComponent;
|
||||||
|
import org.openhab.binding.energidataservice.internal.exception.DataServiceException;
|
||||||
|
import org.openhab.binding.energidataservice.internal.handler.EnergiDataServiceHandler;
|
||||||
|
import org.openhab.core.io.console.Console;
|
||||||
|
import org.openhab.core.io.console.ConsoleCommandCompleter;
|
||||||
|
import org.openhab.core.io.console.StringsCompleter;
|
||||||
|
import org.openhab.core.io.console.extensions.AbstractConsoleCommandExtension;
|
||||||
|
import org.openhab.core.io.console.extensions.ConsoleCommandExtension;
|
||||||
|
import org.openhab.core.thing.ThingRegistry;
|
||||||
|
import org.osgi.service.component.annotations.Activate;
|
||||||
|
import org.osgi.service.component.annotations.Component;
|
||||||
|
import org.osgi.service.component.annotations.Reference;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link EnergiDataServiceCommandExtension} is responsible for handling console commands.
|
||||||
|
*
|
||||||
|
* @author Jacob Laursen - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
@Component(service = ConsoleCommandExtension.class)
|
||||||
|
public class EnergiDataServiceCommandExtension extends AbstractConsoleCommandExtension {
|
||||||
|
|
||||||
|
private static final String SUBCMD_UPDATE = "update";
|
||||||
|
|
||||||
|
private static final StringsCompleter SUBCMD_COMPLETER = new StringsCompleter(List.of(SUBCMD_UPDATE), false);
|
||||||
|
|
||||||
|
private final ThingRegistry thingRegistry;
|
||||||
|
|
||||||
|
private class EnergiDataServiceConsoleCommandCompleter implements ConsoleCommandCompleter {
|
||||||
|
@Override
|
||||||
|
public boolean complete(String[] args, int cursorArgumentIndex, int cursorPosition, List<String> candidates) {
|
||||||
|
if (cursorArgumentIndex <= 0) {
|
||||||
|
return SUBCMD_COMPLETER.complete(args, cursorArgumentIndex, cursorPosition, candidates);
|
||||||
|
} else if (cursorArgumentIndex == 1) {
|
||||||
|
return new StringsCompleter(Stream.of(PriceComponent.values()).map(PriceComponent::toString).toList(),
|
||||||
|
false).complete(args, cursorArgumentIndex, cursorPosition, candidates);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Activate
|
||||||
|
public EnergiDataServiceCommandExtension(final @Reference ThingRegistry thingRegistry) {
|
||||||
|
super(EnergiDataServiceBindingConstants.BINDING_ID, "Interact with the Energi Data Service binding.");
|
||||||
|
this.thingRegistry = thingRegistry;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(String[] args, Console console) {
|
||||||
|
if (args.length < 1) {
|
||||||
|
printUsage(console);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (args[0].toLowerCase()) {
|
||||||
|
case SUBCMD_UPDATE -> update(args, console);
|
||||||
|
default -> printUsage(console);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void update(String[] args, Console console) {
|
||||||
|
ParsedUpdateParameters updateParameters;
|
||||||
|
try {
|
||||||
|
updateParameters = new ParsedUpdateParameters(args);
|
||||||
|
|
||||||
|
for (EnergiDataServiceHandler handler : thingRegistry.getAll().stream().map(thing -> thing.getHandler())
|
||||||
|
.filter(EnergiDataServiceHandler.class::isInstance).map(EnergiDataServiceHandler.class::cast)
|
||||||
|
.toList()) {
|
||||||
|
Instant measureStart = Instant.now();
|
||||||
|
int items = switch (updateParameters.priceComponent) {
|
||||||
|
case SPOT_PRICE ->
|
||||||
|
handler.updateSpotPriceTimeSeries(updateParameters.startDate, updateParameters.endDate);
|
||||||
|
default -> {
|
||||||
|
DatahubTariff datahubTariff = updateParameters.priceComponent.getDatahubTariff();
|
||||||
|
yield datahubTariff == null ? 0
|
||||||
|
: handler.updateTariffTimeSeries(datahubTariff, updateParameters.startDate,
|
||||||
|
updateParameters.endDate);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Instant measureEnd = Instant.now();
|
||||||
|
console.println(items + " prices updated as time series in "
|
||||||
|
+ Duration.between(measureStart, measureEnd).toMillis() + " milliseconds.");
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
console.println("Interrupted.");
|
||||||
|
} catch (DataServiceException e) {
|
||||||
|
console.println("Failed to fetch prices: " + e.getMessage());
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
String message = e.getMessage();
|
||||||
|
if (message != null) {
|
||||||
|
console.println(message);
|
||||||
|
}
|
||||||
|
printUsage(console);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ParsedUpdateParameters {
|
||||||
|
PriceComponent priceComponent;
|
||||||
|
LocalDate startDate;
|
||||||
|
LocalDate endDate;
|
||||||
|
|
||||||
|
private int ARGUMENT_POSITION_PRICE_COMPONENT = 1;
|
||||||
|
private int ARGUMENT_POSITION_START_DATE = 2;
|
||||||
|
private int ARGUMENT_POSITION_END_DATE = 3;
|
||||||
|
|
||||||
|
ParsedUpdateParameters(String[] args) {
|
||||||
|
if (args.length < 3 || args.length > 4) {
|
||||||
|
throw new IllegalArgumentException("Incorrect number of parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
priceComponent = PriceComponent.fromString(args[ARGUMENT_POSITION_PRICE_COMPONENT].toLowerCase());
|
||||||
|
|
||||||
|
try {
|
||||||
|
startDate = LocalDate.parse(args[ARGUMENT_POSITION_START_DATE]);
|
||||||
|
} catch (DateTimeParseException e) {
|
||||||
|
throw new IllegalArgumentException("Invalid start date: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
endDate = args.length == 3 ? startDate : LocalDate.parse(args[ARGUMENT_POSITION_END_DATE]);
|
||||||
|
} catch (DateTimeParseException e) {
|
||||||
|
throw new IllegalArgumentException("Invalid end date: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (endDate.isBefore(startDate)) {
|
||||||
|
throw new IllegalArgumentException("End date must be equal to or higher than start date");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (endDate.isAfter(LocalDate.now())) {
|
||||||
|
throw new IllegalArgumentException("Future end date is not allowed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getUsages() {
|
||||||
|
return Arrays.asList(buildCommandUsage(SUBCMD_UPDATE + " ["
|
||||||
|
+ String.join("|", Stream.of(PriceComponent.values()).map(PriceComponent::toString).toList())
|
||||||
|
+ "] <StartDate> [<EndDate>]", "Update time series in requested period"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable ConsoleCommandCompleter getCompleter() {
|
||||||
|
return new EnergiDataServiceConsoleCommandCompleter();
|
||||||
|
}
|
||||||
|
}
|
@ -18,12 +18,15 @@ import static org.openhab.core.types.TimeSeries.Policy.REPLACE;
|
|||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.LocalTime;
|
import java.time.LocalTime;
|
||||||
|
import java.time.ZoneId;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.time.temporal.ChronoUnit;
|
import java.time.temporal.ChronoUnit;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.Currency;
|
import java.util.Currency;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -41,6 +44,8 @@ import org.eclipse.jetty.http.HttpStatus;
|
|||||||
import org.openhab.binding.energidataservice.internal.ApiController;
|
import org.openhab.binding.energidataservice.internal.ApiController;
|
||||||
import org.openhab.binding.energidataservice.internal.CacheManager;
|
import org.openhab.binding.energidataservice.internal.CacheManager;
|
||||||
import org.openhab.binding.energidataservice.internal.DatahubTariff;
|
import org.openhab.binding.energidataservice.internal.DatahubTariff;
|
||||||
|
import org.openhab.binding.energidataservice.internal.EnergiDataServiceBindingConstants;
|
||||||
|
import org.openhab.binding.energidataservice.internal.PriceListParser;
|
||||||
import org.openhab.binding.energidataservice.internal.action.EnergiDataServiceActions;
|
import org.openhab.binding.energidataservice.internal.action.EnergiDataServiceActions;
|
||||||
import org.openhab.binding.energidataservice.internal.api.ChargeType;
|
import org.openhab.binding.energidataservice.internal.api.ChargeType;
|
||||||
import org.openhab.binding.energidataservice.internal.api.ChargeTypeCode;
|
import org.openhab.binding.energidataservice.internal.api.ChargeTypeCode;
|
||||||
@ -246,7 +251,7 @@ public class EnergiDataServiceHandler extends BaseThingHandler {
|
|||||||
|
|
||||||
updateStatus(ThingStatus.ONLINE);
|
updateStatus(ThingStatus.ONLINE);
|
||||||
updatePrices();
|
updatePrices();
|
||||||
updateTimeSeries();
|
updateElectricityTimeSeriesFromCache();
|
||||||
|
|
||||||
if (isLinked(CHANNEL_SPOT_PRICE)) {
|
if (isLinked(CHANNEL_SPOT_PRICE)) {
|
||||||
long numberOfFutureSpotPrices = cacheManager.getNumberOfFutureSpotPrices();
|
long numberOfFutureSpotPrices = cacheManager.getNumberOfFutureSpotPrices();
|
||||||
@ -297,7 +302,7 @@ public class EnergiDataServiceHandler extends BaseThingHandler {
|
|||||||
Map<String, String> properties = editProperties();
|
Map<String, String> properties = editProperties();
|
||||||
try {
|
try {
|
||||||
ElspotpriceRecord[] spotPriceRecords = apiController.getSpotPrices(config.priceArea, config.getCurrency(),
|
ElspotpriceRecord[] spotPriceRecords = apiController.getSpotPrices(config.priceArea, config.getCurrency(),
|
||||||
start, properties);
|
start, DateQueryParameter.EMPTY, properties);
|
||||||
cacheManager.putSpotPrices(spotPriceRecords, config.getCurrency());
|
cacheManager.putSpotPrices(spotPriceRecords, config.getCurrency());
|
||||||
} finally {
|
} finally {
|
||||||
updateProperties(properties);
|
updateProperties(properties);
|
||||||
@ -305,10 +310,7 @@ public class EnergiDataServiceHandler extends BaseThingHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void downloadTariffs(DatahubTariff datahubTariff) throws InterruptedException, DataServiceException {
|
private void downloadTariffs(DatahubTariff datahubTariff) throws InterruptedException, DataServiceException {
|
||||||
GlobalLocationNumber globalLocationNumber = switch (datahubTariff) {
|
GlobalLocationNumber globalLocationNumber = getGlobalLocationNumber(datahubTariff);
|
||||||
case GRID_TARIFF -> config.getGridCompanyGLN();
|
|
||||||
default -> config.getEnerginetGLN();
|
|
||||||
};
|
|
||||||
if (globalLocationNumber.isEmpty()) {
|
if (globalLocationNumber.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -316,17 +318,28 @@ public class EnergiDataServiceHandler extends BaseThingHandler {
|
|||||||
logger.debug("Cached tariffs of type {} still valid, skipping download.", datahubTariff);
|
logger.debug("Cached tariffs of type {} still valid, skipping download.", datahubTariff);
|
||||||
cacheManager.updateTariffs(datahubTariff);
|
cacheManager.updateTariffs(datahubTariff);
|
||||||
} else {
|
} else {
|
||||||
DatahubTariffFilter filter = switch (datahubTariff) {
|
DatahubTariffFilter filter = getDatahubTariffFilter(datahubTariff);
|
||||||
case GRID_TARIFF -> getGridTariffFilter();
|
|
||||||
case SYSTEM_TARIFF -> DatahubTariffFilterFactory.getSystemTariff();
|
|
||||||
case TRANSMISSION_GRID_TARIFF -> DatahubTariffFilterFactory.getTransmissionGridTariff();
|
|
||||||
case ELECTRICITY_TAX -> DatahubTariffFilterFactory.getElectricityTax();
|
|
||||||
case REDUCED_ELECTRICITY_TAX -> DatahubTariffFilterFactory.getReducedElectricityTax();
|
|
||||||
};
|
|
||||||
cacheManager.putTariffs(datahubTariff, downloadPriceLists(globalLocationNumber, filter));
|
cacheManager.putTariffs(datahubTariff, downloadPriceLists(globalLocationNumber, filter));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private DatahubTariffFilter getDatahubTariffFilter(DatahubTariff datahubTariff) {
|
||||||
|
return switch (datahubTariff) {
|
||||||
|
case GRID_TARIFF -> getGridTariffFilter();
|
||||||
|
case SYSTEM_TARIFF -> DatahubTariffFilterFactory.getSystemTariff();
|
||||||
|
case TRANSMISSION_GRID_TARIFF -> DatahubTariffFilterFactory.getTransmissionGridTariff();
|
||||||
|
case ELECTRICITY_TAX -> DatahubTariffFilterFactory.getElectricityTax();
|
||||||
|
case REDUCED_ELECTRICITY_TAX -> DatahubTariffFilterFactory.getReducedElectricityTax();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private GlobalLocationNumber getGlobalLocationNumber(DatahubTariff datahubTariff) {
|
||||||
|
return switch (datahubTariff) {
|
||||||
|
case GRID_TARIFF -> config.getGridCompanyGLN();
|
||||||
|
default -> config.getEnerginetGLN();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private Collection<DatahubPricelistRecord> downloadPriceLists(GlobalLocationNumber globalLocationNumber,
|
private Collection<DatahubPricelistRecord> downloadPriceLists(GlobalLocationNumber globalLocationNumber,
|
||||||
DatahubTariffFilter filter) throws InterruptedException, DataServiceException {
|
DatahubTariffFilter filter) throws InterruptedException, DataServiceException {
|
||||||
Map<String, String> properties = editProperties();
|
Map<String, String> properties = editProperties();
|
||||||
@ -369,8 +382,8 @@ public class EnergiDataServiceHandler extends BaseThingHandler {
|
|||||||
start);
|
start);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new DatahubTariffFilter(filter, DateQueryParameter.of(filter.getDateQueryParameter(),
|
return new DatahubTariffFilter(filter,
|
||||||
Duration.ofHours(-CacheManager.NUMBER_OF_HISTORIC_HOURS)));
|
DateQueryParameter.of(filter.getStart(), Duration.ofHours(-CacheManager.NUMBER_OF_HISTORIC_HOURS)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void refreshCo2EmissionPrognosis() {
|
private void refreshCo2EmissionPrognosis() {
|
||||||
@ -494,7 +507,79 @@ public class EnergiDataServiceHandler extends BaseThingHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateTimeSeries() {
|
/**
|
||||||
|
* Download spot prices in requested period and update corresponding channel with time series.
|
||||||
|
*
|
||||||
|
* @param startDate Start date of period
|
||||||
|
* @param endDate End date of period
|
||||||
|
* @return number of published states
|
||||||
|
*/
|
||||||
|
public int updateSpotPriceTimeSeries(LocalDate startDate, LocalDate endDate)
|
||||||
|
throws InterruptedException, DataServiceException {
|
||||||
|
if (!isLinked(CHANNEL_SPOT_PRICE)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
Map<String, String> properties = editProperties();
|
||||||
|
try {
|
||||||
|
Currency currency = config.getCurrency();
|
||||||
|
ElspotpriceRecord[] spotPriceRecords = apiController.getSpotPrices(config.priceArea, currency,
|
||||||
|
DateQueryParameter.of(startDate), DateQueryParameter.of(endDate.plusDays(1)), properties);
|
||||||
|
boolean isDKK = EnergiDataServiceBindingConstants.CURRENCY_DKK.equals(currency);
|
||||||
|
TimeSeries spotPriceTimeSeries = new TimeSeries(REPLACE);
|
||||||
|
if (spotPriceRecords.length == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
for (ElspotpriceRecord record : Arrays.stream(spotPriceRecords)
|
||||||
|
.sorted(Comparator.comparing(ElspotpriceRecord::hour)).toList()) {
|
||||||
|
spotPriceTimeSeries.add(record.hour(), getEnergyPrice(
|
||||||
|
(isDKK ? record.spotPriceDKK() : record.spotPriceEUR()).divide(BigDecimal.valueOf(1000)),
|
||||||
|
currency));
|
||||||
|
}
|
||||||
|
sendTimeSeries(CHANNEL_SPOT_PRICE, spotPriceTimeSeries);
|
||||||
|
return spotPriceRecords.length;
|
||||||
|
} finally {
|
||||||
|
updateProperties(properties);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Download tariffs in requested period and update corresponding channel with time series.
|
||||||
|
*
|
||||||
|
* @param datahubTariff Tariff to update
|
||||||
|
* @param startDate Start date of period
|
||||||
|
* @param endDate End date of period
|
||||||
|
* @return number of published states
|
||||||
|
*/
|
||||||
|
public int updateTariffTimeSeries(DatahubTariff datahubTariff, LocalDate startDate, LocalDate endDate)
|
||||||
|
throws InterruptedException, DataServiceException {
|
||||||
|
if (!isLinked(datahubTariff.getChannelId())) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
GlobalLocationNumber globalLocationNumber = getGlobalLocationNumber(datahubTariff);
|
||||||
|
if (globalLocationNumber.isEmpty()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
DatahubTariffFilter filter = getDatahubTariffFilter(datahubTariff);
|
||||||
|
DateQueryParameter start = filter.getStart();
|
||||||
|
DateQueryParameterType filterStartDateType = start.getDateType();
|
||||||
|
LocalDate filterStartDate = start.getDate();
|
||||||
|
if (filterStartDateType != null) {
|
||||||
|
// For filters with date relative to current date, override with provided parameters.
|
||||||
|
filter = new DatahubTariffFilter(filter, DateQueryParameter.of(startDate), DateQueryParameter.of(endDate));
|
||||||
|
} else if (filterStartDate != null && startDate.isBefore(filterStartDate)) {
|
||||||
|
throw new IllegalArgumentException("Start date before " + start.getDate() + " is not supported");
|
||||||
|
}
|
||||||
|
Collection<DatahubPricelistRecord> datahubRecords = downloadPriceLists(globalLocationNumber, filter);
|
||||||
|
ZoneId zoneId = timeZoneProvider.getTimeZone();
|
||||||
|
Instant firstHourStart = startDate.atStartOfDay(zoneId).toInstant();
|
||||||
|
Instant lastHourStart = endDate.plusDays(1).atStartOfDay(zoneId).toInstant();
|
||||||
|
Map<Instant, BigDecimal> tariffMap = new PriceListParser().toHourly(datahubRecords, firstHourStart,
|
||||||
|
lastHourStart);
|
||||||
|
|
||||||
|
return updatePriceTimeSeries(datahubTariff.getChannelId(), tariffMap, CURRENCY_DKK, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateElectricityTimeSeriesFromCache() {
|
||||||
updatePriceTimeSeries(CHANNEL_SPOT_PRICE, cacheManager.getSpotPrices(), config.getCurrency(), false);
|
updatePriceTimeSeries(CHANNEL_SPOT_PRICE, cacheManager.getSpotPrices(), config.getCurrency(), false);
|
||||||
|
|
||||||
for (DatahubTariff datahubTariff : DatahubTariff.values()) {
|
for (DatahubTariff datahubTariff : DatahubTariff.values()) {
|
||||||
@ -503,10 +588,10 @@ public class EnergiDataServiceHandler extends BaseThingHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updatePriceTimeSeries(String channelId, Map<Instant, BigDecimal> priceMap, Currency currency,
|
private int updatePriceTimeSeries(String channelId, Map<Instant, BigDecimal> priceMap, Currency currency,
|
||||||
boolean deduplicate) {
|
boolean deduplicate) {
|
||||||
if (!isLinked(channelId)) {
|
if (!isLinked(channelId)) {
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
List<Entry<Instant, BigDecimal>> prices = priceMap.entrySet().stream().sorted(Map.Entry.comparingByKey())
|
List<Entry<Instant, BigDecimal>> prices = priceMap.entrySet().stream().sorted(Map.Entry.comparingByKey())
|
||||||
.toList();
|
.toList();
|
||||||
@ -525,6 +610,7 @@ public class EnergiDataServiceHandler extends BaseThingHandler {
|
|||||||
if (timeSeries.size() > 0) {
|
if (timeSeries.size() > 0) {
|
||||||
sendTimeSeries(channelId, timeSeries);
|
sendTimeSeries(channelId, timeSeries);
|
||||||
}
|
}
|
||||||
|
return timeSeries.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -97,7 +97,8 @@ public class PriceListParserTest {
|
|||||||
PriceListParser priceListParser = new PriceListParser(
|
PriceListParser priceListParser = new PriceListParser(
|
||||||
Clock.fixed(Instant.parse("2022-12-31T12:00:00Z"), EnergiDataServiceBindingConstants.DATAHUB_TIMEZONE));
|
Clock.fixed(Instant.parse("2022-12-31T12:00:00Z"), EnergiDataServiceBindingConstants.DATAHUB_TIMEZONE));
|
||||||
DatahubPricelistRecords records = getObjectFromJson("DatahubPricelistN1.json", DatahubPricelistRecords.class);
|
DatahubPricelistRecords records = getObjectFromJson("DatahubPricelistN1.json", DatahubPricelistRecords.class);
|
||||||
Map<Instant, BigDecimal> tariffMap = priceListParser.toHourly(Arrays.stream(records.records()).toList(), "CD");
|
Map<Instant, BigDecimal> tariffMap = priceListParser
|
||||||
|
.toHourly(Arrays.stream(records.records()).filter(r -> r.chargeTypeCode().equals("CD")).toList());
|
||||||
|
|
||||||
assertThat(tariffMap.size(), is(60));
|
assertThat(tariffMap.size(), is(60));
|
||||||
assertThat(tariffMap.get(Instant.parse("2022-12-31T22:00:00Z")), is(equalTo(new BigDecimal("0.407717"))));
|
assertThat(tariffMap.get(Instant.parse("2022-12-31T22:00:00Z")), is(equalTo(new BigDecimal("0.407717"))));
|
||||||
@ -110,8 +111,8 @@ public class PriceListParserTest {
|
|||||||
PriceListParser priceListParser = new PriceListParser(
|
PriceListParser priceListParser = new PriceListParser(
|
||||||
Clock.fixed(Instant.parse("2022-12-31T12:00:00Z"), EnergiDataServiceBindingConstants.DATAHUB_TIMEZONE));
|
Clock.fixed(Instant.parse("2022-12-31T12:00:00Z"), EnergiDataServiceBindingConstants.DATAHUB_TIMEZONE));
|
||||||
DatahubPricelistRecords records = getObjectFromJson("DatahubPricelistN1.json", DatahubPricelistRecords.class);
|
DatahubPricelistRecords records = getObjectFromJson("DatahubPricelistN1.json", DatahubPricelistRecords.class);
|
||||||
Map<Instant, BigDecimal> tariffMap = priceListParser.toHourly(Arrays.stream(records.records()).toList(),
|
Map<Instant, BigDecimal> tariffMap = priceListParser
|
||||||
"CD R");
|
.toHourly(Arrays.stream(records.records()).filter(r -> r.chargeTypeCode().equals("CD R")).toList());
|
||||||
|
|
||||||
assertThat(tariffMap.size(), is(60));
|
assertThat(tariffMap.size(), is(60));
|
||||||
assertThat(tariffMap.get(Instant.parse("2022-12-31T22:00:00Z")), is(equalTo(new BigDecimal("-0.407717"))));
|
assertThat(tariffMap.get(Instant.parse("2022-12-31T22:00:00Z")), is(equalTo(new BigDecimal("-0.407717"))));
|
||||||
|
Loading…
Reference in New Issue
Block a user