forked from evcc-io/evcc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmeter.go
123 lines (100 loc) · 3.18 KB
/
meter.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
package meter
import (
"errors"
"fmt"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/provider"
"github.com/evcc-io/evcc/util"
)
func init() {
registry.Add(api.Custom, NewConfigurableFromConfig)
}
//go:generate go run ../cmd/tools/decorate.go -f decorateMeter -b api.Meter -t "api.MeterEnergy,TotalEnergy,func() (float64, error)" -t "api.MeterCurrent,Currents,func() (float64, float64, float64, error)" -t "api.Battery,SoC,func() (float64, error)"
// NewConfigurableFromConfig creates api.Meter from config
func NewConfigurableFromConfig(other map[string]interface{}) (api.Meter, error) {
cc := struct {
Power provider.Config
Energy *provider.Config // optional
SoC *provider.Config // optional
Currents []provider.Config // optional
}{}
if err := util.DecodeOther(other, &cc); err != nil {
return nil, err
}
power, err := provider.NewFloatGetterFromConfig(cc.Power)
if err != nil {
return nil, fmt.Errorf("power: %w", err)
}
m, _ := NewConfigurable(power)
// decorate Meter with MeterEnergy
var totalEnergyG func() (float64, error)
if cc.Energy != nil {
totalEnergyG, err = provider.NewFloatGetterFromConfig(*cc.Energy)
if err != nil {
return nil, fmt.Errorf("energy: %w", err)
}
}
// decorate Meter with MeterCurrent
var currentsG func() (float64, float64, float64, error)
if len(cc.Currents) > 0 {
if len(cc.Currents) != 3 {
return nil, errors.New("need 3 currents")
}
var curr []func() (float64, error)
for idx, cc := range cc.Currents {
c, err := provider.NewFloatGetterFromConfig(cc)
if err != nil {
return nil, fmt.Errorf("currents[%d]: %w", idx, err)
}
curr = append(curr, c)
}
currentsG = collectCurrentProviders(curr)
}
// decorate Meter with BatterySoC
var batterySoCG func() (float64, error)
if cc.SoC != nil {
batterySoCG, err = provider.NewFloatGetterFromConfig(*cc.SoC)
if err != nil {
return nil, fmt.Errorf("battery: %w", err)
}
}
res := m.Decorate(totalEnergyG, currentsG, batterySoCG)
return res, nil
}
// collectCurrentProviders combines phase getters into currents api function
func collectCurrentProviders(g []func() (float64, error)) func() (float64, float64, float64, error) {
return func() (float64, float64, float64, error) {
var currents []float64
for _, currentG := range g {
c, err := currentG()
if err != nil {
return 0, 0, 0, err
}
currents = append(currents, c)
}
return currents[0], currents[1], currents[2], nil
}
}
// NewConfigurable creates a new meter
func NewConfigurable(currentPowerG func() (float64, error)) (*Meter, error) {
m := &Meter{
currentPowerG: currentPowerG,
}
return m, nil
}
// Meter is an api.Meter implementation with configurable getters and setters.
type Meter struct {
currentPowerG func() (float64, error)
}
// Decorate attaches additional capabilities to the base meter
func (m *Meter) Decorate(
totalEnergy func() (float64, error),
currents func() (float64, float64, float64, error),
batterySoC func() (float64, error),
) api.Meter {
return decorateMeter(m, totalEnergy, currents, batterySoC)
}
// CurrentPower implements the api.Meter interface
func (m *Meter) CurrentPower() (float64, error) {
return m.currentPowerG()
}