evcc-io/cmd/metrics_forecast.go
2026-05-22 13:39:04 +02:00

82 lines
2.2 KiB
Go

package cmd
import (
"fmt"
"io"
"os"
"text/tabwriter"
"github.com/evcc-io/evcc/core/metrics"
"github.com/spf13/cobra"
)
// metricsForecastCmd represents the metrics forecast command
var metricsForecastCmd = &cobra.Command{
Use: "forecast",
Short: "Compare solar forecast against actual PV production",
Args: cobra.NoArgs,
Run: runMetricsForecast,
}
func init() {
metricsCmd.AddCommand(metricsForecastCmd)
metricsForecastCmd.Flags().String("range", "", "Quick timeframe: day, month or year")
metricsForecastCmd.Flags().String("from", "", "Start date as YYYY-MM-DD (default today)")
metricsForecastCmd.Flags().String("to", "", "End date as YYYY-MM-DD, inclusive (default today)")
metricsForecastCmd.MarkFlagsMutuallyExclusive("range", "from")
metricsForecastCmd.MarkFlagsMutuallyExclusive("range", "to")
}
func runMetricsForecast(cmd *cobra.Command, args []string) {
setupMetrics(cmd)
from, to, err := metricsTimeframe(cmd.Flag("range").Value.String(), cmd.Flag("from").Value.String(), cmd.Flag("to").Value.String())
if err != nil {
log.FATAL.Fatal(err)
}
series, err := metrics.QueryEnergy(from, to, "month", false)
if err != nil {
log.FATAL.Fatal(err)
}
forecast, actual := metricsForecastTotals(series)
metricsWriteForecastTable(os.Stdout, forecast, actual)
fmt.Fprintln(os.Stderr, "\nvalues in kWh")
}
// metricsForecastTotals sums forecasted solar energy and actual PV production
// over the given series.
func metricsForecastTotals(series []metrics.Series) (forecast, actual float64) {
for _, s := range series {
var sum float64
for _, slot := range s.Data {
sum += slot.Energy
}
switch s.Group {
case metrics.Forecast:
forecast += sum
case metrics.PV:
actual += sum
}
}
return forecast, actual
}
// metricsWriteForecastTable renders a one-row comparison of forecasted versus
// actual solar energy. Accuracy is the actual/forecast ratio, left blank when
// nothing was forecast.
func metricsWriteForecastTable(w io.Writer, forecast, actual float64) {
tw := tabwriter.NewWriter(w, 0, 0, 2, ' ', 0)
fmt.Fprintln(tw, "forecast\tactual\taccuracy")
accuracy := ""
if forecast > 0 {
accuracy = fmt.Sprintf("%.1f%%", actual/forecast*100)
}
fmt.Fprintf(tw, "%.3f\t%.3f\t%s\n", forecast, actual, accuracy)
tw.Flush()
}