Skip to content

Conversation

@lehmanju
Copy link
Contributor

@lehmanju lehmanju commented Jan 1, 2026

Die WARP Charger von Tinkerforge implementieren inzwischen alle eine HTTP API. Dieser PR implementiert diese API analog zur MQTT API. Die MQTT API wird weiter unterstützt, ist aber als deprecated markiert. Zukünftige Nutzer können die Wallbox jetzt einfacher konfigurieren, es ist nur die Angabe einer IP/eines Hostnamens erforderlich.

Damit wird der MQTT Broker für mich dann auch endlich überflüssig und kann abgeschaltet werden.

EDIT:

  • Zwei Templates (WARP und WARP3)
  • Für WARP3 wird das Phase Auto Switching immer disabled, für WARP wird das nicht ausgeführt, weil der Endpunkt fehlt
  • Der EnergyManager wird nur genutzt, wenn die Wallbox das power_manager Feature nicht selbst unterstützt, also nur für WARP
  • Nutze neue meters API, aber behalte meter API als Fallback

@evcc-bot evcc-bot added devices Specific device support enhancement New feature or request labels Jan 1, 2026
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 - I've left some high level feedback:

  • In meterValues the length check len(res) <= 5 will still allow slices of length 5 and then currents/voltages index up to res[5], so this should be len(res) < 6 (or equivalent) to avoid a potential out-of-bounds panic.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `meterValues` the length check `len(res) <= 5` will still allow slices of length 5 and then `currents`/`voltages` index up to `res[5]`, so this should be `len(res) < 6` (or equivalent) to avoid a potential out-of-bounds panic.

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 Jan 1, 2026

devcontainer baute nicht, deswegen moby deaktiviert

OT, bitte neuer PR.

@andig
Copy link
Member

andig commented Jan 1, 2026

Nice PR!

@lehmanju lehmanju force-pushed the warp-http branch 2 times, most recently from d908dee to 2d664be Compare January 1, 2026 16:58
@andig
Copy link
Member

andig commented Jan 1, 2026

Die MQTT API wird weiter unterstützt, ist aber als deprecated markiert

Hast Du dazu eine Referenz?

@lehmanju
Copy link
Contributor Author

lehmanju commented Jan 1, 2026

Die MQTT API wird weiter unterstützt, ist aber als deprecated markiert

Hast Du dazu eine Referenz?

Ah, missverständlich. Die ist nicht in der WARP Firmware deprecated, sondern die evcc Templates sind als deprecated markiert ;).

@andig
Copy link
Member

andig commented Jan 1, 2026

Ah- für mich auch ok. Ohne Umwege ist egtl. immer besser!

@andig andig changed the title Tinkerforge WARP HTTP API Tinkerforge WARP HTTP API (BC) Jan 1, 2026
@andig andig requested a review from premultiply January 1, 2026 17:10
Copy link
Collaborator

@mfuchs1984 mfuchs1984 left a comment

Choose a reason for hiding this comment

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

See inline comment.

@deadrabbit87
Copy link
Contributor

Für Setups wo neben evcc auch andere Systeme auf die Wallbox zugreifen, hat MQTT den Vorteil dass alle Clients parallel per Publish/Subscribe lauschen können, ohne die Wallbox mehrfach zu pollen. Wäre es sinnvoll, die MQTT Templates nicht als deprecated zu markieren, sondern als gleichwertige Alternative zu belassen – für Nutzer die ohnehin einen Broker betreiben?

@lehmanju
Copy link
Contributor Author

lehmanju commented Jan 3, 2026

Für Setups wo neben evcc auch andere Systeme auf die Wallbox zugreifen, hat MQTT den Vorteil dass alle Clients parallel per Publish/Subscribe lauschen können, ohne die Wallbox mehrfach zu pollen. Wäre es sinnvoll, die MQTT Templates nicht als deprecated zu markieren, sondern als gleichwertige Alternative zu belassen – für Nutzer die ohnehin einen Broker betreiben?

Die API wird ja nicht tausendfach pro Sekunde gepollt, insofern ist das denke ich kein Problem. HTTP mit JSON kann schon einiges ab, da kommt man mit evcc nicht so schnell an Grenzen. Andere Clients können ja weiterhin MQTT nutzen. Sollte sich Gegenteiliges herausstellen, kann man das deprecated ja wieder rückgängig machen. Aber besser ist glaube ich schon perspektivisch nur eine API zu pflegen.

@deadrabbit87
Copy link
Contributor

Ich nutze ein WARP2 mit WEM der die Phaseumschaltung macht.

Für Setups wo neben evcc auch andere Systeme auf die Wallbox zugreifen, hat MQTT den Vorteil dass alle Clients parallel per Publish/Subscribe lauschen können, ohne die Wallbox mehrfach zu pollen. Wäre es sinnvoll, die MQTT Templates nicht als deprecated zu markieren, sondern als gleichwertige Alternative zu belassen – für Nutzer die ohnehin einen Broker betreiben?

