Skip to content
Draft
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
c59e6da
feat: tinkerforge warp http api
lehmanju Jan 1, 2026
d4b4eb8
Apply suggestion from @Copilot
andig Jan 5, 2026
ae1a608
Apply suggestion from @Copilot
andig Jan 5, 2026
64e1369
fix: apply review suggestions
lehmanju Jan 9, 2026
6659652
fix: apply review 2
lehmanju Jan 11, 2026
71ec6c7
use struct instead of map for metersValues indices
lehmanju Jan 17, 2026
6a8c964
change meter variables and methods to legacy
lehmanju Jan 23, 2026
3729ddb
Initial commit
marcelGoerentz Jan 24, 2026
f0ddada
Add missing ws files
marcelGoerentz Jan 24, 2026
f163001
Implement suggestions
marcelGoerentz Jan 25, 2026
77969ac
Fix formating issues
marcelGoerentz Jan 25, 2026
e6a7579
Fix Enabled using wrong status
marcelGoerentz Jan 26, 2026
135b029
Add meter index from config when creating the struct
marcelGoerentz Jan 26, 2026
3770a25
Fix code formatting
marcelGoerentz Jan 26, 2026
7ada1fd
Add trace and debug messages, fix formatting
marcelGoerentz Jan 26, 2026
94425ed
Improve the switch clause
marcelGoerentz Jan 26, 2026
aa035a6
Remove uneccessary method
marcelGoerentz Jan 26, 2026
e8a1727
Fix interface assertion, clean up, fix findings
marcelGoerentz Jan 26, 2026
2c363fd
Improve the maintainability and readability of the code
marcelGoerentz Jan 27, 2026
c3db85a
Fix format
marcelGoerentz Jan 27, 2026
c90785e
Implement suggestions
marcelGoerentz Jan 27, 2026
0e7aadd
Refactored to get rid of boilerplate code
marcelGoerentz Jan 28, 2026
29ff7d1
Small improvements
marcelGoerentz Jan 28, 2026
710a0e5
More small improvements
marcelGoerentz Jan 28, 2026
760ee02
Fix formatting
marcelGoerentz Jan 28, 2026
877e720
Use Energy Manager where necessary, delete uneccessary methods and fi…
marcelGoerentz Jan 28, 2026
f3c6280
Abstract handler, abstract POST, abstract ensure method, add status m…
marcelGoerentz Jan 29, 2026
1b092e8
Fix possible deadlock
marcelGoerentz Jan 29, 2026
0a32a63
Merge branch 'master' into feature/add_warp_ws
andig Jan 30, 2026
8a572fc
Simplify
andig Jan 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
621 changes: 621 additions & 0 deletions charger/warp-ws.go

Large diffs are not rendered by default.

1,247 changes: 1,247 additions & 0 deletions charger/warp-ws_decorators.go

Large diffs are not rendered by default.

83 changes: 83 additions & 0 deletions charger/warp/meter_mapper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package warp

import "github.com/evcc-io/evcc/util"

type MeterMapper struct {
indices MeterValuesIndices
Log *util.Logger
}

func (m *MeterMapper) UpdateValueIDs(ids []int) {
required := []int{
ValueIDVoltageL1N,
ValueIDVoltageL2N,
ValueIDVoltageL3N,
ValueIDCurrentImExSumL1,
ValueIDCurrentImExSumL2,
ValueIDCurrentImExSumL3,
ValueIDPowerImExSum,
ValueIDEnergyAbsImSum,
}

// check that all IDs are present
missing := []int{}
for _, req := range required {
found := false
for _, id := range ids {
if id == req {
found = true
break
}
}
if !found {
missing = append(missing, req)
}
}

if len(missing) > 0 {
m.Log.ERROR.Printf("missing required meter value IDs: %v", missing)
return
}

// Create mapping
var idx MeterValuesIndices
Copy link
Member

@andig andig Jan 30, 2026

Choose a reason for hiding this comment

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

Why aren't the indexes simply a

map[int]int

then

for i, valueID := range ids {
    indexes[valueID] = i
}

and the entire mapper goes away.

for i, valueID := range ids {
switch valueID {
case ValueIDVoltageL1N:
idx.VoltageL1NIndex = i
case ValueIDVoltageL2N:
idx.VoltageL2NIndex = i
case ValueIDVoltageL3N:
idx.VoltageL3NIndex = i
case ValueIDCurrentImExSumL1:
idx.CurrentImExSumL1Index = i
case ValueIDCurrentImExSumL2:
idx.CurrentImExSumL2Index = i
case ValueIDCurrentImExSumL3:
idx.CurrentImExSumL3Index = i
case ValueIDPowerImExSum:
idx.PowerImExSumIndex = i
case ValueIDEnergyAbsImSum:
idx.EnergyAbsImSumIndex = i
}
}

m.indices = idx
}

