Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add myPV AC Thor #18034

Merged
merged 2 commits into from
Jan 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 45 additions & 36 deletions charger/mypv-elwa2.go → charger/mypv.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,12 @@ import (
"github.com/evcc-io/evcc/util/sponsor"
)

// MyPvElwa2 charger implementation
type MyPvElwa2 struct {
log *util.Logger
conn *modbus.Connection
power uint32
// MyPv charger implementation
type MyPv struct {
log *util.Logger
conn *modbus.Connection
power uint32
regPower uint16
}

const (
Expand All @@ -42,16 +43,23 @@ const (
elwaRegTempLimit = 1002
elwaRegStatus = 1003
elwaRegPower = 1074
thorRegPower = 1060
)

func init() {
registry.AddCtx("ac-elwa-2", NewMyPvElwa2FromConfig)
}
// https://github.com/evcc-io/evcc/discussions/12761
registry.AddCtx("ac-elwa-2", func(ctx context.Context, other map[string]interface{}) (api.Charger, error) {
return newMyPvFromConfig(ctx, "ac-elwa-2", other, elwaRegPower)
})

// https://github.com/evcc-io/evcc/discussions/12761
// https: // github.com/evcc-io/evcc/issues/18020
registry.AddCtx("ac-thor", func(ctx context.Context, other map[string]interface{}) (api.Charger, error) {
return newMyPvFromConfig(ctx, "ac-thor", other, thorRegPower)
})
}

// NewMyPvElwa2FromConfig creates a MyPvElwa2 charger from generic config
func NewMyPvElwa2FromConfig(ctx context.Context, other map[string]interface{}) (api.Charger, error) {
// newMyPvFromConfig creates a MyPv charger from generic config
func newMyPvFromConfig(ctx context.Context, name string, other map[string]interface{}, regPower uint16) (api.Charger, error) {
cc := modbus.TcpSettings{
ID: 1,
}
Expand All @@ -60,11 +68,11 @@ func NewMyPvElwa2FromConfig(ctx context.Context, other map[string]interface{}) (
return nil, err
}

return NewMyPvElwa2(ctx, cc.URI, cc.ID)
return NewMyPv(ctx, name, cc.URI, cc.ID, regPower)
}

// NewMyPvElwa2 creates myPV AC Elwa 2 charger
func NewMyPvElwa2(ctx context.Context, uri string, slaveID uint8) (api.Charger, error) {
// NewMyPv creates myPV AC Elwa 2 or Thor charger
func NewMyPv(ctx context.Context, name, uri string, slaveID uint8, regPower uint16) (api.Charger, error) {
conn, err := modbus.NewConnection(uri, "", "", 0, modbus.Tcp, slaveID)
if err != nil {
return nil, err
Expand All @@ -74,34 +82,35 @@ func NewMyPvElwa2(ctx context.Context, uri string, slaveID uint8) (api.Charger,
return nil, api.ErrSponsorRequired
}

log := util.NewLogger("ac-elwa-2")
log := util.NewLogger(name)
conn.Logger(log.TRACE)

wb := &MyPvElwa2{
log: log,
conn: conn,
wb := &MyPv{
log: log,
conn: conn,
regPower: regPower,
}

go wb.heartbeat(ctx, 30*time.Second)

return wb, nil
}

var _ api.IconDescriber = (*MyPvElwa2)(nil)
var _ api.IconDescriber = (*MyPv)(nil)

// Icon implements the api.IconDescriber interface
func (v *MyPvElwa2) Icon() string {
func (v *MyPv) Icon() string {
return "waterheater"
}

var _ api.FeatureDescriber = (*MyPvElwa2)(nil)
var _ api.FeatureDescriber = (*MyPv)(nil)

// Features implements the api.FeatureDescriber interface
func (wb *MyPvElwa2) Features() []api.Feature {
func (wb *MyPv) Features() []api.Feature {
return []api.Feature{api.IntegratedDevice, api.Heating}
}

func (wb *MyPvElwa2) heartbeat(ctx context.Context, timeout time.Duration) {
func (wb *MyPv) heartbeat(ctx context.Context, timeout time.Duration) {
for tick := time.Tick(timeout); ; {
select {
case <-tick:
Expand All @@ -122,7 +131,7 @@ func (wb *MyPvElwa2) heartbeat(ctx context.Context, timeout time.Duration) {
}

// Status implements the api.Charger interface
func (wb *MyPvElwa2) Status() (api.ChargeStatus, error) {
func (wb *MyPv) Status() (api.ChargeStatus, error) {
res := api.StatusA
b, err := wb.conn.ReadHoldingRegisters(elwaRegStatus, 1)
if err != nil {
Expand All @@ -138,7 +147,7 @@ func (wb *MyPvElwa2) Status() (api.ChargeStatus, error) {
}

// Enabled implements the api.Charger interface
func (wb *MyPvElwa2) Enabled() (bool, error) {
func (wb *MyPv) Enabled() (bool, error) {
b, err := wb.conn.ReadHoldingRegisters(elwaRegSetPower, 1)
if err != nil {
return false, err
Expand All @@ -147,7 +156,7 @@ func (wb *MyPvElwa2) Enabled() (bool, error) {
return binary.BigEndian.Uint16(b) > 0, nil
}

func (wb *MyPvElwa2) setPower(power uint16) error {
func (wb *MyPv) setPower(power uint16) error {
b := make([]byte, 2)
binary.BigEndian.PutUint16(b, power)

Expand All @@ -156,7 +165,7 @@ func (wb *MyPvElwa2) setPower(power uint16) error {
}

// Enable implements the api.Charger interface
func (wb *MyPvElwa2) Enable(enable bool) error {
func (wb *MyPv) Enable(enable bool) error {
var power uint16
if enable {
power = uint16(atomic.LoadUint32(&wb.power))
Expand All @@ -166,14 +175,14 @@ func (wb *MyPvElwa2) Enable(enable bool) error {
}

// MaxCurrent implements the api.Charger interface
func (wb *MyPvElwa2) MaxCurrent(current int64) error {
func (wb *MyPv) MaxCurrent(current int64) error {
return wb.MaxCurrentMillis(float64(current))
}

var _ api.ChargerEx = (*MyPvElwa2)(nil)
var _ api.ChargerEx = (*MyPv)(nil)

// MaxCurrentMillis implements the api.ChargerEx interface
func (wb *MyPvElwa2) MaxCurrentMillis(current float64) error {
func (wb *MyPv) MaxCurrentMillis(current float64) error {
power := uint16(230 * current)

err := wb.setPower(power)
Expand All @@ -184,22 +193,22 @@ func (wb *MyPvElwa2) MaxCurrentMillis(current float64) error {
return err
}

var _ api.Meter = (*MyPvElwa2)(nil)
var _ api.Meter = (*MyPv)(nil)

// CurrentPower implements the api.Meter interface
func (wb *MyPvElwa2) CurrentPower() (float64, error) {
b, err := wb.conn.ReadHoldingRegisters(elwaRegPower, 1)
func (wb *MyPv) CurrentPower() (float64, error) {
b, err := wb.conn.ReadHoldingRegisters(wb.regPower, 1)
if err != nil {
return 0, err
}

return float64(binary.BigEndian.Uint16(b)), nil
}

var _ api.Battery = (*MyPvElwa2)(nil)
var _ api.Battery = (*MyPv)(nil)

// CurrentPower implements the api.Meter interface
func (wb *MyPvElwa2) Soc() (float64, error) {
func (wb *MyPv) Soc() (float64, error) {
b, err := wb.conn.ReadHoldingRegisters(elwaRegTemp, 1)
if err != nil {
return 0, err
Expand All @@ -208,10 +217,10 @@ func (wb *MyPvElwa2) Soc() (float64, error) {
return float64(binary.BigEndian.Uint16(b)) / 10, nil
}

var _ api.SocLimiter = (*MyPvElwa2)(nil)
var _ api.SocLimiter = (*MyPv)(nil)

// GetLimitSoc implements the api.SocLimiter interface
func (wb *MyPvElwa2) GetLimitSoc() (int64, error) {
func (wb *MyPv) GetLimitSoc() (int64, error) {
b, err := wb.conn.ReadHoldingRegisters(elwaRegTempLimit, 1)
if err != nil {
return 0, err
Expand Down
14 changes: 14 additions & 0 deletions templates/definition/charger/ac-thor.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
template: ac-thor
products:
- brand: my-PV
description:
generic: AC THOR, AC THOR 9s
group: heating
requirements:
evcc: ["sponsorship"]
params:
- name: modbus
choice: ["tcpip"]
render: |
type: ac-thor
{{- include "modbus" . }}
Loading