8000 Add Kathrein charger by TheMagnetar · Pull Request #20412 · evcc-io/evcc · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Add Kathrein charger #20412

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

Merged
merged 13 commits into from
Apr 30, 2025
Merged

Add Kathrein charger #20412

merged 13 commits into from
Apr 30, 2025

Conversation

TheMagnetar
Copy link
Contributor
@TheMagnetar TheMagnetar commented Apr 5, 2025

Since this is my first contribution to this project and it is not yet tested, I have marked this PR it as draft to gather feedback.

Summary by Sourcery

Add support for Kathrein electric vehicle chargers with Modbus TCP integration

New Features:

  • Implement Kathrein charger support with comprehensive Modbus communication
  • Add support for 1-phase and 3-phase charging
  • Implement detailed charger diagnostics and energy monitoring

Documentation:

  • Add product compatibility notes for KWB-AC20, KWB-AC40, KWB-AC60, KWB-AC40E, KWB-AC60E models

Chores:

  • Add Kathrein charger template configuration
  • Implement custom Modbus register mapping for Kathrein chargers

@andig andig added the devices Specific device support label Apr 5, 2025
@Dodi1968
Copy link
Contributor
Dodi1968 commented Apr 7, 2025

Mir ist aufgefallen, dass sich die Funktionen „NewKathreinFromConfig“ und „NewKathrein“ auf das Modbus-RTU-Protokoll beziehen. Die Kathrein-Wallbox lässt sich jedoch nur über das Modbus-TCP-Protokoll ansprechen (ähnlich wie z.B. keba-modbus.go). Siehe auch Absatz „Communication Interface“ im Modbus-Register-Dokument von Kathrein. Und als Unit-ID sollte 0x00 gesetzt werden.

@Dodi1968
Copy link
Contributor
Dodi1968 commented Apr 7, 2025

Folgende Punkte sind mir noch aufgefallen:

Phasen bei Definition der Variablen Currents und Powers nicht korrekt nummeriert (L1 / L2 / L3):
186: kathreinRegCurrents = []uint16{kathreinRegL1Current, kathreinRegL1Current, kathreinRegL1Current} // uint16 A
187: kathreinRegPowers = []uint16{kathreinRegL1Power, kathreinRegL1Power, kathreinRegL2Power} // uint16 W

Beim Setzen von MaxCurrentMillis liefert "current" einen Float64-Wert in A, der noch in mA umgerechnet werden muss, also:
309: curr := uint16(current * 1e3)

TotalEnergy:
385: return float64(binary.BigEndian.Uint32(b)) / 1e3, err
Teilen durch 1e3 ist nicht korrekt, da vom Register schon ein Float32-Wert in kWh (nicht Wh) geliefert wird.

Außerdem bin ich mir nicht sicher, ob die Umrechnung des Registerwerts im Format float32 nach float64 so korrekt ist (kommt an mehreren Stellen vor):
float64(binary.BigEndian.Uint32(b))
wobei "b" der Registerwert ist.
Ich habe keine andere Wallbox gefunden, die float-Werte liefert.
keba-modbus.go verwendet die gleiche Umrechnungsformel für das Registerformat uint32.

@premultiply premultiply requested a review from Copilot April 8, 2025 06:55
Copy link
@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

Comments suppressed due to low confidence (1)

api/chargemodestatus.go:57

  • The variable 's1' is used but its origin is unclear; if this is unintended, consider using the appropriate variable (possibly 's') or ensuring 's1' is properly defined.
return ChargeStatus(s1), nil

@TheMagnetar
Copy link
Contributor Author

Folgende Punkte sind mir noch aufgefallen:

Phasen bei Definition der Variablen Currents und Powers nicht korrekt nummeriert (L1 / L2 / L3): 186: kathreinRegCurrents = []uint16{kathreinRegL1Current, kathreinRegL1Current, kathreinRegL1Current} // uint16 A 187: kathreinRegPowers = []uint16{kathreinRegL1Power, kathreinRegL1Power, kathreinRegL2Power} // uint16 W

Beim Setzen von MaxCurrentMillis liefert "current" einen Float64-Wert in A, der noch in mA umgerechnet werden muss, also: 309: curr := uint16(current * 1e3)

TotalEnergy: 385: return float64(binary.BigEndian.Uint32(b)) / 1e3, err Teilen durch 1e3 ist nicht korrekt, da vom Register schon ein Float32-Wert in kWh (nicht Wh) geliefert wird.