func (m *MeterMapper) UpdateValues(vals []float64, power *float64, energy *float64, voltL, currL *[3]float64) {
// get the highest possible index
highestIndex := max(m.indices.VoltageL1NIndex, m.indices.VoltageL2NIndex, m.indices.VoltageL3NIndex,
m.indices.CurrentImExSumL1Index, m.indices.CurrentImExSumL2Index, m.indices.CurrentImExSumL3Index,
m.indices.PowerImExSumIndex, m.indices.EnergyAbsImSumIndex)

// check boundaries
if len(vals) < highestIndex+1 {
return
}

*voltL = [3]float64{vals[m.indices.VoltageL1NIndex], vals[m.indices.VoltageL2NIndex], vals[m.indices.VoltageL3NIndex]}
*currL = [3]float64{vals[m.indices.CurrentImExSumL1Index], vals[m.indices.CurrentImExSumL2Index], vals[m.indices.CurrentImExSumL3Index]}
*power = vals[m.indices.PowerImExSumIndex]
*energy = vals[m.indices.EnergyAbsImSumIndex]
}
57 changes: 26 additions & 31 deletions charger/warp/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ const (
FeatureMeterAllValues = "meter_all_values"
FeatureMeterPhases = "meter_phases"
FeatureNfc = "nfc"
FeatureMeters = "meters"
FeaturePhaseSwitch = "phase_switch"
)

// https://www.warp-charger.com/api.html#evse_state
Expand All @@ -22,18 +24,6 @@ type EvseExternalCurrent struct {
Current int `json:"current"`
}

// https://www.warp-charger.com/api.html#evse_low_level_state
type LowLevelState struct {
TimeSinceStateChange int64 `json:"time_since_state_change"`
Uptime int64 `json:"uptime"`
LedState int `json:"led_state"`
CpPwmDutyCycle int `json:"cp_pwm_duty_cycle"`
AdcValues []int `json:"adc_values"`
Voltages []int
Resistances []int
Gpio []bool
}

