From 7cd94aa18716ac39b395dc7ef68b7100ae04f0e9 Mon Sep 17 00:00:00 2001 From: Markus Breitenberger Date: Sat, 26 Apr 2025 14:13:29 +0200 Subject: [PATCH] KEBA: Add KeContact P40 models (#20860) --- charger/keba-modbus.go | 56 ++++++++++++++----- templates/definition/charger/keba-modbus.yaml | 3 + 2 files changed, 45 insertions(+), 14 deletions(-) diff --git a/charger/keba-modbus.go b/charger/keba-modbus.go index 6ad7d20cc..5b28b95b4 100644 --- a/charger/keba-modbus.go +++ b/charger/keba-modbus.go @@ -34,12 +34,15 @@ import ( // https://www.keba.com/en/emobility/service-support/downloads/Downloads // https://www.keba.com/download/x/dea7ae6b84/kecontactp30modbustcp_pgen.pdf +// https://www.keba.com/download/x/4a24e19f80/kecontactp40modbustcp_pgen.pdf // Keba is an api.Charger implementation type Keba struct { *embed - log *util.Logger - conn *modbus.Connection + log *util.Logger + conn *modbus.Connection + current uint16 + regEnable uint16 } const ( @@ -88,12 +91,14 @@ func NewKebaFromConfig(ctx context.Context, other map[string]interface{}) (api.C return nil, err } - // features + // optional features var ( currentPower, totalEnergy func() (float64, error) currents func() (float64, float64, float64, error) identify func() (string, error) reason func() (api.Reason, error) + phasesS func(int) error + phasesG func() (int, error) ) b, err := wb.conn.ReadHoldingRegisters(kebaRegProduct, 2) @@ -101,22 +106,34 @@ func NewKebaFromConfig(ctx context.Context, other map[string]interface{}) (api.C return nil, err } - if features := binary.BigEndian.Uint32(b); (features/10)%10 > 0 { + productCodeStr := fmt.Sprintf("%d", binary.BigEndian.Uint32(b)) + + var hasEnergyMeter bool + var hasRFID bool + + if len(productCodeStr) == 6 && productCodeStr[0] == '3' { + // P30 + hasEnergyMeter = productCodeStr[4] != '0' + hasRFID = productCodeStr[5] == '1' + } else if len(productCodeStr) == 7 && productCodeStr[0] == '4' { + // P40 + wb.regEnable = kebaRegMaxCurrent + hasEnergyMeter = productCodeStr[4] != '0' + hasRFID = productCodeStr[5] == '1' + } + + if hasEnergyMeter { currentPower = wb.currentPower totalEnergy = wb.totalEnergy currents = wb.currents } - if features := binary.BigEndian.Uint32(b); features%10 > 0 { + if hasRFID { identify = wb.identify reason = wb.statusReason } // phases - var ( - phasesS func(int) error - phasesG func() (int, error) - ) if b, err := wb.conn.ReadHoldingRegisters(kebaRegPhaseSource, 2); err == nil { if source := binary.BigEndian.Uint32(b); source == 3 { phasesS = wb.phases1p3p @@ -152,9 +169,10 @@ func NewKeba(ctx context.Context, embed embed, uri string, slaveID uint8) (*Keba conn.Logger(log.TRACE) wb := &Keba{ - embed: &embed, - log: log, - conn: conn, + embed: &embed, + log: log, + conn: conn, + regEnable: kebaRegEnable, } return wb, err @@ -248,9 +266,14 @@ func (wb *Keba) Enabled() (bool, error) { func (wb *Keba) Enable(enable bool) error { var u uint16 if enable { - u = 1 + if wb.regEnable == kebaRegMaxCurrent { + u = wb.current + } else { + u = 1 + } } - _, err := wb.conn.WriteSingleRegister(kebaRegEnable, u) + + _, err := wb.conn.WriteSingleRegister(wb.regEnable, u) return err } @@ -265,6 +288,11 @@ var _ api.ChargerEx = (*Keba)(nil) func (wb *Keba) MaxCurrentMillis(current float64) error { u := uint16(current * 1000) _, err := wb.conn.WriteSingleRegister(kebaRegMaxCurrent, u) + + if err == nil { + wb.current = u + } + return err } diff --git a/templates/definition/charger/keba-modbus.yaml b/templates/definition/charger/keba-modbus.yaml index dbaf2fddc..5941c4f54 100644 --- a/templates/definition/charger/keba-modbus.yaml +++ b/templates/definition/charger/keba-modbus.yaml @@ -3,6 +3,9 @@ products: - brand: KEBA description: generic: KeContact P20, P30, C/X Series + - brand: KEBA + description: + generic: KeContact P40, P40 Pro - brand: BMW description: generic: i Wallbox