Skip to content

Conversation

premultiply
Copy link
Member

@premultiply premultiply commented Oct 3, 2025

Adds EEBus meter support

Features

Use Case Support:

  • MGCP (Monitoring of Grid Connection Point) - for grid meters (usage: grid)
  • MPC (Monitoring & Power Consumption) - for all other devices
  • LPC (Limitation of Power Consumption) - for §14a EnWG power limiting via api.Dimmer

Capabilities:

  • ✅ Power, energy, current, and voltage measurements
  • ✅ Power limiting for controllable consumption devices
  • ✅ Dynamic use case selection based on meter usage type
  • ✅ Automatic mDNS discovery with optional manual IP configuration

Implementation

  • eebus.go - Core implementation with dual monitoring use cases (MGCP/MPC) + LPC support
  • eebus.go - Added MPC use case to server
  • eebus.yaml - Template configuration
  • eebus_test.go - Unit tests
  • template_test.go - Added acceptable error for EEBus

Configuration Example

meters:
  - name: grid
    type: eebus
    ski: ABC123...
    usage: grid  # Uses MGCP

  - name: heat_pump
    type: eebus
    ski: DEF456...
    usage: ext # Uses MPC + LPC (Dimmer API)

Output Example (SMA Sunny Home Manager 2.0 - MGCP)

$ ./evcc -c /tmp/evcc-test-eebus.yaml meter test_eebus --timeout 30s                                    
[main  ] INFO 2025/10/06 01:53:31 using config file: /tmp/evcc-test-eebus.yaml
[db    ] INFO 2025/10/06 01:53:31 using sqlite database: /tmp/evcc.db
[eebus ] INFO 2025/10/06 01:53:31 Local SKI: 7daeda0e1d9f329abc158ad23eef8323ad6ae331
[eebus ] ERROR 2025/10/06 01:53:32 operation is not supported on function electricalConnectionParameterDescriptionListData
Power:           631W           1.572s  
Energy:          16071.1kWh             
Current L1..L3:  not available          
Voltage L1..L3:  not available          
Power L1..L3:    not available  

Total time:                     1.572s

Closes #24075

/cc @DerAndereAndi @CiNcH83