// https://www.warp-charger.com/api.html#meter_state
type MeterState struct {
State int `json:"state"` // Warp 1 only
Expand All @@ -48,14 +38,27 @@ type MeterValues struct {
PhasesConnected []bool `json:"phases_connected"`
}

// https://www.warp-charger.com/api.html#meter_all_values
type MeterAllValues struct {
PhasesActive []bool `json:"phases_active"`
PhasesConnected []bool `json:"phases_connected"`
}
// Meter value IDs according to Tinkerforge meter_value_id.csv
const (
ValueIDVoltageL1N = 1 // Voltage L1-N
ValueIDVoltageL2N = 2 // Voltage L2-N
ValueIDVoltageL3N = 3 // Voltage L3-N
ValueIDCurrentImExSumL1 = 13 // Current L1 Im-Ex Sum
ValueIDCurrentImExSumL2 = 17 // Current L2 Im-Ex Sum
ValueIDCurrentImExSumL3 = 21 // Current L3 Im-Ex Sum
ValueIDPowerImExSum = 74 // Power Im-Ex Sum L1 L2 L3
ValueIDEnergyAbsImSum = 209 // Energy Im Sum L1 L2 L3
)

type UsersConfig struct {
Users []User `json:"users"`
type MeterValuesIndices struct {
VoltageL1NIndex int
VoltageL2NIndex int
VoltageL3NIndex int
CurrentImExSumL1Index int
CurrentImExSumL2Index int
CurrentImExSumL3Index int
PowerImExSumIndex int
EnergyAbsImSumIndex int
}

type ChargeTrackerCurrentCharge struct {
Expand All @@ -68,23 +71,15 @@ type ChargeTrackerCurrentCharge struct {
} `json:"authorization_info"`
}

type User struct {
ID int `json:"id"`
Roles int `json:"roles"`
Current int `json:"current"`
DisplayName string `json:"display_name"`
UserName string `json:"username"`
}

type LastNfcTag struct {
type NfcTag struct {
UserID int `json:"user_id"`
Type int `json:"tag_type"`
ID string `json:"tag_id"`
}

type EmConfig struct {
ContactorInstalled bool `json:"contactor_installed"`
PhaseSwitchingMode int `json:"phase_switching_mode"`
type NfcConfig struct {
AuthorizedTags []NfcTag `json:"authorized_tags"`
DeadTimePostStart int `json:"deadtime_post_start"`
}

//go:generate go tool enumer -type ExternalControl -trimprefix ExternalControl -transform whitespace
Expand Down
File renamed without changes.
File renamed without changes.
47 changes: 47 additions & 0 deletions templates/definition/charger/tinkerforge-warp-ws.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
template: tinkerforge-warp-ws
products:
- brand: TinkerForge
description:
generic: WARP Charger Smart (WebSocket)
- brand: TinkerForge
description:
generic: WARP Charger Pro (WebSocket)
capabilities: ["mA", "1p3p", "rfid"]
requirements:
description:
en: Firmware v2 required. Automatic phase switching requires the additional WARP Energy Manager.
de: Firmware v2 erforderlich. Für automatische Phasenumschaltung wird zusätzlich der WARP Energy Manager benötigt.
evcc: ["skiptest"]
params:
- name: uri
required: true
- name: user
- name: password
- name: energyManagerUri
description:
generic: Energy Manager URI
help:
de: HTTP(S) Adresse des WARP Energy Manager
en: HTTP(S) address of the WARP Energy Manager
- name: energyManagerUser
description:
de: Energy Manager Benutzerkonto
en: Energy Manager Username
help:
de: bspw. E-Mail Adresse, User Id, etc.
en: e.g. email address, user id, etc.
- name: energyManagerPassword
description:
de: Energy Manager Passwort
en: Energy Manager Password
help:
de: Bei führenden Nullen bitte in einfache Hochkommata setzen
en: Use single quotes in case of leading zeros
render: |
type: warp-ws
uri: {{ .uri }}
user: {{ .user }}
password: {{ .password }}
energyManagerUri: {{ .energyManagerUri }}
energyManagerUser: {{ .energyManagerUser }}
energyManagerPassword: {{ .energyManagerPassword }}
1 change: 1 addition & 0 deletions templates/definition/charger/tinkerforge-warp.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
template: tinkerforge-warp
deprecated: true
covers:
- tinkerforge-warp-pro
products:
Expand Down
1 change: 1 addition & 0 deletions templates/definition/charger/tinkerforge-warp3-smart.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
template: tinkerforge-warp3-smart
deprecated: true
products:
- brand: TinkerForge
description:
Expand Down
25 changes: 25 additions & 0 deletions templates/definition/charger/tinkerforge-warp3-ws.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
template: tinkerforge-warp3-ws
products:
- brand: TinkerForge
description:
generic: WARP3 Charger Smart (WebSocket)
- brand: TinkerForge
description:
generic: WARP3 Charger Pro (WebSocket)
capabilities: ["mA", "1p3p", "rfid"]
requirements:
description:
de: Die automatische Phasenumschaltung bei 1p Fahrzeugen muss deaktiviert sein. Siehe [docs.warp-charger.com](https://docs.warp-charger.com/docs/mqtt_http/api_reference/evse#evse_phase_auto_switch_warp3).
en: The automatic phase switching for 1p vehicles must be deactivated. See [docs.warp-charger.com](https://docs.warp-charger.com/docs/mqtt_http/api_reference/evse#evse_phase_auto_switch_warp3).
evcc: ["skiptest"]
params:
- name: uri
required: true
- name: user
- name: password
render: |
type: warp-ws
uri: {{ .uri }}
user: {{ .user }}
password: {{ .password }}
disablePhaseAutoSwitch: true
1 change: 1 addition & 0 deletions templates/definition/charger/tinkerforge-warp3.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
template: tinkerforge-warp3
deprecated: true
products:
- brand: TinkerForge
description:
Expand Down
Loading