From dda8b56029ec3652e59a597c4d18dd65342c62ee Mon Sep 17 00:00:00 2001 From: "Jannis M." <58087069+jannismunz@users.noreply.github.com> Date: Sat, 6 Sep 2025 00:11:09 +0200 Subject: [PATCH] Add support for my-PV AC ELWA-E (#23358) --- charger/mypv.go | 43 ++++++++++++++------- templates/definition/charger/ac-elwa-e.yaml | 24 ++++++++++++ 2 files changed, 53 insertions(+), 14 deletions(-) create mode 100644 templates/definition/charger/ac-elwa-e.yaml diff --git a/charger/mypv.go b/charger/mypv.go index 977662ae1..3a5999c8b 100644 --- a/charger/mypv.go +++ b/charger/mypv.go @@ -21,6 +21,7 @@ import ( "context" "encoding/binary" "fmt" + "slices" "sync/atomic" "time" @@ -45,12 +46,13 @@ type MyPv struct { } const ( - elwaRegSetPower = 1000 - elwaRegTempLimit = 1002 - elwaRegStatus = 1003 - elwaRegLoadState = 1059 - elwaRegPower = 1000 // https://github.com/evcc-io/evcc/issues/18020#issuecomment-2585300804 - elwaRegOperationState = 1077 + elwaRegSetPower = 1000 + elwaRegTempLimit = 1002 + elwaRegStatus = 1003 + elwaRegLoadState = 1059 + elwaRegPower = 1000 // https://github.com/evcc-io/evcc/issues/18020#issuecomment-2585300804 + elwaRegOperationState = 1077 + elwaERegOperationState = elwaRegStatus // same register for elwa-e operation state ) var elwaTemp = []uint16{1001, 1030, 1031} @@ -66,6 +68,10 @@ func init() { registry.AddCtx("ac-thor", func(ctx context.Context, other map[string]interface{}) (api.Charger, error) { return newMyPvFromConfig(ctx, "ac-thor", other, 9) }) + + registry.AddCtx("ac-elwa-e", func(ctx context.Context, other map[string]interface{}) (api.Charger, error) { + return newMyPvFromConfig(ctx, "ac-elwa-e", other, 2) + }) } // newMyPvFromConfig creates a MyPv charger from generic config @@ -194,20 +200,29 @@ func (wb *MyPv) Status() (api.ChargeStatus, error) { // Enabled implements the api.Charger interface func (wb *MyPv) Enabled() (bool, error) { - b, err := wb.conn.ReadHoldingRegisters(elwaRegOperationState, 1) + // "ac-thor" and "ac-elwa-2" + reg := elwaRegOperationState + enabled := []uint16{1, 2} // heating PV excess, boost backup + + if wb.name == "ac-elwa-e" { + reg = elwaERegOperationState + enabled = []uint16{2, 4} // heating PV excess, boost backup + } + + // register read + b, err := wb.conn.ReadHoldingRegisters(uint16(reg), 1) if err != nil { return false, err } + state := binary.BigEndian.Uint16(b) - switch binary.BigEndian.Uint16(b) { - case - 1, // heating PV excess - 2: // boost backup - return true, nil - case - 0: // standby + // determine enabled state + if state == 0 { // standby return false, nil } + if slices.Contains(enabled, state) { + return true, nil + } // fallback to cached value as last resort return wb.enabled, nil diff --git a/templates/definition/charger/ac-elwa-e.yaml b/templates/definition/charger/ac-elwa-e.yaml new file mode 100644 index 000000000..0d6c834cc --- /dev/null +++ b/templates/definition/charger/ac-elwa-e.yaml @@ -0,0 +1,24 @@ +template: ac-elwa-e +products: + - brand: my-PV + description: + generic: AC ELWA-E +group: heating +requirements: + description: + de: Für den Heizstab von my-PV ohne Display. Über das lokale Webinterface des AC ELWA-E muss der Wert für die "Zeitablauf Ansteuerung" auf einen Wert etwas größer der Intervall Zeit von evcc (z.B. + 5s) gesetzt werden. + en: For the heating rod from my-PV without a display. Use the local web interface of AC ELWA-E to set the value "Power timeout" to a value slightly higher (e.g. + 5s) than the interval time of evcc. + evcc: ["sponsorship"] +params: + - name: modbus + choice: ["tcpip"] + - name: scale + type: float + default: 1 + description: + en: "Scale factor for power limit" + de: "Skalierungsfaktor der Leistungsvorgabe" +render: | + type: ac-elwa-e + {{- include "modbus" . }} + scale: {{ .scale }}