@premultiply premultiply self-assigned this Oct 3, 2025
@premultiply premultiply added the devices Specific device support label Oct 3, 2025
@premultiply premultiply requested a review from andig October 3, 2025 21:53
@premultiply premultiply marked this pull request as ready for review October 3, 2025 21:54
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 there - I've reviewed your changes and they look great!

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location> `meter/eebus_test.go:10` </location>
<code_context>
+	"github.com/evcc-io/evcc/util/test"
+)
+
+func TestEEBus(t *testing.T) {
+	acceptable := []string{
+		"eebus not configured",
</code_context>

<issue_to_address>
**suggestion (testing):** Test only covers the 'not configured' error case.

Please add tests for successful initialization and for both MGCP and MPC usage types to verify correct meter setup and dynamic use case selection.

Suggested implementation:

```golang
func TestEEBus(t *testing.T) {
	acceptable := []string{
		"eebus not configured",
	}

	// Test not configured error case
	values := map[string]any{
		"ski":     "test-ski",
		"ip":      "192.0.2.2",
		"usage":   "grid",
		"timeout": "10s",
	}

	if _, err := NewFromConfig(context.TODO(), "eebus", values); err != nil && !test.Acceptable(err, acceptable) {
		t.Errorf("unexpected error: %v", err)
	}

	// Test successful initialization (simulate with minimal valid config)
	successValues := map[string]any{
		"ski":     "valid-ski",
		"ip":      "192.0.2.3",
		"usage":   "grid",
		"timeout": "10s",
	}
	meter, err := NewFromConfig(context.TODO(), "eebus", successValues)
	if err != nil {
		t.Errorf("expected successful initialization, got error: %v", err)
	}
	if meter == nil {
		t.Error("expected meter instance, got nil")
	}

	// Test MGCP usage type
	mgcpValues := map[string]any{
		"ski":     "mgcp-ski",
		"ip":      "192.0.2.4",
		"usage":   "mgcp",
		"timeout": "10s",
	}
	mgcpMeter, err := NewFromConfig(context.TODO(), "eebus", mgcpValues)
	if err != nil {
		t.Errorf("MGCP usage: expected successful initialization, got error: %v", err)
	}
	if mgcpMeter == nil {
		t.Error("MGCP usage: expected meter instance, got nil")
	}
	// Optionally, check for MGCP-specific behavior if available
	// e.g. assert MGCP use case selection

	// Test MPC usage type
	mpcValues := map[string]any{
		"ski":     "mpc-ski",
		"ip":      "192.0.2.5",
		"usage":   "mpc",
		"timeout": "10s",
	}
	mpcMeter, err := NewFromConfig(context.TODO(), "eebus", mpcValues)
	if err != nil {
		t.Errorf("MPC usage: expected successful initialization, got error: %v", err)
	}
	if mpcMeter == nil {
		t.Error("MPC usage: expected meter instance, got nil")
	}
	// Optionally, check for MPC-specific behavior if available
	// e.g. assert MPC use case selection
}

```

If the `NewFromConfig` function requires a real EEBus backend or network connection, you may need to mock or stub out the relevant parts for these tests to work reliably in CI. Also, if there are specific methods or fields to check for MGCP/MPC use case selection, add assertions for those.
</issue_to_address>

### Comment 2
<location> `meter/eebus.go:97` </location>
<code_context>

 // UseCaseEvent implements the eebus.Device interface
 func (c *EEBus) UseCaseEvent(_ spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event eebusapi.EventType) {
-	switch event {
-	case mgcp.DataUpdatePower:
</code_context>

<issue_to_address>
**issue (complexity):** Consider refactoring MGCP/MPC selection into a provider interface to eliminate repeated if/else logic.

Here’s one way to collapse all the `if c.useMGCP { … } else { … }` logic into a single strategy-style provider. You end up with one unified switch in `UseCaseEvent` and four tiny `dataUpdateX` methods:

1) Define a small interface and add it to your struct:

```go
// at top of file
type dataUseCase interface {
  Power(spineapi.EntityRemoteInterface) (float64, error)
  EnergyConsumed(spineapi.EntityRemoteInterface) (float64, error)
  CurrentPerPhase(spineapi.EntityRemoteInterface) ([]float64, error)
  VoltagePerPhase(spineapi.EntityRemoteInterface) ([]float64, error)
}

type EEBus struct {
  // ...
  provider dataUseCase
  // remove: useMGCP
}
```

2) In your constructor, wire up the right implementation once:

```go
func NewEEBus(… usage templates.Usage, …) (*EEBus, error) {
  c := &EEBus{ /**/ }

  if usage == templates.UsageGrid {
    c.provider = c.uc.MGCP
  } else {
    c.provider = c.uc.MPC
  }

  //
  return c, nil
}
```

3) Collapse `UseCaseEvent` into one switch with both event-IDs:

```go
func (c *EEBus) UseCaseEvent(_ spineapi.DeviceRemoteInterface, ent spineapi.EntityRemoteInterface, event eebusapi.EventType) {
  switch event {
  case mgcp.DataUpdatePower,    mpc.DataUpdatePower:
    c.dataUpdatePower(ent)
  case mgcp.DataUpdateEnergyConsumed,    mpc.DataUpdateEnergyConsumed:
    c.dataUpdateEnergyConsumed(ent)
  case mgcp.DataUpdateCurrentPerPhase,    mpc.DataUpdateCurrentsPerPhase:
    c.dataUpdateCurrentPerPhase(ent)
  case mgcp.DataUpdateVoltagePerPhase,    mpc.DataUpdateVoltagePerPhase:
    c.dataUpdateVoltagePerPhase(ent)
  }
}
```

4) Simplify each `dataUpdateX`:

```go
func (c *EEBus) dataUpdatePower(ent spineapi.EntityRemoteInterface) {
  v, err := c.provider.Power(ent)
  if err != nil {
    c.log.ERROR.Println("Power:", err)
    return
  }
  c.power.Set(v)
}

// …and similarly for EnergyConsumed, CurrentPerPhase, VoltagePerPhase…
```

This removes all of the nested `if/else` blocks while keeping MGCP vs MPC selection at construction time only.
</issue_to_address>

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.

@andig
Copy link
Member

andig commented Oct 3, 2025

Das muss jemand mit Gegenstelle usprobiefen

@CiNcH83
Copy link
Contributor

CiNcH83 commented Oct 4, 2025

Das ging aber schnell...

Wie kann ich da die Limitierung triggern? Muss ich da evcc noch mit dem controlbox simulator koppeln und darüber triggern?

[EDIT]
Zum Testen würde ich das eher direkt (REST oder CLI) machen. Der von @vollautomat erweiterte controlbox simulator hat bei mir noch große Probleme verursacht, wenn mehrere EEBUS Geräte im Netz waren...

[EDIT2]
Da ich mein Produktivsystem nicht gerne für Experimente verwende, setze ich gerade einen Raspberry Pi 4 zum Bauen und Testen auf.

