Goodwe Wifi: fix timeout not handled (#12050)
This commit is contained in:
parent
0ec4afc3dc
commit
aefaeddecc
3 changed files with 86 additions and 59 deletions
|
|
@ -1,15 +1,17 @@
|
|||
package meter
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/evcc-io/evcc/api"
|
||||
"github.com/evcc-io/evcc/meter/goodwe"
|
||||
"github.com/evcc-io/evcc/util"
|
||||
"github.com/evcc-io/evcc/util/request"
|
||||
)
|
||||
|
||||
type goodWeWiFi struct {
|
||||
*goodwe.Server
|
||||
usage string
|
||||
uri string
|
||||
usage string
|
||||
inverter *util.Monitor[goodwe.Inverter]
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
|
@ -19,31 +21,36 @@ func init() {
|
|||
//go:generate go run ../cmd/tools/decorate.go -f decorateGoodWeWifi -b *goodWeWiFi -r api.Meter -t "api.Battery,Soc,func() (float64, error)"
|
||||
|
||||
func NewGoodWeWifiFromConfig(other map[string]interface{}) (api.Meter, error) {
|
||||
var cc struct {
|
||||
cc := struct {
|
||||
capacity `mapstructure:",squash"`
|
||||
URI, Usage string
|
||||
Timeout time.Duration
|
||||
}{
|
||||
Timeout: request.Timeout,
|
||||
}
|
||||
|
||||
if err := util.DecodeOther(other, &cc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewGoodWeWiFi(cc.URI, cc.Usage)
|
||||
return NewGoodWeWiFi(cc.URI, cc.Usage, cc.Timeout)
|
||||
}
|
||||
|
||||
func NewGoodWeWiFi(uri string, usage string) (api.Meter, error) {
|
||||
instance, err := goodwe.Instance()
|
||||
func NewGoodWeWiFi(uri, usage string, timeout time.Duration) (api.Meter, error) {
|
||||
instance, err := goodwe.Instance(util.NewLogger("goodwe-wifi"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := &goodWeWiFi{
|
||||
Server: instance,
|
||||
usage: usage,
|
||||
uri: uri,
|
||||
inverter := instance.GetInverter(uri)
|
||||
if inverter == nil {
|
||||
inverter = instance.AddInverter(uri, timeout)
|
||||
}
|
||||
|
||||
instance.AddInverter(uri)
|
||||
res := &goodWeWiFi{
|
||||
usage: usage,
|
||||
inverter: inverter,
|
||||
}
|
||||
|
||||
// decorate api.BatterySoc
|
||||
var batterySoc func() (float64, error)
|
||||
|
|
@ -55,7 +62,10 @@ func NewGoodWeWiFi(uri string, usage string) (api.Meter, error) {
|
|||
}
|
||||
|
||||
func (m *goodWeWiFi) CurrentPower() (float64, error) {
|
||||
data := m.GetInverter(m.uri)
|
||||
data, err := m.inverter.Get()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
switch m.usage {
|
||||
case "grid":
|
||||
|
|
@ -69,5 +79,6 @@ func (m *goodWeWiFi) CurrentPower() (float64, error) {
|
|||
}
|
||||
|
||||
func (m *goodWeWiFi) batterySoc() (float64, error) {
|
||||
return m.GetInverter(m.uri).Soc, nil
|
||||
data, err := m.inverter.Get()
|
||||
return data.Soc, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ import (
|
|||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/evcc-io/evcc/util"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -12,7 +14,7 @@ var (
|
|||
mu sync.RWMutex
|
||||
)
|
||||
|
||||
func Instance() (*Server, error) {
|
||||
func Instance(log *util.Logger) (*Server, error) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
|
|
@ -21,7 +23,8 @@ func Instance() (*Server, error) {
|
|||
}
|
||||
|
||||
instance = &Server{
|
||||
inverters: make(map[string]Inverter),
|
||||
log: log,
|
||||
inverters: make(map[string]*util.Monitor[Inverter]),
|
||||
}
|
||||
|
||||
addr, err := net.ResolveUDPAddr("udp", "0.0.0.0:8899")
|
||||
|
|
@ -40,70 +43,81 @@ func Instance() (*Server, error) {
|
|||
return instance, err
|
||||
}
|
||||
|
||||
func (m *Server) AddInverter(ip string) {
|
||||
func (m *Server) AddInverter(ip string, timeout time.Duration) *util.Monitor[Inverter] {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
m.inverters[ip] = Inverter{IP: ip}
|
||||
monitor := util.NewMonitor[Inverter](timeout)
|
||||
m.inverters[ip] = monitor
|
||||
return monitor
|
||||
}
|
||||
|
||||
func (m *Server) GetInverter(uri string) Inverter {
|
||||
func (m *Server) GetInverter(ip string) *util.Monitor[Inverter] {
|
||||
mu.RLock()
|
||||
defer mu.RUnlock()
|
||||
return m.inverters[uri]
|
||||
return m.inverters[ip]
|
||||
}
|
||||
|
||||
func (m *Server) readData() {
|
||||
for _, inverter := range m.inverters {
|
||||
addr, err := net.ResolveUDPAddr("udp", inverter.IP+":8899")
|
||||
if err != nil {
|
||||
return
|
||||
for {
|
||||
mu.RLock()
|
||||
ips := make([]string, 0, len(m.inverters))
|
||||
for ip := range m.inverters {
|
||||
ips = append(ips, ip)
|
||||
}
|
||||
mu.RUnlock()
|
||||
|
||||
if _, err := m.conn.WriteToUDP([]byte{0xF7, 0x03, 0x89, 0x1C, 0x00, 0x7D, 0x7A, 0xE7}, addr); err != nil {
|
||||
return
|
||||
}
|
||||
time.Sleep(5 * time.Second)
|
||||
if _, err := m.conn.WriteToUDP([]byte{0xF7, 0x03, 0x90, 0x88, 0x00, 0x0D, 0x3D, 0xB3}, addr); err != nil {
|
||||
return
|
||||
for _, ip := range ips {
|
||||
addr, err := net.ResolveUDPAddr("udp", net.JoinHostPort(ip, "8899"))
|
||||
if err != nil {
|
||||
m.log.ERROR.Println(err)
|
||||
continue
|
||||
}
|
||||
if _, err := m.conn.WriteToUDP([]byte{0xF7, 0x03, 0x89, 0x1C, 0x00, 0x7D, 0x7A, 0xE7}, addr); err != nil {
|
||||
m.log.ERROR.Println(err)
|
||||
continue
|
||||
}
|
||||
time.Sleep(5 * time.Second)
|
||||
if _, err := m.conn.WriteToUDP([]byte{0xF7, 0x03, 0x90, 0x88, 0x00, 0x0D, 0x3D, 0xB3}, addr); err != nil {
|
||||
m.log.ERROR.Println(err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
m.readData()
|
||||
}
|
||||
|
||||
func (m *Server) listen() {
|
||||
for {
|
||||
buf := make([]byte, 1024)
|
||||
_, addr, err := m.conn.ReadFromUDP(buf)
|
||||
n, addr, err := m.conn.ReadFromUDP(buf)
|
||||
if err != nil {
|
||||
m.log.ERROR.Println(err)
|
||||
continue
|
||||
}
|
||||
m.log.TRACE.Printf("recv from %s: % X", addr, buf[:n])
|
||||
|
||||
ip := addr.IP.String()
|
||||
monitor := m.GetInverter(ip)
|
||||
if monitor == nil {
|
||||
m.log.ERROR.Println("unknown inverter:", ip)
|
||||
continue
|
||||
}
|
||||
|
||||
ip := addr.IP.String()
|
||||
monitor.SetFunc(func(inverter Inverter) Inverter {
|
||||
if buf[4] == 250 {
|
||||
ui := func(u, i int16) float64 {
|
||||
return float64(int16(binary.BigEndian.Uint16(buf[u:]))) *
|
||||
float64(int16(binary.BigEndian.Uint16(buf[i:]))) / 100
|
||||
}
|
||||
inverter.PvPower = ui(11, 13) + ui(19, 21)
|
||||
inverter.BatteryPower = ui(165, 167)
|
||||
inverter.NetPower = -float64(int32(binary.BigEndian.Uint32(buf[83:])))
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
if buf[4] == 250 {
|
||||
vPv1 := float64(int16(binary.BigEndian.Uint16(buf[11:]))) * 0.1
|
||||
vPv2 := float64(int16(binary.BigEndian.Uint16(buf[19:]))) * 0.1
|
||||
iPv1 := float64(int16(binary.BigEndian.Uint16(buf[13:]))) * 0.1
|
||||
iPv2 := float64(int16(binary.BigEndian.Uint16(buf[21:]))) * 0.1
|
||||
iBatt := float64(int16(binary.BigEndian.Uint16(buf[167:]))) * 0.1
|
||||
vBatt := float64(int16(binary.BigEndian.Uint16(buf[165:]))) * 0.1
|
||||
if buf[4] == 26 {
|
||||
inverter.Soc = float64(buf[20])
|
||||
}
|
||||
|
||||
pvPower := vPv1*iPv1 + vPv2*iPv2
|
||||
|
||||
inverter := m.inverters[ip]
|
||||
inverter.PvPower = pvPower
|
||||
inverter.BatteryPower = vBatt * iBatt
|
||||
inverter.NetPower = -float64(int32(binary.BigEndian.Uint32(buf[83:])))
|
||||
|
||||
m.inverters[ip] = inverter
|
||||
}
|
||||
|
||||
if buf[4] == 26 {
|
||||
inverter := m.inverters[ip]
|
||||
inverter.Soc = float64(buf[20])
|
||||
m.inverters[ip] = inverter
|
||||
}
|
||||
mu.Unlock()
|
||||
return inverter
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,15 +2,17 @@ package goodwe
|
|||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/evcc-io/evcc/util"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
log *util.Logger
|
||||
conn *net.UDPConn
|
||||
inverters map[string]Inverter
|
||||
inverters map[string]*util.Monitor[Inverter]
|
||||
}
|
||||
|
||||
type Inverter struct {
|
||||
IP string
|
||||
PvPower float64
|
||||
NetPower float64
|
||||
BatteryPower float64
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue