]> git.basschouten.com Git - openhab-addons.git/blob
6b32f35fe318de7c2ab734f658db303d4d15b132
[openhab-addons.git] /
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.energidataservice.internal.console;
14
15 import java.time.Duration;
16 import java.time.Instant;
17 import java.time.LocalDate;
18 import java.time.format.DateTimeParseException;
19 import java.util.Arrays;
20 import java.util.List;
21 import java.util.stream.Stream;
22
23 import org.eclipse.jdt.annotation.NonNullByDefault;
24 import org.eclipse.jdt.annotation.Nullable;
25 import org.openhab.binding.energidataservice.internal.DatahubTariff;
26 import org.openhab.binding.energidataservice.internal.EnergiDataServiceBindingConstants;
27 import org.openhab.binding.energidataservice.internal.PriceComponent;
28 import org.openhab.binding.energidataservice.internal.exception.DataServiceException;
29 import org.openhab.binding.energidataservice.internal.handler.EnergiDataServiceHandler;
30 import org.openhab.core.io.console.Console;
31 import org.openhab.core.io.console.ConsoleCommandCompleter;
32 import org.openhab.core.io.console.StringsCompleter;
33 import org.openhab.core.io.console.extensions.AbstractConsoleCommandExtension;
34 import org.openhab.core.io.console.extensions.ConsoleCommandExtension;
35 import org.openhab.core.thing.ThingRegistry;
36 import org.osgi.service.component.annotations.Activate;
37 import org.osgi.service.component.annotations.Component;
38 import org.osgi.service.component.annotations.Reference;
39
40 /**
41  * The {@link EnergiDataServiceCommandExtension} is responsible for handling console commands.
42  *
43  * @author Jacob Laursen - Initial contribution
44  */
45 @NonNullByDefault
46 @Component(service = ConsoleCommandExtension.class)
47 public class EnergiDataServiceCommandExtension extends AbstractConsoleCommandExtension {
48
49     private static final String SUBCMD_UPDATE = "update";
50
51     private static final StringsCompleter SUBCMD_COMPLETER = new StringsCompleter(List.of(SUBCMD_UPDATE), false);
52
53     private final ThingRegistry thingRegistry;
54
55     private class EnergiDataServiceConsoleCommandCompleter implements ConsoleCommandCompleter {
56         @Override
57         public boolean complete(String[] args, int cursorArgumentIndex, int cursorPosition, List<String> candidates) {
58             if (cursorArgumentIndex <= 0) {
59                 return SUBCMD_COMPLETER.complete(args, cursorArgumentIndex, cursorPosition, candidates);
60             } else if (cursorArgumentIndex == 1) {
61                 return new StringsCompleter(Stream.of(PriceComponent.values()).map(PriceComponent::toString).toList(),
62                         false).complete(args, cursorArgumentIndex, cursorPosition, candidates);
63             }
64             return false;
65         }
66     }
67
68     @Activate
69     public EnergiDataServiceCommandExtension(final @Reference ThingRegistry thingRegistry) {
70         super(EnergiDataServiceBindingConstants.BINDING_ID, "Interact with the Energi Data Service binding.");
71         this.thingRegistry = thingRegistry;
72     }
73
74     @Override
75     public void execute(String[] args, Console console) {
76         if (args.length < 1) {
77             printUsage(console);
78             return;
79         }
80
81         switch (args[0].toLowerCase()) {
82             case SUBCMD_UPDATE -> update(args, console);
83             default -> printUsage(console);
84         }
85     }
86
87     private void update(String[] args, Console console) {
88         ParsedUpdateParameters updateParameters;
89         try {
90             updateParameters = new ParsedUpdateParameters(args);
91
92             for (EnergiDataServiceHandler handler : thingRegistry.getAll().stream().map(thing -> thing.getHandler())
93                     .filter(EnergiDataServiceHandler.class::isInstance).map(EnergiDataServiceHandler.class::cast)
94                     .toList()) {
95                 Instant measureStart = Instant.now();
96                 int items = switch (updateParameters.priceComponent) {
97                     case SPOT_PRICE ->
98                         handler.updateSpotPriceTimeSeries(updateParameters.startDate, updateParameters.endDate);
99                     default -> {
100                         DatahubTariff datahubTariff = updateParameters.priceComponent.getDatahubTariff();
101                         yield datahubTariff == null ? 0
102                                 : handler.updateTariffTimeSeries(datahubTariff, updateParameters.startDate,
103                                         updateParameters.endDate);
104                     }
105                 };
106                 Instant measureEnd = Instant.now();
107                 console.println(items + " prices updated as time series in "
108                         + Duration.between(measureStart, measureEnd).toMillis() + " milliseconds.");
109             }
110         } catch (InterruptedException e) {
111             console.println("Interrupted.");
112         } catch (DataServiceException e) {
113             console.println("Failed to fetch prices: " + e.getMessage());
114         } catch (IllegalArgumentException e) {
115             String message = e.getMessage();
116             if (message != null) {
117                 console.println(message);
118             }
119             printUsage(console);
120             return;
121         }
122     }
123
124     private class ParsedUpdateParameters {
125         PriceComponent priceComponent;
126         LocalDate startDate;
127         LocalDate endDate;
128
129         private int ARGUMENT_POSITION_PRICE_COMPONENT = 1;
130         private int ARGUMENT_POSITION_START_DATE = 2;
131         private int ARGUMENT_POSITION_END_DATE = 3;
132
133         ParsedUpdateParameters(String[] args) {
134             if (args.length < 3 || args.length > 4) {
135                 throw new IllegalArgumentException("Incorrect number of parameters");
136             }
137
138             priceComponent = PriceComponent.fromString(args[ARGUMENT_POSITION_PRICE_COMPONENT].toLowerCase());
139
140             try {
141                 startDate = LocalDate.parse(args[ARGUMENT_POSITION_START_DATE]);
142             } catch (DateTimeParseException e) {
143                 throw new IllegalArgumentException("Invalid start date: " + e.getMessage(), e);
144             }
145
146             try {
147                 endDate = args.length == 3 ? startDate : LocalDate.parse(args[ARGUMENT_POSITION_END_DATE]);
148             } catch (DateTimeParseException e) {
149                 throw new IllegalArgumentException("Invalid end date: " + e.getMessage(), e);
150             }
151
152             if (endDate.isBefore(startDate)) {
153                 throw new IllegalArgumentException("End date must be equal to or higher than start date");
154             }
155
156             if (endDate.isAfter(LocalDate.now())) {
157                 throw new IllegalArgumentException("Future end date is not allowed");
158             }
159         }
160     }
161
162     @Override
163     public List<String> getUsages() {
164         return Arrays.asList(buildCommandUsage(SUBCMD_UPDATE + " ["
165                 + String.join("|", Stream.of(PriceComponent.values()).map(PriceComponent::toString).toList())
166                 + "] <StartDate> [<EndDate>]", "Update time series in requested period"));
167     }
168
169     @Override
170     public @Nullable ConsoleCommandCompleter getCompleter() {
171         return new EnergiDataServiceConsoleCommandCompleter();
172     }
173 }