Skip to content

Commit 01faac1

Browse files
authored
Add solar forecast using Solcast and Forecast.Solar (evcc-io#18269)
1 parent 7e8e182 commit 01faac1

File tree

18 files changed

+415
-90
lines changed

18 files changed

+415
-90
lines changed

api/globalconfig/types.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ type Tariffs struct {
140140
FeedIn config.Typed
141141
Co2 config.Typed
142142
Planner config.Typed
143+
Solar config.Typed
143144
}
144145

145146
type Network struct {

api/tariff.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package api
22

33
//go:generate enumer -type TariffType -trimprefix TariffType -transform=lower -text
4+
//go:generate enumer -type TariffUsage -trimprefix TariffUsage -transform=lower
45

56
type TariffType int
67

@@ -10,4 +11,16 @@ const (
1011
TariffTypePriceDynamic
1112
TariffTypePriceForecast
1213
TariffTypeCo2
14+
TariffTypeSolar
15+
)
16+
17+
type TariffUsage int
18+
19+
const (
20+
_ TariffUsage = iota
21+
TariffUsageCo2
22+
TariffUsageFeedin
23+
TariffUsageGrid
24+
TariffUsagePlanner
25+
TariffUsageSolar
1326
)

api/tarifftype_enumer.go

Lines changed: 8 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/tariffusage_enumer.go

Lines changed: 91 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cmd/setup.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -755,11 +755,12 @@ func tariffInstance(name string, conf config.Typed) (api.Tariff, error) {
755755
return instance, nil
756756
}
757757

758-
func configureTariff(name string, conf config.Typed, t *api.Tariff) error {
758+
func configureTariff(u api.TariffUsage, conf config.Typed, t *api.Tariff) error {
759759
if conf.Type == "" {
760760
return nil
761761
}
762762

763+
name := u.String()
763764
res, err := tariffInstance(name, conf)
764765
if err != nil {
765766
return &DeviceError{name, err}
@@ -786,10 +787,11 @@ func configureTariffs(conf globalconfig.Tariffs) (*tariff.Tariffs, error) {
786787
}
787788

788789
var eg errgroup.Group
789-
eg.Go(func() error { return configureTariff("grid", conf.Grid, &tariffs.Grid) })
790-
eg.Go(func() error { return configureTariff("feedin", conf.FeedIn, &tariffs.FeedIn) })
791-
eg.Go(func() error { return configureTariff("co2", conf.Co2, &tariffs.Co2) })
792-
eg.Go(func() error { return configureTariff("planner", conf.Planner, &tariffs.Planner) })
790+
eg.Go(func() error { return configureTariff(api.TariffUsageGrid, conf.Grid, &tariffs.Grid) })
791+
eg.Go(func() error { return configureTariff(api.TariffUsageFeedin, conf.FeedIn, &tariffs.FeedIn) })
792+
eg.Go(func() error { return configureTariff(api.TariffUsageCo2, conf.Co2, &tariffs.Co2) })
793+
eg.Go(func() error { return configureTariff(api.TariffUsagePlanner, conf.Planner, &tariffs.Planner) })
794+
eg.Go(func() error { return configureTariff(api.TariffUsageSolar, conf.Solar, &tariffs.Solar) })
793795

794796
if err := eg.Wait(); err != nil {
795797
return nil, &ClassError{ClassTariff, err}

cmd/tariff.go

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,14 @@ func runTariff(cmd *cobra.Command, args []string) {
4242
name = args[0]
4343
}
4444

45-
for key, tf := range map[string]api.Tariff{
46-
"grid": tariffs.Grid,
47-
"feedin": tariffs.FeedIn,
48-
"co2": tariffs.Co2,
49-
"planner": tariffs.Planner,
45+
for u, tf := range map[api.TariffUsage]api.Tariff{
46+
api.TariffUsageGrid: tariffs.Grid,
47+
api.TariffUsageFeedin: tariffs.FeedIn,
48+
api.TariffUsageCo2: tariffs.Co2,
49+
api.TariffUsagePlanner: tariffs.Planner,
50+
api.TariffUsageSolar: tariffs.Solar,
5051
} {
52+
key := u.String()
5153
if name != "" && key != name {
5254
continue
5355
}
@@ -65,8 +67,20 @@ func runTariff(cmd *cobra.Command, args []string) {
6567
fatal(err)
6668
}
6769

70+
unit := "Price/Cost"
71+
switch tf.Type() {
72+
case api.TariffTypeCo2:
73+
unit += "Footprint (gCO2/kWh)"
74+
case api.TariffTypeSolar:
75+
unit = "Yield (W)"
76+
default:
77+
if c := conf.Tariffs.Currency; c != "" {
78+
unit += fmt.Sprintf(" (%s/kWh)", c)
79+
}
80+
}
81+
6882
tw := tabwriter.NewWriter(os.Stdout, 0, 0, 4, ' ', 0)
69-
fmt.Fprintln(tw, "From\tTo\tPrice/Cost")
83+
fmt.Fprintln(tw, "From\tTo\t"+unit)
7084
const format = "2006-01-02 15:04:05"
7185

7286
for _, r := range rates {

core/keys/site.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ const (
2626
TariffGrid = "tariffGrid"
2727
TariffPriceHome = "tariffPriceHome"
2828
TariffPriceLoadpoints = "tariffPriceLoadpoints"
29+
TariffSolar = "tariffSolar"
2930
Vehicles = "vehicles"
3031

3132
// meters

core/site.go

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ func (site *Site) Boot(log *util.Logger, loadpoints []*Loadpoint, tariffs *tarif
151151
})
152152
}
153153

154-
tariff := site.GetTariff(PlannerTariff)
154+
tariff := site.GetTariff(api.TariffUsagePlanner)
155155

156156
// give loadpoints access to vehicles and database
157157
for _, lp := range loadpoints {
@@ -757,8 +757,8 @@ func (site *Site) greenShare(powerFrom float64, powerTo float64) float64 {
757757

758758
// effectivePrice calculates the real energy price based on self-produced and grid-imported energy.
759759
func (site *Site) effectivePrice(greenShare float64) *float64 {
760-
if grid, err := site.tariffs.CurrentGridPrice(); err == nil {
761-
feedin, err := site.tariffs.CurrentFeedInPrice()
760+
if grid, err := tariff.CurrentPrice(site.GetTariff(api.TariffUsageGrid)); err == nil {
761+
feedin, err := tariff.CurrentPrice(site.GetTariff(api.TariffUsageFeedin))
762762
if err != nil {
763763
feedin = 0
764764
}
@@ -770,7 +770,7 @@ func (site *Site) effectivePrice(greenShare float64) *float64 {
770770

771771
// effectiveCo2 calculates the amount of emitted co2 based on self-produced and grid-imported energy.
772772
func (site *Site) effectiveCo2(greenShare float64) *float64 {
773-
if co2, err := site.tariffs.CurrentCo2(); err == nil {
773+
if co2, err := tariff.CurrentPrice(site.GetTariff(api.TariffUsageCo2)); err == nil {
774774
effCo2 := co2 * (1 - greenShare)
775775
return &effCo2
776776
}
@@ -781,26 +781,29 @@ func (site *Site) publishTariffs(greenShareHome float64, greenShareLoadpoints fl
781781
site.publish(keys.GreenShareHome, greenShareHome)
782782
site.publish(keys.GreenShareLoadpoints, greenShareLoadpoints)
783783

784-
if gridPrice, err := site.tariffs.CurrentGridPrice(); err == nil {
785-
site.publish(keys.TariffGrid, gridPrice)
784+
if v, err := tariff.CurrentPrice(site.GetTariff(api.TariffUsageGrid)); err == nil {
785+
site.publish(keys.TariffGrid, v)
786786
}
787-
if feedInPrice, err := site.tariffs.CurrentFeedInPrice(); err == nil {
788-
site.publish(keys.TariffFeedIn, feedInPrice)
787+
if v, err := tariff.CurrentPrice(site.GetTariff(api.TariffUsageFeedin)); err == nil {
788+
site.publish(keys.TariffFeedIn, v)
789789
}
790-
if co2, err := site.tariffs.CurrentCo2(); err == nil {
791-
site.publish(keys.TariffCo2, co2)
790+
if v, err := tariff.CurrentPrice(site.GetTariff(api.TariffUsageCo2)); err == nil {
791+
site.publish(keys.TariffCo2, v)
792792
}
793-
if price := site.effectivePrice(greenShareHome); price != nil {
794-
site.publish(keys.TariffPriceHome, price)
793+
if v, err := tariff.CurrentPrice(site.GetTariff(api.TariffUsageSolar)); err == nil {
794+
site.publish(keys.TariffSolar, v)
795795
}
796-
if co2 := site.effectiveCo2(greenShareHome); co2 != nil {
797-
site.publish(keys.TariffCo2Home, co2)
796+
if v := site.effectivePrice(greenShareHome); v != nil {
797+
site.publish(keys.TariffPriceHome, v)
798798
}
799-
if price := site.effectivePrice(greenShareLoadpoints); price != nil {
800-
site.publish(keys.TariffPriceLoadpoints, price)
799+
if v := site.effectiveCo2(greenShareHome); v != nil {
800+
site.publish(keys.TariffCo2Home, v)
801801
}
802-
if co2 := site.effectiveCo2(greenShareLoadpoints); co2 != nil {
803-
site.publish(keys.TariffCo2Loadpoints, co2)
802+
if v := site.effectivePrice(greenShareLoadpoints); v != nil {
803+
site.publish(keys.TariffPriceLoadpoints, v)
804+
}
805+
if v := site.effectiveCo2(greenShareLoadpoints); v != nil {
806+
site.publish(keys.TariffCo2Loadpoints, v)
804807
}
805808
}
806809

@@ -926,7 +929,7 @@ func (site *Site) prepare() {
926929
site.publish(keys.ResidualPower, site.GetResidualPower())
927930

928931
site.publish(keys.Currency, site.tariffs.Currency)
929-
if tariff := site.GetTariff(PlannerTariff); tariff != nil {
932+
if tariff := site.GetTariff(api.TariffUsagePlanner); tariff != nil {
930933
site.publish(keys.SmartCostType, tariff.Type())
931934
} else {
932935
site.publish(keys.SmartCostType, nil)

core/site/api.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ type API interface {
5555
//
5656

5757
// GetTariff returns the respective tariff
58-
GetTariff(string) api.Tariff
58+
GetTariff(api.TariffUsage) api.Tariff
5959

6060
//
6161
// battery control

core/site_api.go

Lines changed: 2 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,6 @@ var _ site.API = (*Site)(nil)
1717

1818
var ErrBatteryNotConfigured = errors.New("battery not configured")
1919

20-
const (
21-
GridTariff = "grid"
22-
FeedinTariff = "feedin"
23-
PlannerTariff = "planner"
24-
)
25-
2620
// isConfigurable checks if the meter is configurable
2721
func isConfigurable(ref string) bool {
2822
dev, _ := config.Meters().ByName(ref)
@@ -272,39 +266,10 @@ func (site *Site) SetResidualPower(power float64) error {
272266
}
273267

274268
// GetTariff returns the respective tariff if configured or nil
275-
func (site *Site) GetTariff(tariff string) api.Tariff {
269+
func (site *Site) GetTariff(tariff api.TariffUsage) api.Tariff {
276270
site.RLock()
277271
defer site.RUnlock()
278-
279-
switch tariff {
280-
case GridTariff:
281-
return site.tariffs.Grid
282-
283-
case FeedinTariff:
284-
return site.tariffs.FeedIn
285-
286-
case PlannerTariff:
287-
switch {
288-
case site.tariffs.Planner != nil:
289-
// prio 0: manually set planner tariff
290-
return site.tariffs.Planner
291-
292-
case site.tariffs.Grid != nil && site.tariffs.Grid.Type() == api.TariffTypePriceForecast:
293-
// prio 1: grid tariff with forecast
294-
return site.tariffs.Grid
295-
296-
case site.tariffs.Co2 != nil:
297-
// prio 2: co2 tariff
298-
return site.tariffs.Co2
299-
300-
default:
301-
// prio 3: static grid tariff
302-
return site.tariffs.Grid
303-
}
304-
305-
default:
306-
return nil
307-
}
272+
return site.tariffs.Get(tariff)
308273
}
309274

310275
// GetBatteryDischargeControl returns the battery control mode (no discharge only)

0 commit comments

Comments
 (0)