Die API wird ja nicht tausendfach pro Sekunde gepollt, insofern ist das denke ich kein Problem. HTTP mit JSON kann schon einiges ab, da kommt man mit evcc nicht so schnell an Grenzen. Andere Clients können ja weiterhin MQTT nutzen. Sollte sich Gegenteiliges herausstellen, kann man das deprecated ja wieder rückgängig machen. Aber besser ist glaube ich schon perspektivisch nur eine API zu pflegen.

Ich wollte das nur anmerken, da ich persönlich lieber weiter den Broker nutzen wollen würde.

Ist aber nicht konstruktiv begründet, sondern nur Geschmackssache.

@lehmanju
Copy link
Contributor Author

lehmanju commented Jan 4, 2026

@deadrabbit87 @mfuchs1984 gerne nochmal testen ;)

@mfuchs1984
Copy link
Collaborator

Habe selbst keinen WARP charger ;)

@deadrabbit87
Copy link
Contributor

Mach ich heute Abend.

@andig
Copy link
Member

andig commented Jan 12, 2026

@rtrbt could you kindly take a look at https://github.com/evcc-io/evcc/pull/26334/changes#diff-8a0b3f5a73d38e9a0a8367e7d12113e1ee968bd65f9e444feb12c22091c300a7R283? This looks like a horrible API. Couldn't this be returned as map in the first place?

@rtrbt
Copy link

rtrbt commented Jan 13, 2026

Couldn't this be returned as map in the first place?

Nope, the /values API has to be as small as possible due to memory constraints on the charger (we support up to 7 chargers with 96 values each) and also to reduce traffic, because value IDs are stable and values change all the time.

Building an index cache with the value IDs and then indexing directly into values is using the API as intended. This could however be done with one indirection less by not maintaining warp.metersValuesMap as a map, but as an array instead:

https://github.com/evcc-io/evcc/pull/26334/changes#diff-8a0b3f5a73d38e9a0a8367e7d12113e1ee968bd65f9e444feb12c22091c300a7R344-R351

	result.VoltageL1N = res[0]
	result.VoltageL2N = res[1]
	result.VoltageL3N = res[2]
	result.CurrentImExSumL1 = res[3]
	result.CurrentImExSumL2 = res[4]
	result.CurrentImExSumL3 = res[5]
	result.PowerImExSum = res[6]
	result.EnergyAbsImSum = res[7]

This could be simplified more if it is possible to iterate over struct members in Go, or if you change warp.MetersValues to be an array.

@andig
Copy link
Member

andig commented Jan 13, 2026

@lehmanju could you pick this up?

@marcelGoerentz
Copy link
Contributor

marcelGoerentz commented Jan 13, 2026

Just one question, why don't we use a web socket instead of http requests? The communication will then be bidirectional and we can use most of the mqtt implementation, since the data coming from the charger will be the same.

EDIT:
Never mind, the websocket is only for retrieving data, command needs to be done via Http request.
However it is still useful to receive live data and events.

@lehmanju
Copy link
Contributor Author

The problem is:

  • I want to index by name not by magic value
  • Because indices in the final /values call are dynamic, these "names" need to be dynamic as well
  • Dynamic names = Map/Struct of indices which is then used during decoding

So, an alternative approach would be to use a struct instead of map for metersValuesMap. Other than that, I fail to see how this can be simplified while remaining readable in any other way. I've now implemented the struct approach.

@lehmanju lehmanju marked this pull request as ready for review January 17, 2026 12:44
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 - I've found 1 issue, and left some high level feedback:

  • In hasFeature you cache an empty feature list when the initial /info/features call fails, which will permanently disable all feature-based behavior for that instance; consider surfacing the error during construction or retrying instead of silently treating it as 'no features'.
  • metersValueIds currently fails charger initialization if any of the required value IDs are missing; if some devices legitimately expose only a subset, it might be more robust to degrade functionality (e.g., disable currents/voltages/energy individually) instead of returning an error for the whole charger.
  • In disablePhaseAutoSwitch the error message 'disabling phase auto switch failed' is wrapped twice (once on request creation and once on Do); consider centralizing the wrapping to avoid redundant and slightly confusing error text.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In hasFeature you cache an empty feature list when the initial /info/features call fails, which will permanently disable all feature-based behavior for that instance; consider surfacing the error during construction or retrying instead of silently treating it as 'no features'.
- metersValueIds currently fails charger initialization if any of the required value IDs are missing; if some devices legitimately expose only a subset, it might be more robust to degrade functionality (e.g., disable currents/voltages/energy individually) instead of returning an error for the whole charger.
- In disablePhaseAutoSwitch the error message 'disabling phase auto switch failed' is wrapped twice (once on request creation and once on Do); consider centralizing the wrapping to avoid redundant and slightly confusing error text.

## Individual Comments

