evcc-io/plugin/aa55udp.go
2026-06-04 11:41:03 +02:00

78 lines
2.1 KiB
Go

package plugin
import (
"context"
"fmt"
"net"
"net/netip"
"github.com/evcc-io/evcc/plugin/aa55"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/request"
)
func init() {
registry.AddCtx("aa55udp", NewAA55UDPFromConfig)
}
// NewAA55UDPFromConfig creates a GoodWe AA55-over-UDP plugin.
//
// Register read mode (single register):
//
// source: aa55udp
// host: 192.168.1.26
// id: 127 # 0x7F for DT/DNS/ES/EM (default); 247 (0xF7) for ET/EH/BT/BH
// register: 30127
// count: 2 # 1 = 16-bit, 2 = 32-bit
// decode: int32be
// scale: 1.0
//
// Block read mode (fetch an enclosing block once, extract the target register):
//
// source: aa55udp
// host: 192.168.1.26 # inverter IP; port 8899 is always used
// id: 247 # 0x7F (default) for DT/DNS/ES/EM; 247 (0xF7) for ET/EH/BT/BH
// register: 35139 # target register
// count: 2 # 1 = 16-bit, 2 = 32-bit
// block: # enclosing block fetched in a single UDP exchange
// register: 35100 # block start register
// count: 125 # block length (registers)
// decode: int32be # int32be | uint32be | uint32nan | int16be | uint16be | float32be
// scale: 1.0 # optional multiplier (default 1.0)
func NewAA55UDPFromConfig(ctx context.Context, other map[string]any) (Plugin, error) {
cc := struct {
Host string
Id int
Register uint16
Count uint16
Block *aa55.Block
Decode string
Scale float64
}{
Id: int(aa55.InverterAddr),
Count: 2,
Scale: 1.0,
}
if err := util.DecodeOther(other, &cc); err != nil {
return nil, err
}
raddr, err := net.ResolveUDPAddr("udp4", net.JoinHostPort(cc.Host, "8899"))
if err != nil {
return nil, err
}
dialer := &net.Dialer{Timeout: request.Timeout}
conn, err := dialer.DialUDP(ctx, "udp4", netip.AddrPort{}, raddr.AddrPort())
if err != nil {
return nil, err
}
res, err := aa55.New(util.NewLogger("aa55udp"), conn, cc.Id, cc.Register, cc.Count, cc.Block, cc.Decode, cc.Scale)
if err != nil {
return nil, fmt.Errorf("aa55udp: %w", err)
}
return res, nil
}