Außerdem bin ich mir nicht sicher, ob die Umrechnung des Registerwerts im Format float32 nach float64 so korrekt ist (kommt an mehreren Stellen vor): float64(binary.BigEndian.Uint32(b)) wobei "b" der Registerwert ist. Ich habe keine andere Wallbox gefunden, die float-Werte liefert. keba-modbus.go verwendet die gleiche Umrechnungsformel für das Registerformat uint32.

Everything must still be tested, I have not come to it yet // Alles muss hier getestet werden. Leider ist bei mir Zeit im Moment sehr begrenzt

@Dodi1968
Copy link
Contributor
Dodi1968 commented Apr 9, 2025

Die Umrechnung eines 32-Bit-Registerwertes (float32) nach float64 geht offenbar so:
float64(math.Float32frombits(binary.BigEndian.Uint32(b)))

}

enabled, err := wb.Enabled()
if err == nil && enabled {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bei Fehler lieber Abbruch?

}

// Switch phases
_, err = wb.conn.WriteSingleRegister(kathreinRegSetpointRelais, u)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bei Fehler Abbruch

case 0x0007:
return 3, nil
default:
return 0, err
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gibts diesen Fall?

Und: err ist hier immer nil!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may happen? This may prevent simply catching any value, like Kathrein implementing a new return value (like 2phase switching?) in the future without breaking the evcc since any new value will be graciously ignored by evcc. Granted, err is "nil". This will be changed.

// GetPhases implements the api.PhaseGetter interface
func (wb *Kathrein) GetPhases() (int, error) {
b, err := wb.conn.ReadHoldingRegisters(kathreinRegSetpointRelais, 1)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

fmt.Printf("Device - Timestamp (local):\t%d\n", b)
}

if b, err := wb.conn.ReadHoldingRegisters(kathreinRegL1Voltage, 2); err == nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Können wir die ganzen Messwerte (und ihre zugehörigen Konstanten) bitte weg lassen? Entweder werden sie schon ausgelesen oder sie sind für evcc nicht relevant.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I just wanted to make sure I was reading them correctly when first testing it. I will clean it up once I have a successful test

@andig andig changed the title Add support for Kathrein Wallbox Add Kathrein charger Apr 12, 2025
@andig andig marked this pull request as ready for review April 12, 2025 08:49
@andig
Copy link
Member
andig commented Apr 12, 2025

@TheMagnetar great PR, looking almost ready. Can I kindly ask you to contact info@evcc.io?

@andig
Copy link
Member
andig commented Apr 12, 2025

PS.: what's still missing is a template to make the implementation accessible.

@Dodi1968
Copy link
Contributor
Dodi1968 commented Apr 12, 2025

@TheMagnetar
Für das Template „evcc/templates/definition/charger/kathrein.yaml“
kann man sich an den folgenden Beispielen orientieren:
alfen.yaml,
peblar.yaml,
phoenix-em-eth.yaml

Vorschlag:

`template: kathrein
products:

  • brand: Kathrein
    description:
    generic: KWB-AC20, KWB-AC40, KWB-AC60, KWB-AC40E, KWB-AC60E
    capabilities: ["1p3p", "mA"]
    requirements:
    evcc: ["sponsorship"]
    description:
    de: Der Modbus-Server muss eingeschaltet sein.
    en: The Modbus Server must be enabled.
    params:
  • name: modbus
    choice: ["tcpip"]
    id: 0
    render: |
    type: kathrein
    {{- include "modbus" . }}`

@premultiply premultiply self-assigned this Apr 13, 2025
@TheMagnetar
Copy link
Contributor Author

@TheMagnetar great PR, looking almost ready. Can I kindly ask you to contact info@evcc.io?

Done

@github-actions github-actions bot added the stale Outdated and ready to close label Apr 22, 2025
@andig andig marked this pull request as draft April 27, 2025 13:13
@github-actions github-actions bot removed the stale Outdated and ready to close label Apr 27, 2025
@Dodi1968
Copy link
Contributor
Dodi1968 commented Apr 28, 2025

Failing check:
render_testing.go:71: map[host:localhost id:1 modbus:tcpip port:502 tcpip:true template:Kathrein]

Wäre hier nicht "id:0" korrekt?
Siehe "kathrein.yaml" und auch Modbus-Register-Dokument von Kathrein

@premultiply
Könnte das die Ursache für den Modbus-Fehler "invalid modbus configuration" sein?

@andig
Copy link
Member
andig commented Apr 29, 2025