### Comment 1
<location> `charger/warp-http_decorators.go:9` </location>
<code_context>
+	"github.com/evcc-io/evcc/api"
+)
+
+func decorateWarpHTTP(base *WarpHTTP, meter func() (float64, error), meterEnergy func() (float64, error), phaseCurrents func() (float64, float64, float64, error), phaseVoltages func() (float64, float64, float64, error), identifier func() (string, error), phaseSwitcher func(int) error, phaseGetter func() (int, error)) api.Charger {
+	switch {
+	case identifier == nil && meter == nil && phaseSwitcher == nil:
</code_context>

<issue_to_address>
**issue (complexity):** Consider changing the generator to build the decorated charger by sequentially wrapping `base` once per non-nil capability instead of enumerating every interface combination in a massive switch.

You can avoid the combinatorial explosion entirely by making the generator compose decorators incrementally instead of precomputing every combination.

Right now, each `case` defines a new anonymous struct that embeds `*WarpHTTP` plus some interfaces. Instead, you can:

1. Start from `api.Charger` (`base`).
2. For each non-nil capability function, wrap the current charger value in a small anonymous struct that embeds:
   - the previous `api.Charger`, and
   - exactly one new interface implementation.

Because the embedded `api.Charger` already implements all previously-added interfaces, each wrapper automatically implements the union of all interfaces without needing a dedicated struct per combination.

Conceptually, your generator can produce something like this:

```go
func decorateWarpHTTP(
	base *WarpHTTP,
	meter func() (float64, error),
	meterEnergy func() (float64, error),
	phaseCurrents func() (float64, float64, float64, error),
	phaseVoltages func() (float64, float64, float64, error),
	identifier func() (string, error),
	phaseSwitcher func(int) error,
	phaseGetter func() (int, error),
) api.Charger {
	var c api.Charger = base

	if meter != nil {
		c = &struct {
			api.Charger
			api.Meter
		}{
			Charger: c,
			Meter: &decorateWarpHTTPMeterImpl{
				meter: meter,
			},
		}
	}

	if meterEnergy != nil {
		c = &struct {
			api.Charger
			api.MeterEnergy
		}{
			Charger: c,
			MeterEnergy: &decorateWarpHTTPMeterEnergyImpl{
				meterEnergy: meterEnergy,
			},
		}
	}

	if phaseCurrents != nil {
		c = &struct {
			api.Charger
			api.PhaseCurrents
		}{
			Charger: c,
			PhaseCurrents: &decorateWarpHTTPPhaseCurrentsImpl{
				phaseCurrents: phaseCurrents,
			},
		}
	}

	// ...repeat pattern for phaseVoltages, identifier, phaseSwitcher, phaseGetter...

	return c
}
```

Key points:

- You only ever create at most one anonymous struct per capability.
- The method set grows by embedding the previous `api.Charger`, so the final `c` implements all required interfaces.
- The nil case is naturally handled: if all funcs are nil, `c` stays as `base`.

Refactoring the generator to emit this sequential wrapping pattern will:

- Reduce `decorateWarpHTTP` from hundreds of nearly identical `case` branches to a short, linear function.
- Preserve all existing functionality and type behavior (the returned value still implements `api.Charger` plus whichever optional interfaces are available).
</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.

@lehmanju
Copy link
Contributor Author

@andig any TODOs left?

@andig
Copy link
Member

andig commented Jan 23, 2026

Ich bin nicht glücklich mit der meter(s)Values Unterscheidung, auch wenn die API so heisst. Das ist wahnsinnig subtil und visuell kaum zu unterscheiden.

@lehmanju
Copy link
Contributor Author

@andig erledigt

@marcelGoerentz
Copy link
Contributor

Ich habe eine Implementierung mit Websocket.
Wäre vielleicht die schönere Lösung, die Strukturen aus dem MQTT Version werden auch wieder genutzt.
Also falls Interesse besteht, werde ich ein PR einreichen.
Mit Websocket hat man den Vorteil das man nicht aktiv Pollen muss und die HTTP-API nur für Kommandos nutzt.
Die Daten sind dann wie bei der MQTT Version immer aktuell.

@andig
Copy link
Member

andig commented Jan 24, 2026

Fänd ich gut. Der Klarheit halber: neuer PR gegen master der auf diesem hier aufbaut und den replaced?

@marcelGoerentz
Copy link
Contributor

@andig ja, so würde ich es machen.

Ist noch nicht ganz fertig, aber bald.

@lehmanju
Copy link
Contributor Author

Cool, danke für's übernehmen!

@marcelGoerentz
Copy link
Contributor

@lehmanju Eine Frage habe ich noch, warum hast die Meters API konfigurierbar gemacht. Macht es nicht Sinn nur das erste Meter (Index 0) auszulesen?

Jedes andere Meter ist nicht vom Charger und kann bei evcc direkt eingebunden werden.

@lehmanju
Copy link
Contributor Author

@marcelGoerentz kannst auch hardcoden, aber "theoretisch" ist es halt in der warp konfigurierbar.

@marcelGoerentz
Copy link
Contributor

@andig was meinst du dazu?

Hardcoded würde immer das "interne" Meter nutzen. Konfigurierbar kann der Nutzer aussuchen welches der in der WARP konfigurierten Meter genutzt werden soll.

@lehmanju Man kann in der WARP auch externe Meter den Index 0 geben, dadurch wird es als "Internes" genutzt.

@lehmanju
Copy link
Contributor Author

Definitiv ist hardcoded in Ordnung, wurde oben im Thread schon besprochen. Ist hier ja auch so, nur halt in der yaml.

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

Labels

devices Specific device support enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants