forked from evcc-io/evcc
/
meter.go
152 lines (124 loc) · 3.79 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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
package meter
import (
"errors"
"fmt"
"github.com/mark-sch/evcc/api"
"github.com/mark-sch/evcc/provider"
"github.com/mark-sch/evcc/util"
)
func init() {
registry.Add("default", NewConfigurableFromConfig)
}
//go:generate go run ../../cmd/tools/decorate.go -p meter -f decorateMeter -b api.Meter -o meter_decorators -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
}
for k, v := range map[string]string{"power": cc.Power.Type} {
if v == "" {
return nil, fmt.Errorf("default meter config: %s required", k)
}
}
power, err := provider.NewFloatGetterFromConfig(cc.Power)
if err != nil {
return nil, fmt.Errorf("power: %w", err)
}
m, _ := NewConfigurable(power)
// decorate Meter with MeterEnergy
if cc.Energy != nil {
m.totalEnergyG, err = provider.NewFloatGetterFromConfig(*cc.Energy)
if err != nil {
return nil, fmt.Errorf("energy: %w", err)
}
}
// decorate Meter with MeterCurrent
if len(cc.Currents) > 0 {
if len(cc.Currents) != 3 {
return nil, errors.New("need 3 currents")
}
for idx, cc := range cc.Currents {
c, err := provider.NewFloatGetterFromConfig(cc)
if err != nil {
return nil, fmt.Errorf("currents[%d]: %w", idx, err)
}
m.currentsG = append(m.currentsG, c)
}
}
// decorate Meter with BatterySoC
if cc.SoC != nil {
m.batterySoCG, err = provider.NewFloatGetterFromConfig(*cc.SoC)
if err != nil {
return nil, fmt.Errorf("battery: %w", err)
}
}
res := m.Decorate(m.totalEnergyG, m.currentsG, m.batterySoCG)
return res, 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)
totalEnergyG func() (float64, error)
currentsG []func() (float64, error)
batterySoCG func() (float64, error)
}
// Decorate attaches additional capabilities to the base meter
func (m *Meter) Decorate(
totalEnergyG func() (float64, error),
currentsG []func() (float64, error),
batterySoCG func() (float64, error),
) api.Meter {
var totalEnergy func() (float64, error)
if totalEnergyG != nil {
m.totalEnergyG = totalEnergyG
totalEnergy = m.totalEnergy
}
var currents func() (float64, float64, float64, error)
if currentsG != nil {
m.currentsG = currentsG
currents = m.currents
}
var batterySoC func() (float64, error)
if batterySoCG != nil {
m.batterySoCG = batterySoCG
batterySoC = m.batterySoC
}
return decorateMeter(m, totalEnergy, currents, batterySoC)
}
// CurrentPower implements the api.Meter interface
func (m *Meter) CurrentPower() (float64, error) {
return m.currentPowerG()
}
// totalEnergy implements the api.MeterEnergy interface
func (m *Meter) totalEnergy() (float64, error) {
return m.totalEnergyG()
}
// currents implements the api.MeterCurrent interface
func (m *Meter) currents() (float64, float64, float64, error) {
var currents []float64
for _, currentG := range m.currentsG {
c, err := currentG()
if err != nil {
return 0, 0, 0, err
}
currents = append(currents, c)
}
return currents[0], currents[1], currents[2], nil
}
// batterySoC implements the api.Battery interface
func (m *Meter) batterySoC() (float64, error) {
return m.batterySoCG()
}