82 lines
2.2 KiB
Go
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()
|
|
}
|