@premultiply
Copy link
Member Author

Im ersten Schritt wäre es schon toll erstmal das als ext-Meter einzurichten und mit der WP zu koppeln.

evcc meter sollte dann hoffentlich die Messdaten der WP zurückgeben.

Wenn du einen "EEBus-Netzzähler" hast oder diesen simulieren kannst könntest du ein weiteres Meter damit koppeln und dabei usage: grid konfigurieren. Auch hier sollte evcc meter die erwarten Messwerte liefern.

@CiNcH83
Copy link
Contributor

CiNcH83 commented Oct 4, 2025

OK. Ich setze erstmal alles auf dem Raspi4 auf und baue da dann deinen Branch. Wollte ich eh schon lange machen, da ich dann evtl. aktiver mithelfen kann. Auch wenn Go aktuell noch eine Fremdsprache für mich ist. Falls das OK ist, würde ich bei Probleme ggfs. auf Slack auf dich zukommen.

@premultiply
Copy link
Member Author

Wie kann ich da die Limitierung triggern? Muss ich da evcc noch mit dem controlbox simulator koppeln und darüber triggern?

Ja, im Prinzip so.

Ich bin mir aber gerade noch nicht sicher was noch innerhalb evcc ggf. fehlt damit wir das dann auch letztendlich am gekoppelten Verbraucher sehen. Hier das ist ja quasi nur der Adapter. @andig

@andig andig changed the title Add EEBus meter EEBus meter: add MPC/LPC use cases Oct 4, 2025
@CiNcH83
Copy link
Contributor

CiNcH83 commented Oct 4, 2025

Kurzes Status-Update... nachdem ich mit den apt Packages grandios gescheitert bin, habe ich die korrekten Paket-Versionen jetzt installiert und konnte den Branch von @premultiply erfolgreich bauen und ausführen. Als nächstes geht es ans Testen des EEBUS Meters. Ich versuche immer mal wieder ein bisschen Zeit zwischen dem Familienleben zu investieren. Sonst spätestens Montag, wo ich den ganzen Tag Zeit dafür habe...

@CiNcH83

This comment was marked as outdated.

@andig

This comment was marked as outdated.

@premultiply

This comment was marked as outdated.

@andig

This comment was marked as resolved.

@CiNcH83
Copy link
Contributor

CiNcH83 commented Oct 4, 2025

Hab es rausgelöscht...

Assozierung in der myVAILLANT App durchgeführt:
image

Log:

Pi@raspberrypi:~/repos/evcc-eebus-meter $ ./evcc -c eebus.yaml meter
[main  ] INFO 2025/10/04 13:42:21 using config file: eebus.yaml
[db    ] INFO 2025/10/04 13:42:21 using sqlite database: /home/Pi/.evcc/evcc.db
[eebus ] INFO 2025/10/04 13:42:21 Local SKI: 3d820f423a3066cfe2801a4e4d8aa561a12e65c3
timeout
timeout
timeout
timeout
timeout
timeout
timeout
timeout
timeout
Power:  timeout
Energy: timeout
[eebus ] ERROR 2025/10/04 13:42:22 websocket server error: http: Server closed

Die Timeouts kommen in sehr schneller Abfolge.

@premultiply
Copy link
Member Author

Das Ding ist, dass evcc meter hier nicht funktioniert, da es nicht lange genug auf die asynchron eintreffenen Daten wartet.
Du müsstest es wohl mit einer laufenden Instanz testen.

Ich teste das gerade parallel mit dem Sunny Home Manger der offensichtlich MGCP unterstützt.

@CiNcH83
Copy link
Contributor

CiNcH83 commented Oct 4, 2025

Müsste ich den MCP dann irgendwann im Log vorbeifliegen sehen? Kommt aktuell nichts... Oder muss ich dann einen Loadpoint mit dem Meter anlegen und im UI schauen? Nutze immer noch obige Minimal-Konfig. Produktivsystem läuft parallel auf dem anderen Raspi (EEBUS ist dort deaktiviert).

@premultiply premultiply marked this pull request as ready for review October 12, 2025 09:24
sourcery-ai[bot]

This comment was marked as outdated.

@premultiply
Copy link
Member Author

ping @CiNcH83

@CiNcH83
Copy link
Contributor

CiNcH83 commented Oct 12, 2025

./evcc -c eebus.yaml meter arotherm_eebus --timeout 130s
[main  ] INFO 2025/10/12 14:20:13 using config file: eebus.yaml
[db    ] INFO 2025/10/12 14:20:13 using sqlite database: /home/Pi/.evcc/evcc.db
[eebus ] INFO 2025/10/12 14:20:13 Local SKI: 3d820f423a3066cfe2801a4e4d8aa561a12e65c3
Power:           timeout        1m38.259s
Energy:          not available
Current L1..L3:  not available
Voltage L1..L3:  not available