Sieht so aus. Dafür muss die 1 aus der defaults.yaml raus ODER hier sollte ausnahmsweise mal einfach nur host konfiguriert werden und nicht der modbus Mechanismus genutzt.

@premultiply
Copy link
Member

Laut Manual ignoriert die Box ohnehin die Modbus-ID also ist es völlig egal was dort eingetragen wird. Kann also auch 1 sein.

@andig
Copy link
Member
andig commented Apr 29, 2025

Supi. Was fehlt denn dann hier noch?

@Dodi1968
Copy link
Contributor

In der kathrein.go (Zeile 150) und im Template kathrein.yaml ist aber "ID:0" gesetzt.

@andig
Copy link
Member
andig commented Apr 29, 2025

Wie gesagt. Das geht halt so nicht. Und wenns eh egal ist können wir das such ignorieren.

@Dodi1968
Copy link
Contributor

Mein Kommentar zielte auf die Frage ab, ob man das dann nicht auch in den beiden Dateien anpassen müsste.

Wir hatten die ID auf 0 gesetzt aufgrund der Aussage im Modbus-Dokument von Kathrein: "At the moment, the Unit-ID is ignored, but 0x00 (Broadcast) is recommended for future compatibility."

@andig andig marked this pull request as ready for review April 30, 2025 10:07
Copy link
Contributor
sourcery-ai bot commented Apr 30, 2025

Reviewer's Guide

This pull request introduces support for the Kathrein wallbox by adding a new charger implementation that communicates via Modbus TCP.

File-Level Changes

Change Details Files
Implement Kathrein charger logic using Modbus.
  • Define the Kathrein struct to manage the Modbus connection and state.
  • Implement standard charger interfaces (Status, Enabled, Enable, MaxCurrent, MaxCurrentMillis).
  • Implement meter interfaces (CurrentPower, Currents, Voltages, ChargedEnergy, TotalEnergy).
  • Implement phase switching (Phases1p3p, GetPhases).
  • Implement charge timer (ChargeDuration).
  • Implement diagnostic functions (Diagnose).
  • Define Kathrein-specific Modbus register addresses.
  • Add constructor NewKathreinFromConfig to initialize the charger from configuration and enable EMS control.
  • Include sponsor check.
  • Add helper function getPhaseValues to read phase data.
charger/kathrein.go
Add configuration template for the Kathrein charger.
  • Define the Kathrein template.
  • Specify product details, capabilities (1p3p, mA), and requirements (Modbus activation, sponsorship).
  • Configure Modbus TCP/IP parameters.
templates/definition/charger/kathrein.yaml

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Contributor
@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @TheMagnetar - I've reviewed your changes - here's some feedback:

  • Please ensure the license header in charger/kathrein.go matches the project's standard MIT license.
  • Consider making the enabling of EMS control (kathreinRegEMSControlRegister) configurable instead of always enabling it during initialization.
Here's what I looked at during the review
  • 🟢 General issues: all looks good
  • 🟢 Security: all looks good
  • 🟢 Testing: all looks good
  • 🟡 Complexity: 1 issue found
  • 🟢 Documentation: all looks good

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.


var _ api.Diagnosis = (*Kathrein)(nil)

// Diagnose implements the api.Diagnosis interface
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (complexity): Consider creating a helper function to encapsulate the register reading and printing logic in the Diagnose method.

Consider abstracting the repeated read/print logic into a helper that accepts a conversion function. For example, you could add a helper like this:

func (wb *Kathrein) readAndPrint(reg uint16, count uint16, label string, conv func([]byte) string) {
	if b, err := wb.conn.ReadHoldingRegisters(reg, count); err == nil {
		fmt.Printf("%s:\t%s\n", label, conv(b))
	}
}

Then, update Diagnose() to call this helper. For instance:

func (wb *Kathrein) Diagnose() {
	wb.readAndPrint(kathreinRegHeader, 1, "Header - Mapping Version",
		func(b []byte) string {
			return fmt.Sprintf("%d", b[1])
		})
	wb.readAndPrint(kathreinRegDeviceInfo, 1, "Device - Info",
		func(b []byte) string {
			return fmt.Sprintf("%d", b[1])
		})
	wb.readAndPrint(kathreinRegDeviceNumber, 8, "Device - Number",
		func(b []byte) string { return string(b) })
	// …continue for remaining registers…
}

This refactoring minimizes duplication, reduces nesting, and preserves all functionality.

@andig andig merged commit ed644b9 into evcc-io:master Apr 30, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
devices Specific device support
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants
0