forked from evcc-io/evcc
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlgess.go
More file actions
168 lines (144 loc) Β· 4.67 KB
/
Copy pathlgess.go
File metadata and controls
168 lines (144 loc) Β· 4.67 KB
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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
package meter
import (
"errors"
"fmt"
"strings"
"time"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/meter/lgpcs"
"github.com/evcc-io/evcc/util"
)
// LgEss implements the api.Meter interface
type LgEss struct {
usage string // grid, pv, battery
conn *lgpcs.Com // communication with the lgpcs device
}
func init() {
registry.Add("lgess8", NewLgEss8FromConfig)
registry.Add("lgess15", NewLgEss15FromConfig)
}
//go:generate go tool decorate -f decorateLgEss -b *LgEss -r api.Meter -t "api.MeterEnergy,TotalEnergy,func() (float64, error)" -t "api.BatteryCapacity,Capacity,func() float64" -t "api.Battery,Soc,func() (float64, error)" -t "api.BatterySocLimiter,GetSocLimits,func() (float64, float64)" -t "api.BatteryController,SetBatteryMode,func(api.BatteryMode) error"
func NewLgEss8FromConfig(other map[string]any) (api.Meter, error) {
return NewLgEssFromConfig(other, lgpcs.LgEss8)
}
func NewLgEss15FromConfig(other map[string]any) (api.Meter, error) {
return NewLgEssFromConfig(other, lgpcs.LgEss15)
}
// NewLgEssFromConfig creates an LgEss Meter from generic config
func NewLgEssFromConfig(other map[string]any, essType lgpcs.Model) (api.Meter, error) {
cc := struct {
batteryCapacity `mapstructure:",squash"`
batterySocLimits `mapstructure:",squash"`
URI, Usage string
Registration, Password string
Cache time.Duration
}{
batterySocLimits: batterySocLimits{
MinSoc: 20,
MaxSoc: 95,
},
Cache: time.Second,
}
if err := util.DecodeOther(other, &cc); err != nil {
return nil, err
}
if cc.Usage == "" {
return nil, errors.New("missing usage")
}
return NewLgEss(cc.URI, cc.Usage, cc.Registration, cc.Password, cc.Cache, cc.batteryCapacity.Decorator(), cc.batterySocLimits, essType)
}
// NewLgEss creates an LgEss Meter
func NewLgEss(uri, usage, registration, password string, cache time.Duration, capacity func() float64, batterySocLimits batterySocLimits, essType lgpcs.Model) (api.Meter, error) {
conn, err := lgpcs.GetInstance(uri, registration, password, cache, essType)
if err != nil {
return nil, err
}
m := &LgEss{
usage: strings.ToLower(usage),
conn: conn,
}
// decorate api.MeterEnergy
var totalEnergy func() (float64, error)
if m.usage == "grid" && essType != lgpcs.LgEss15 {
totalEnergy = m.totalEnergy
}
// decorate battery
var batterySoc func() (float64, error)
var batterySocLimiter func() (float64, float64)
var setBatteryMode func(api.BatteryMode) error
if usage == "battery" {
batterySoc = m.batterySoc
batterySocLimiter = batterySocLimits.Decorator()
if version, err := conn.GetFirmwareVersion(); err == nil && version >= 7430 {
setBatteryMode = m.batteryMode(batterySocLimits)
}
}
return decorateLgEss(m, totalEnergy, capacity, batterySoc, batterySocLimiter, setBatteryMode), nil
}
// CurrentPower implements the api.Meter interface
func (m *LgEss) CurrentPower() (float64, error) {
data, err := m.conn.Data()
if err != nil {
return 0, err
}
switch m.usage {
case "grid":
return data.GetGridPower(), nil
case "pv":
return data.GetPvTotalPower(), nil
case "battery":
return data.GetBatConvPower(), nil
default:
return 0, fmt.Errorf("invalid usage: %s", m.usage)
}
}
// totalEnergy implements the api.MeterEnergy interface
func (m *LgEss) totalEnergy() (float64, error) {
data, err := m.conn.Data()
if err != nil {
return 0, err
}
switch m.usage {
case "grid":
return data.GetCurrentGridFeedInEnergy() / 1e3, nil
default:
return 0, fmt.Errorf("invalid usage: %s", m.usage)
}
}
// batterySoc implements the api.Battery interface
func (m *LgEss) batterySoc() (float64, error) {
data, err := m.conn.Data()
if err != nil {
return 0, err
}
return data.GetBatUserSoc(), nil
}
// batteryMode implements the api.BatteryController interface
func (m *LgEss) batteryMode(batterySocLimits batterySocLimits) func(api.BatteryMode) error {
return func(mode api.BatteryMode) error {
switch mode {
case api.BatteryNormal:
// firmeware bug: battery not discharging after hold mode
// if battery is sleeping, wake up with charging for 10sec
m.conn.BatteryMode("on", 100, true)
time.Sleep(10 * time.Second)
// now turn Battery discharge on
return m.conn.BatteryMode("on", int(batterySocLimits.MinSoc), true)
case api.BatteryHold:
soc, err := m.batterySoc()
if err != nil {
return err
}
// soc needs to be the next higher int value to stop discharging immediately
// example: batterySoc=50.7 -> set 51
if int(soc)+1 < 100 {
soc++
}
return m.conn.BatteryMode("on", int(soc), true)
case api.BatteryCharge:
return m.conn.BatteryMode("on", int(batterySocLimits.MaxSoc), true)
default:
return api.ErrNotAvailable
}
}
}