Total time:                     1m38.259s
[eebus ] ERROR 2025/10/12 14:21:52 websocket server error: http: Server closed

evcc-eebus-debug.log

@CiNcH83
Copy link
Contributor

CiNcH83 commented Oct 12, 2025

Jetzt, wo ich den meter ext gemacht habe, funktioniert es plötzlich zuverlässig:

Pi@raspberrypi:~/repos/evcc-eebus-meter $ ./evcc -c eebus.yaml meter arotherm_eebus --timeout 130s
[main  ] INFO 2025/10/12 19:54:26 using config file: eebus.yaml
[db    ] INFO 2025/10/12 19:54:26 using sqlite database: /home/Pi/.evcc/evcc.db
[eebus ] INFO 2025/10/12 19:54:26 Local SKI: 3d820f423a3066cfe2801a4e4d8aa561a12e65c3
Power:           1181W          454ms
Energy:          not available
Current L1..L3:  not available
Voltage L1..L3:  not available

Total time:                     454ms
[eebus ] ERROR 2025/10/12 19:54:27 websocket server error: http: Server closed

Sobald ich das ext rausnehme, geht es nicht mehr. Ist dann wohl wichtig...

@premultiply
Copy link
Member Author

@andig Rein?

@andig
Copy link
Member

andig commented Oct 13, 2025

@naltatis wollen wir eine ext usage einführen? Ich würde das ungern ausschließlich für diesen Case machen.
@premultiply alternative Vorgehensweisung: die würden hier ein eebus-grid und ein eebus Template ein und differenzieren darüber.

@andig andig marked this pull request as draft October 13, 2025 11:53
@andig andig added the needs decision Unsure if we should really do this label Oct 13, 2025
@premultiply
Copy link
Member Author

Zwei Templates, eins fest mit usage: grid und eins ganz ohne würde auch funktionieren.

value = limit
}

return c.uc.LPC.SetConsumptionLimit(ucapi.LoadLimit{
Copy link
Member

Choose a reason for hiding this comment

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

Achso: das müssen wir vor Merge immer noch klären! Im Zweifel erstmal ohne LPC.

Copy link
Member

Choose a reason for hiding this comment

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

Das hier ist noch offen- würde ich in den Dim PR verschieben.

@andig andig added the needs documentation Triggers issue creation in evcc-io/docs label Oct 13, 2025
@jan666
Copy link

jan666 commented Oct 17, 2025

Ich habe den Pull Request zufällig gefunden. Es scheint genau das zu sein was ich suche (Vaillant aroTHERM Plus, Fronius SYMO Gen24 WR und BYD Akku) um überschuss limitiert in den Heizungspuffer zu bekommen.

Ich hatte evcc schon aufgesetzt mit PV und Akku über den Wechselrichter und Wärmepumpe über die SensoNET API. Ich würde aber lieber über den PV-Ready Kontakt gehen und die Leistung auf den Überschuss limitieren.

Ich habe den branch bereits ausgecheckt und kompiliert. Habe aber nicht wirklich verstanden wie ich jetzt weiter vorgehen muss. Den ControlBox Simulator hatte ich schon erfolgreich getestet.

Da ich kein evcc Produktivsystem habe könnte ich also problemlos testen wenn mir jemand sagen würde wie ich es einrichten muss.

@CiNcH83
Copy link
Contributor

CiNcH83 commented Oct 17, 2025

Ich habe den branch bereits ausgecheckt und kompiliert. Habe aber nicht wirklich verstanden wie ich jetzt weiter vorgehen muss. Den ControlBox Simulator hatte ich schon erfolgreich getestet.

Hier gibt es eine recht gute Anleitung, was zu tun ist.

Der eegbus-go Fork von @meisel2000 ist etwas älter und kann noch kein MPC. Der Fork von @vollautomat kann MPC. Da sind die Aufrufparameter dann etwas anders als in der verlinkten Anleitung. Man sollte sich für eine Variante entscheiden und dann dabei bleiben. Die Variante von @vollautomat verwendet eine leicht geänderte Geräte-Kennung und hat daher eine andere SHIPID/SKI, was diverse EEBUS Geräte im Netz (wie die Vaillant aroTHERM) verwirrt.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

devices Specific device support needs decision Unsure if we should really do this needs documentation Triggers issue creation in evcc-io/docs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Improve EEBus meter

5 participants