From e707282a010b506845a2661665b4422d35be917e Mon Sep 17 00:00:00 2001 From: Marcel Goerentz <57457529+marcelGoerentz@users.noreply.github.com> Date: Wed, 4 Feb 2026 11:51:45 +0100 Subject: [PATCH] Add Tinkerforge WARP WebSocket API (BC) (#26970) --- charger/warp-ws.go | 516 +++++++ charger/warp-ws_decorators.go | 1247 +++++++++++++++++ charger/warp/types.go | 97 +- charger/{warp2.go => warp2-mqtt.go} | 8 +- ...decorators.go => warp2-mqtt_decorators.go} | 0 .../charger/tinkerforge-warp-ws.yaml | 32 + .../definition/charger/tinkerforge-warp.yaml | 1 + .../charger/tinkerforge-warp1-ws.yaml | 29 + .../charger/tinkerforge-warp2-ws.yaml | 44 + .../charger/tinkerforge-warp3-smart.yaml | 1 + .../definition/charger/tinkerforge-warp3.yaml | 1 + 11 files changed, 1911 insertions(+), 65 deletions(-) create mode 100644 charger/warp-ws.go create mode 100644 charger/warp-ws_decorators.go rename charger/{warp2.go => warp2-mqtt.go} (97%) rename charger/{warp2_decorators.go => warp2-mqtt_decorators.go} (100%) create mode 100644 templates/definition/charger/tinkerforge-warp-ws.yaml create mode 100644 templates/definition/charger/tinkerforge-warp1-ws.yaml create mode 100644 templates/definition/charger/tinkerforge-warp2-ws.yaml diff --git a/charger/warp-ws.go b/charger/warp-ws.go new file mode 100644 index 000000000..16a4aab8e --- /dev/null +++ b/charger/warp-ws.go @@ -0,0 +1,516 @@ +package charger + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "net/url" + "path" + "slices" + "strings" + "sync" + "time" + + "github.com/cenkalti/backoff/v4" + "github.com/coder/websocket" + "github.com/evcc-io/evcc/api" + "github.com/evcc-io/evcc/charger/warp" + "github.com/evcc-io/evcc/util" + "github.com/evcc-io/evcc/util/request" + "github.com/jpfielding/go-http-digest/pkg/digest" +) + +type WarpWS struct { + *request.Helper + + // config + emHelper *request.Helper + log *util.Logger + uri string + emURI string + meterIndex uint + + mu sync.RWMutex + + // capabilities + features []string + + // evse + evse warp.Evse + maxCurrent int64 // input from evcc + + // meter + meter warp.MeterValues + meterMap map[int]int + metersValueIDsTopic string + metersValuesTopic string + + // nfc + chargeTracker warp.ChargeTrackerCurrentCharge + + // power manager + pmState warp.PmState + pmLowLevelState warp.PmLowLevelState +} + +type warpEvent struct { + Topic string `json:"topic"` + Payload json.RawMessage `json:"payload"` +} + +var _ api.ChargerEx = (*WarpWS)(nil) + +func init() { + registry.AddCtx("warp-ws", NewWarpWSFromConfig) +} + +//go:generate go tool decorate -f decorateWarpWS -b *WarpWS -r api.Charger -t "api.Meter,CurrentPower,func() (float64, error)" -t "api.MeterEnergy,TotalEnergy,func() (float64, error)" -t "api.PhaseCurrents,Currents,func() (float64, float64, float64, error)" -t "api.PhaseVoltages,Voltages,func() (float64, float64, float64, error)" -t "api.Identifier,Identify,func() (string, error)" -t "api.PhaseSwitcher,Phases1p3p,func(int) error" -t "api.PhaseGetter,GetPhases,func() (int, error)" + +func NewWarpWSFromConfig(ctx context.Context, other map[string]any) (api.Charger, error) { + var cc struct { + URI string + User string + Password string + EnergyManagerURI string + EnergyManagerUser string + EnergyManagerPassword string + DisablePhaseAutoSwitch bool + EnergyMeterIndex uint + } + + if err := util.DecodeOther(other, &cc); err != nil { + return nil, err + } + + wb, err := NewWarpWS(ctx, cc.URI, cc.User, cc.Password, cc.EnergyMeterIndex) + if err != nil { + return nil, err + } + + // Feature: Meter -> Meter is legacy API, Meters is the new API + var currentPower, totalEnergy func() (float64, error) + if wb.hasFeature(warp.FeatureMeter) || wb.hasFeature(warp.FeatureMeters) { + currentPower = wb.currentPower + totalEnergy = wb.totalEnergy + } + + // Feature: Phases + var currents, voltages func() (float64, float64, float64, error) + if wb.hasFeature(warp.FeatureMeters) || wb.hasFeature(warp.FeatureMeterAllValues) { + currents = wb.currents + voltages = wb.voltages + } + + if wb.hasFeature(warp.FeaturePhaseSwitch) && wb.emURI == "" { + wb.emURI = wb.uri + wb.emHelper = wb.Helper + } else if cc.EnergyManagerURI != "" { // fallback to Energy Manager + wb.emURI, err = parseURI(cc.EnergyManagerURI, false) + if wb.emURI == "" { + return nil, err + } else if err != nil { + wb.log.DEBUG.Println(err) + } + wb.emHelper = request.NewHelper(wb.log) + if cc.EnergyManagerUser != "" { + wb.emHelper.Client.Transport = digest.NewTransport(cc.EnergyManagerUser, cc.EnergyManagerPassword, wb.emHelper.Client.Transport) + } + } + + // Feature: NFC + var identify func() (string, error) + if wb.hasFeature(warp.FeatureNfc) { + identify = wb.identify + } + + // Feature: EM + var phases func(int) error + var getPhases func() (int, error) + if wb.pmState.ExternalControl != 1 { + phases = wb.phases1p3p + getPhases = wb.getPhases + } + + // Phase Auto Switching needs to be disabled for WARP3 + // Necessary if charging 1p only vehicles + if cc.DisablePhaseAutoSwitch { + // unfortunately no feature to check for, instead this is set in template + if err := wb.disablePhaseAutoSwitch(); err != nil { + return nil, err + } + wb.log.TRACE.Println("disabled phase auto switching") + } + + return decorateWarpWS(wb, currentPower, totalEnergy, currents, voltages, identify, phases, getPhases), nil +} + +func NewWarpWS(ctx context.Context, uri, user, password string, meterIndex uint) (*WarpWS, error) { + log := util.NewLogger("warp-ws") + + client := request.NewHelper(log) + if user != "" { + client.Client.Transport = digest.NewTransport(user, password, client.Client.Transport) + } + + w := &WarpWS{ + Helper: client, log: log, + uri: util.DefaultScheme(uri, "http"), + meterIndex: meterIndex, + meterMap: map[int]int{}, + metersValueIDsTopic: fmt.Sprintf("meters/%d/value_ids", meterIndex), + metersValuesTopic: fmt.Sprintf("meters/%d/values", meterIndex), + } + + go w.run(ctx) + + return w, nil +} + +func (w *WarpWS) run(ctx context.Context) { + uri, err := parseURI(w.uri, true) + if err != nil { + w.log.DEBUG.Println(err) + if uri == "" { + return + } + } + w.log.TRACE.Printf("connecting to %s …", uri) + + bo := backoff.NewExponentialBackOff(backoff.WithMaxElapsedTime(0)) + for ctx.Err() == nil { + conn, _, err := websocket.Dial(ctx, uri, nil) + if err != nil { + if ctx.Err() != nil { + return + } + time.Sleep(bo.NextBackOff()) + continue + } + + bo.Reset() + if err := w.handleConnection(ctx, conn); err != nil { + w.log.ERROR.Println(err) + } + } +} + +func parseURI(uri string, toWS bool) (string, error) { + u, err := url.Parse(util.DefaultScheme(strings.TrimRight(uri, "/"), "http")) + if err != nil { + return "", err + } + if u.Scheme == "https" || u.Scheme == "wss" { + u.Scheme = "http" + err = fmt.Errorf("https or wss are not supported, using http/ws instead") + } + if toWS { + u.Scheme = "ws" + u.Path = path.Join(u.Path, "/ws") + } + return u.String(), err +} + +func (w *WarpWS) handleConnection(ctx context.Context, conn *websocket.Conn) error { + defer conn.Close(websocket.StatusInternalError, "reconnect") + for { + msgType, r, err := conn.Reader(ctx) + if err != nil { + return err + } + if msgType != websocket.MessageText { + continue // next frame + } + + dec := json.NewDecoder(r) + for { + var event warpEvent + if err := dec.Decode(&event); err != nil { + if errors.Is(err, io.EOF) { + break //next frame + } + return err + } + + if err := w.handleEvent(event.Topic, event.Payload); err != nil { + w.log.ERROR.Printf("bad payload for topic %s: %v", event.Topic, err) + } + } + } +} + +func (w *WarpWS) handleEvent(topic string, payload json.RawMessage) error { + w.mu.Lock() + defer w.mu.Unlock() + + var err error + switch topic { + case "charge_tracker/current_charge": + err = json.Unmarshal(payload, &w.chargeTracker) + case "evse/external_current": + err = json.Unmarshal(payload, &w.evse.ExternalCurrent) + case "evse/user_current": + err = json.Unmarshal(payload, &w.evse.UserCurrent) + case "evse/user_enabled": + err = json.Unmarshal(payload, &w.evse.UserEnabled) + case "evse/state": + err = json.Unmarshal(payload, &w.evse.State) + case "meter/all_values": + if !slices.Contains(w.features, warp.FeatureMeters) { + return nil + } + err = json.Unmarshal(payload, &w.meter.TmpValues) + if len(w.meter.TmpValues) > 5 { + copy(w.meter.Voltages[:], w.meter.TmpValues[:3]) + copy(w.meter.Currents[:], w.meter.TmpValues[3:6]) + } + case "meter/values": + if !slices.Contains(w.features, warp.FeatureMeters) { + return nil + } + err = json.Unmarshal(payload, &w.meter) + case w.metersValueIDsTopic: + var ids []int + if err = json.Unmarshal(payload, &ids); err != nil { + return err + } + w.meterMap = make(map[int]int, len(ids)) + for i, id := range ids { + w.meterMap[id] = i + } + case w.metersValuesTopic: + if err := json.Unmarshal(payload, &w.meter.TmpValues); err != nil { + return err + } + + get := func(id int) (float64, bool) { + if idx, ok := w.meterMap[id]; ok && idx < len(w.meter.TmpValues) { + return w.meter.TmpValues[idx], true + } + return 0, false + } + + s := warp.DefaultSchema + if v, ok := get(s.PowerID); ok { + w.meter.Power = v + } + if v, ok := get(s.EnergyAbsID); ok { + w.meter.EnergyAbs = v + } + for p, ids := range s.Phases { + if v, ok := get(ids.CurrentID); ok { + w.meter.Currents[p] = v + } + if v, ok := get(ids.VoltageID); ok { + w.meter.Voltages[p] = v + } + } + case "power_manager/state": + err = json.Unmarshal(payload, &w.pmState) + case "power_manager/low_level_state": + err = json.Unmarshal(payload, &w.pmLowLevelState) + } + return err +} + +func (w *WarpWS) hasFeature(feature string) bool { + w.mu.RLock() + if w.features != nil { + w.mu.RUnlock() + return slices.Contains(w.features, feature) + } + uri := fmt.Sprintf("%s/info/features", w.uri) + w.mu.RUnlock() + + var f []string + if err := w.GetJSON(uri, &f); err == nil { + w.mu.Lock() + w.features = f + w.mu.Unlock() + return slices.Contains(f, feature) + } + + return false +} + +func (w *WarpWS) Enable(enable bool) error { + var curr int64 + if enable { + w.mu.RLock() + curr = w.maxCurrent + w.mu.RUnlock() + } + return w.setCurrent(curr) +} + +func (w *WarpWS) Enabled() (bool, error) { + w.mu.RLock() + defer w.mu.RUnlock() + return w.evse.ExternalCurrent.Current > 0, nil +} + +// MaxCurrent implements the api.Charger interface +func (w *WarpWS) MaxCurrent(current int64) error { + return w.MaxCurrentMillis(float64(current)) +} + +// MaxCurrentMillis implements the api.ChargerEx interface +func (w *WarpWS) MaxCurrentMillis(current float64) error { + curr := int64(current * 1e3) + err := w.setCurrent(curr) + if err == nil { + w.mu.Lock() + w.maxCurrent = curr + w.mu.Unlock() + } + return err +} + +func (w *WarpWS) statusFromEvseStatus(state int) (api.ChargeStatus, error) { + if state < 3 { + return []api.ChargeStatus{api.StatusA, api.StatusB, api.StatusC}[state], nil + } + return api.StatusNone, fmt.Errorf("unknown evse status: %d", state) +} + +func (w *WarpWS) Status() (api.ChargeStatus, error) { + w.mu.RLock() + defer w.mu.RUnlock() + return w.statusFromEvseStatus(w.evse.State.Iec61851State) +} + +func (w *WarpWS) StatusReason() (api.Reason, error) { + w.mu.RLock() + defer w.mu.RUnlock() + if status, err := w.statusFromEvseStatus(w.evse.State.Iec61851State); err != nil { + return api.ReasonUnknown, err + } else if status == api.StatusB && w.evse.UserEnabled.Enabled && w.evse.UserCurrent.Current == 0 { + return api.ReasonWaitingForAuthorization, nil + } + return api.ReasonUnknown, nil +} + +func (w *WarpWS) currentPower() (float64, error) { + w.mu.RLock() + defer w.mu.RUnlock() + return w.meter.Power, nil +} + +func (w *WarpWS) totalEnergy() (float64, error) { + w.mu.RLock() + defer w.mu.RUnlock() + return w.meter.EnergyAbs, nil +} + +func (w *WarpWS) currents() (float64, float64, float64, error) { + w.mu.RLock() + defer w.mu.RUnlock() + return w.meter.Currents[0], w.meter.Currents[1], w.meter.Currents[2], nil +} + +func (w *WarpWS) voltages() (float64, float64, float64, error) { + w.mu.RLock() + defer w.mu.RUnlock() + return w.meter.Voltages[0], w.meter.Voltages[1], w.meter.Voltages[2], nil +} + +func (w *WarpWS) identify() (string, error) { + w.mu.RLock() + defer w.mu.RUnlock() + return w.chargeTracker.AuthorizationInfo.TagId, nil +} + +func (w *WarpWS) setCurrent(curr int64) error { + uri := fmt.Sprintf("%s/evse/external_current", w.uri) + req, _ := request.New(http.MethodPost, uri, request.MarshalJSON(map[string]int64{"current": curr}), request.JSONEncoding) + _, err := w.Do(req) + return err +} + +func (w *WarpWS) disablePhaseAutoSwitch() error { + uri := fmt.Sprintf("%s/evse/phase_auto_switch", w.uri) + req, _ := request.New(http.MethodPost, uri, request.MarshalJSON(map[string]bool{"enabled": false}), request.JSONEncoding) + _, err := w.Do(req) + return err +} + +// phases1p3p implements the api.PhaseSwitcher interface +func (w *WarpWS) phases1p3p(phases int) error { + if ec, err := w.ensurePmState(); err != nil || ec.ExternalControl > warp.ExternalControlAvailable { + return fmt.Errorf("external control not available: %d", ec.ExternalControl) + } + w.mu.RLock() + em := w.emHelper + uri := fmt.Sprintf("%s/power_manager/external_control", w.emURI) + w.mu.RUnlock() + + req, _ := request.New(http.MethodPost, uri, request.MarshalJSON(map[string]int{"phases_wanted": phases}), request.JSONEncoding) + + if em != nil { + _, err := em.Do(req) + return err + } + + _, err := w.Do(req) + return err +} + +// getPhases implements the api.PhaseGetter interface +func (w *WarpWS) getPhases() (int, error) { + s, err := w.ensurePmLowLevelState() + if err != nil { + return 0, err + } + + if s.Is3phase { + return 3, nil + } + return 1, nil +} + +func (w *WarpWS) ensurePmLowLevelState() (warp.PmLowLevelState, error) { + w.mu.RLock() + s := w.pmLowLevelState + em := w.emHelper + uri := w.emURI + w.mu.RUnlock() + if em == nil || uri == "" { + return s, nil + } + + var ns warp.PmLowLevelState + if err := em.GetJSON(fmt.Sprintf("%s/power_manager/low_level_state", uri), &ns); err != nil { + return warp.PmLowLevelState{}, err + } + + w.mu.Lock() + w.pmLowLevelState = ns + w.mu.Unlock() + + return ns, nil +} + +func (w *WarpWS) ensurePmState() (warp.PmState, error) { + w.mu.RLock() + s := w.pmState + em := w.emHelper + uri := w.emURI + w.mu.RUnlock() + + if em == nil || uri == "" || s.ExternalControl != 0 { + return s, nil + } + + var ns warp.PmState + if err := em.GetJSON(fmt.Sprintf("%s/power_manager/state", uri), &ns); err != nil { + return warp.PmState{}, err + } + + w.mu.Lock() + w.pmState = ns + w.mu.Unlock() + + return ns, nil +} diff --git a/charger/warp-ws_decorators.go b/charger/warp-ws_decorators.go new file mode 100644 index 000000000..0d77aa229 --- /dev/null +++ b/charger/warp-ws_decorators.go @@ -0,0 +1,1247 @@ +package charger + +// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT. + +import ( + "github.com/evcc-io/evcc/api" +) + +func decorateWarpWS(base *WarpWS, 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: + return base + + case identifier == nil && meter != nil && meterEnergy == nil && phaseCurrents == nil && phaseSwitcher == nil && phaseVoltages == nil: + return &struct { + *WarpWS + api.Meter + }{ + WarpWS: base, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + } + + case identifier == nil && meter != nil && meterEnergy != nil && phaseCurrents == nil && phaseSwitcher == nil && phaseVoltages == nil: + return &struct { + *WarpWS + api.Meter + api.MeterEnergy + }{ + WarpWS: base, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + MeterEnergy: &decorateWarpWSMeterEnergyImpl{ + meterEnergy: meterEnergy, + }, + } + + case identifier == nil && meter != nil && meterEnergy == nil && phaseCurrents != nil && phaseSwitcher == nil && phaseVoltages == nil: + return &struct { + *WarpWS + api.Meter + api.PhaseCurrents + }{ + WarpWS: base, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + PhaseCurrents: &decorateWarpWSPhaseCurrentsImpl{ + phaseCurrents: phaseCurrents, + }, + } + + case identifier == nil && meter != nil && meterEnergy != nil && phaseCurrents != nil && phaseSwitcher == nil && phaseVoltages == nil: + return &struct { + *WarpWS + api.Meter + api.MeterEnergy + api.PhaseCurrents + }{ + WarpWS: base, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + MeterEnergy: &decorateWarpWSMeterEnergyImpl{ + meterEnergy: meterEnergy, + }, + PhaseCurrents: &decorateWarpWSPhaseCurrentsImpl{ + phaseCurrents: phaseCurrents, + }, + } + + case identifier == nil && meter != nil && meterEnergy == nil && phaseCurrents == nil && phaseSwitcher == nil && phaseVoltages != nil: + return &struct { + *WarpWS + api.Meter + api.PhaseVoltages + }{ + WarpWS: base, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + PhaseVoltages: &decorateWarpWSPhaseVoltagesImpl{ + phaseVoltages: phaseVoltages, + }, + } + + case identifier == nil && meter != nil && meterEnergy != nil && phaseCurrents == nil && phaseSwitcher == nil && phaseVoltages != nil: + return &struct { + *WarpWS + api.Meter + api.MeterEnergy + api.PhaseVoltages + }{ + WarpWS: base, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + MeterEnergy: &decorateWarpWSMeterEnergyImpl{ + meterEnergy: meterEnergy, + }, + PhaseVoltages: &decorateWarpWSPhaseVoltagesImpl{ + phaseVoltages: phaseVoltages, + }, + } + + case identifier == nil && meter != nil && meterEnergy == nil && phaseCurrents != nil && phaseSwitcher == nil && phaseVoltages != nil: + return &struct { + *WarpWS + api.Meter + api.PhaseCurrents + api.PhaseVoltages + }{ + WarpWS: base, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + PhaseCurrents: &decorateWarpWSPhaseCurrentsImpl{ + phaseCurrents: phaseCurrents, + }, + PhaseVoltages: &decorateWarpWSPhaseVoltagesImpl{ + phaseVoltages: phaseVoltages, + }, + } + + case identifier == nil && meter != nil && meterEnergy != nil && phaseCurrents != nil && phaseSwitcher == nil && phaseVoltages != nil: + return &struct { + *WarpWS + api.Meter + api.MeterEnergy + api.PhaseCurrents + api.PhaseVoltages + }{ + WarpWS: base, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + MeterEnergy: &decorateWarpWSMeterEnergyImpl{ + meterEnergy: meterEnergy, + }, + PhaseCurrents: &decorateWarpWSPhaseCurrentsImpl{ + phaseCurrents: phaseCurrents, + }, + PhaseVoltages: &decorateWarpWSPhaseVoltagesImpl{ + phaseVoltages: phaseVoltages, + }, + } + + case identifier != nil && meter == nil && phaseSwitcher == nil: + return &struct { + *WarpWS + api.Identifier + }{ + WarpWS: base, + Identifier: &decorateWarpWSIdentifierImpl{ + identifier: identifier, + }, + } + + case identifier != nil && meter != nil && meterEnergy == nil && phaseCurrents == nil && phaseSwitcher == nil && phaseVoltages == nil: + return &struct { + *WarpWS + api.Identifier + api.Meter + }{ + WarpWS: base, + Identifier: &decorateWarpWSIdentifierImpl{ + identifier: identifier, + }, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + } + + case identifier != nil && meter != nil && meterEnergy != nil && phaseCurrents == nil && phaseSwitcher == nil && phaseVoltages == nil: + return &struct { + *WarpWS + api.Identifier + api.Meter + api.MeterEnergy + }{ + WarpWS: base, + Identifier: &decorateWarpWSIdentifierImpl{ + identifier: identifier, + }, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + MeterEnergy: &decorateWarpWSMeterEnergyImpl{ + meterEnergy: meterEnergy, + }, + } + + case identifier != nil && meter != nil && meterEnergy == nil && phaseCurrents != nil && phaseSwitcher == nil && phaseVoltages == nil: + return &struct { + *WarpWS + api.Identifier + api.Meter + api.PhaseCurrents + }{ + WarpWS: base, + Identifier: &decorateWarpWSIdentifierImpl{ + identifier: identifier, + }, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + PhaseCurrents: &decorateWarpWSPhaseCurrentsImpl{ + phaseCurrents: phaseCurrents, + }, + } + + case identifier != nil && meter != nil && meterEnergy != nil && phaseCurrents != nil && phaseSwitcher == nil && phaseVoltages == nil: + return &struct { + *WarpWS + api.Identifier + api.Meter + api.MeterEnergy + api.PhaseCurrents + }{ + WarpWS: base, + Identifier: &decorateWarpWSIdentifierImpl{ + identifier: identifier, + }, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + MeterEnergy: &decorateWarpWSMeterEnergyImpl{ + meterEnergy: meterEnergy, + }, + PhaseCurrents: &decorateWarpWSPhaseCurrentsImpl{ + phaseCurrents: phaseCurrents, + }, + } + + case identifier != nil && meter != nil && meterEnergy == nil && phaseCurrents == nil && phaseSwitcher == nil && phaseVoltages != nil: + return &struct { + *WarpWS + api.Identifier + api.Meter + api.PhaseVoltages + }{ + WarpWS: base, + Identifier: &decorateWarpWSIdentifierImpl{ + identifier: identifier, + }, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + PhaseVoltages: &decorateWarpWSPhaseVoltagesImpl{ + phaseVoltages: phaseVoltages, + }, + } + + case identifier != nil && meter != nil && meterEnergy != nil && phaseCurrents == nil && phaseSwitcher == nil && phaseVoltages != nil: + return &struct { + *WarpWS + api.Identifier + api.Meter + api.MeterEnergy + api.PhaseVoltages + }{ + WarpWS: base, + Identifier: &decorateWarpWSIdentifierImpl{ + identifier: identifier, + }, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + MeterEnergy: &decorateWarpWSMeterEnergyImpl{ + meterEnergy: meterEnergy, + }, + PhaseVoltages: &decorateWarpWSPhaseVoltagesImpl{ + phaseVoltages: phaseVoltages, + }, + } + + case identifier != nil && meter != nil && meterEnergy == nil && phaseCurrents != nil && phaseSwitcher == nil && phaseVoltages != nil: + return &struct { + *WarpWS + api.Identifier + api.Meter + api.PhaseCurrents + api.PhaseVoltages + }{ + WarpWS: base, + Identifier: &decorateWarpWSIdentifierImpl{ + identifier: identifier, + }, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + PhaseCurrents: &decorateWarpWSPhaseCurrentsImpl{ + phaseCurrents: phaseCurrents, + }, + PhaseVoltages: &decorateWarpWSPhaseVoltagesImpl{ + phaseVoltages: phaseVoltages, + }, + } + + case identifier != nil && meter != nil && meterEnergy != nil && phaseCurrents != nil && phaseSwitcher == nil && phaseVoltages != nil: + return &struct { + *WarpWS + api.Identifier + api.Meter + api.MeterEnergy + api.PhaseCurrents + api.PhaseVoltages + }{ + WarpWS: base, + Identifier: &decorateWarpWSIdentifierImpl{ + identifier: identifier, + }, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + MeterEnergy: &decorateWarpWSMeterEnergyImpl{ + meterEnergy: meterEnergy, + }, + PhaseCurrents: &decorateWarpWSPhaseCurrentsImpl{ + phaseCurrents: phaseCurrents, + }, + PhaseVoltages: &decorateWarpWSPhaseVoltagesImpl{ + phaseVoltages: phaseVoltages, + }, + } + + case identifier == nil && meter == nil && phaseGetter == nil && phaseSwitcher != nil: + return &struct { + *WarpWS + api.PhaseSwitcher + }{ + WarpWS: base, + PhaseSwitcher: &decorateWarpWSPhaseSwitcherImpl{ + phaseSwitcher: phaseSwitcher, + }, + } + + case identifier == nil && meter != nil && meterEnergy == nil && phaseCurrents == nil && phaseGetter == nil && phaseSwitcher != nil && phaseVoltages == nil: + return &struct { + *WarpWS + api.Meter + api.PhaseSwitcher + }{ + WarpWS: base, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + PhaseSwitcher: &decorateWarpWSPhaseSwitcherImpl{ + phaseSwitcher: phaseSwitcher, + }, + } + + case identifier == nil && meter != nil && meterEnergy != nil && phaseCurrents == nil && phaseGetter == nil && phaseSwitcher != nil && phaseVoltages == nil: + return &struct { + *WarpWS + api.Meter + api.MeterEnergy + api.PhaseSwitcher + }{ + WarpWS: base, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + MeterEnergy: &decorateWarpWSMeterEnergyImpl{ + meterEnergy: meterEnergy, + }, + PhaseSwitcher: &decorateWarpWSPhaseSwitcherImpl{ + phaseSwitcher: phaseSwitcher, + }, + } + + case identifier == nil && meter != nil && meterEnergy == nil && phaseCurrents != nil && phaseGetter == nil && phaseSwitcher != nil && phaseVoltages == nil: + return &struct { + *WarpWS + api.Meter + api.PhaseCurrents + api.PhaseSwitcher + }{ + WarpWS: base, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + PhaseCurrents: &decorateWarpWSPhaseCurrentsImpl{ + phaseCurrents: phaseCurrents, + }, + PhaseSwitcher: &decorateWarpWSPhaseSwitcherImpl{ + phaseSwitcher: phaseSwitcher, + }, + } + + case identifier == nil && meter != nil && meterEnergy != nil && phaseCurrents != nil && phaseGetter == nil && phaseSwitcher != nil && phaseVoltages == nil: + return &struct { + *WarpWS + api.Meter + api.MeterEnergy + api.PhaseCurrents + api.PhaseSwitcher + }{ + WarpWS: base, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + MeterEnergy: &decorateWarpWSMeterEnergyImpl{ + meterEnergy: meterEnergy, + }, + PhaseCurrents: &decorateWarpWSPhaseCurrentsImpl{ + phaseCurrents: phaseCurrents, + }, + PhaseSwitcher: &decorateWarpWSPhaseSwitcherImpl{ + phaseSwitcher: phaseSwitcher, + }, + } + + case identifier == nil && meter != nil && meterEnergy == nil && phaseCurrents == nil && phaseGetter == nil && phaseSwitcher != nil && phaseVoltages != nil: + return &struct { + *WarpWS + api.Meter + api.PhaseSwitcher + api.PhaseVoltages + }{ + WarpWS: base, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + PhaseSwitcher: &decorateWarpWSPhaseSwitcherImpl{ + phaseSwitcher: phaseSwitcher, + }, + PhaseVoltages: &decorateWarpWSPhaseVoltagesImpl{ + phaseVoltages: phaseVoltages, + }, + } + + case identifier == nil && meter != nil && meterEnergy != nil && phaseCurrents == nil && phaseGetter == nil && phaseSwitcher != nil && phaseVoltages != nil: + return &struct { + *WarpWS + api.Meter + api.MeterEnergy + api.PhaseSwitcher + api.PhaseVoltages + }{ + WarpWS: base, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + MeterEnergy: &decorateWarpWSMeterEnergyImpl{ + meterEnergy: meterEnergy, + }, + PhaseSwitcher: &decorateWarpWSPhaseSwitcherImpl{ + phaseSwitcher: phaseSwitcher, + }, + PhaseVoltages: &decorateWarpWSPhaseVoltagesImpl{ + phaseVoltages: phaseVoltages, + }, + } + + case identifier == nil && meter != nil && meterEnergy == nil && phaseCurrents != nil && phaseGetter == nil && phaseSwitcher != nil && phaseVoltages != nil: + return &struct { + *WarpWS + api.Meter + api.PhaseCurrents + api.PhaseSwitcher + api.PhaseVoltages + }{ + WarpWS: base, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + PhaseCurrents: &decorateWarpWSPhaseCurrentsImpl{ + phaseCurrents: phaseCurrents, + }, + PhaseSwitcher: &decorateWarpWSPhaseSwitcherImpl{ + phaseSwitcher: phaseSwitcher, + }, + PhaseVoltages: &decorateWarpWSPhaseVoltagesImpl{ + phaseVoltages: phaseVoltages, + }, + } + + case identifier == nil && meter != nil && meterEnergy != nil && phaseCurrents != nil && phaseGetter == nil && phaseSwitcher != nil && phaseVoltages != nil: + return &struct { + *WarpWS + api.Meter + api.MeterEnergy + api.PhaseCurrents + api.PhaseSwitcher + api.PhaseVoltages + }{ + WarpWS: base, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + MeterEnergy: &decorateWarpWSMeterEnergyImpl{ + meterEnergy: meterEnergy, + }, + PhaseCurrents: &decorateWarpWSPhaseCurrentsImpl{ + phaseCurrents: phaseCurrents, + }, + PhaseSwitcher: &decorateWarpWSPhaseSwitcherImpl{ + phaseSwitcher: phaseSwitcher, + }, + PhaseVoltages: &decorateWarpWSPhaseVoltagesImpl{ + phaseVoltages: phaseVoltages, + }, + } + + case identifier != nil && meter == nil && phaseGetter == nil && phaseSwitcher != nil: + return &struct { + *WarpWS + api.Identifier + api.PhaseSwitcher + }{ + WarpWS: base, + Identifier: &decorateWarpWSIdentifierImpl{ + identifier: identifier, + }, + PhaseSwitcher: &decorateWarpWSPhaseSwitcherImpl{ + phaseSwitcher: phaseSwitcher, + }, + } + + case identifier != nil && meter != nil && meterEnergy == nil && phaseCurrents == nil && phaseGetter == nil && phaseSwitcher != nil && phaseVoltages == nil: + return &struct { + *WarpWS + api.Identifier + api.Meter + api.PhaseSwitcher + }{ + WarpWS: base, + Identifier: &decorateWarpWSIdentifierImpl{ + identifier: identifier, + }, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + PhaseSwitcher: &decorateWarpWSPhaseSwitcherImpl{ + phaseSwitcher: phaseSwitcher, + }, + } + + case identifier != nil && meter != nil && meterEnergy != nil && phaseCurrents == nil && phaseGetter == nil && phaseSwitcher != nil && phaseVoltages == nil: + return &struct { + *WarpWS + api.Identifier + api.Meter + api.MeterEnergy + api.PhaseSwitcher + }{ + WarpWS: base, + Identifier: &decorateWarpWSIdentifierImpl{ + identifier: identifier, + }, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + MeterEnergy: &decorateWarpWSMeterEnergyImpl{ + meterEnergy: meterEnergy, + }, + PhaseSwitcher: &decorateWarpWSPhaseSwitcherImpl{ + phaseSwitcher: phaseSwitcher, + }, + } + + case identifier != nil && meter != nil && meterEnergy == nil && phaseCurrents != nil && phaseGetter == nil && phaseSwitcher != nil && phaseVoltages == nil: + return &struct { + *WarpWS + api.Identifier + api.Meter + api.PhaseCurrents + api.PhaseSwitcher + }{ + WarpWS: base, + Identifier: &decorateWarpWSIdentifierImpl{ + identifier: identifier, + }, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + PhaseCurrents: &decorateWarpWSPhaseCurrentsImpl{ + phaseCurrents: phaseCurrents, + }, + PhaseSwitcher: &decorateWarpWSPhaseSwitcherImpl{ + phaseSwitcher: phaseSwitcher, + }, + } + + case identifier != nil && meter != nil && meterEnergy != nil && phaseCurrents != nil && phaseGetter == nil && phaseSwitcher != nil && phaseVoltages == nil: + return &struct { + *WarpWS + api.Identifier + api.Meter + api.MeterEnergy + api.PhaseCurrents + api.PhaseSwitcher + }{ + WarpWS: base, + Identifier: &decorateWarpWSIdentifierImpl{ + identifier: identifier, + }, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + MeterEnergy: &decorateWarpWSMeterEnergyImpl{ + meterEnergy: meterEnergy, + }, + PhaseCurrents: &decorateWarpWSPhaseCurrentsImpl{ + phaseCurrents: phaseCurrents, + }, + PhaseSwitcher: &decorateWarpWSPhaseSwitcherImpl{ + phaseSwitcher: phaseSwitcher, + }, + } + + case identifier != nil && meter != nil && meterEnergy == nil && phaseCurrents == nil && phaseGetter == nil && phaseSwitcher != nil && phaseVoltages != nil: + return &struct { + *WarpWS + api.Identifier + api.Meter + api.PhaseSwitcher + api.PhaseVoltages + }{ + WarpWS: base, + Identifier: &decorateWarpWSIdentifierImpl{ + identifier: identifier, + }, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + PhaseSwitcher: &decorateWarpWSPhaseSwitcherImpl{ + phaseSwitcher: phaseSwitcher, + }, + PhaseVoltages: &decorateWarpWSPhaseVoltagesImpl{ + phaseVoltages: phaseVoltages, + }, + } + + case identifier != nil && meter != nil && meterEnergy != nil && phaseCurrents == nil && phaseGetter == nil && phaseSwitcher != nil && phaseVoltages != nil: + return &struct { + *WarpWS + api.Identifier + api.Meter + api.MeterEnergy + api.PhaseSwitcher + api.PhaseVoltages + }{ + WarpWS: base, + Identifier: &decorateWarpWSIdentifierImpl{ + identifier: identifier, + }, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + MeterEnergy: &decorateWarpWSMeterEnergyImpl{ + meterEnergy: meterEnergy, + }, + PhaseSwitcher: &decorateWarpWSPhaseSwitcherImpl{ + phaseSwitcher: phaseSwitcher, + }, + PhaseVoltages: &decorateWarpWSPhaseVoltagesImpl{ + phaseVoltages: phaseVoltages, + }, + } + + case identifier != nil && meter != nil && meterEnergy == nil && phaseCurrents != nil && phaseGetter == nil && phaseSwitcher != nil && phaseVoltages != nil: + return &struct { + *WarpWS + api.Identifier + api.Meter + api.PhaseCurrents + api.PhaseSwitcher + api.PhaseVoltages + }{ + WarpWS: base, + Identifier: &decorateWarpWSIdentifierImpl{ + identifier: identifier, + }, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + PhaseCurrents: &decorateWarpWSPhaseCurrentsImpl{ + phaseCurrents: phaseCurrents, + }, + PhaseSwitcher: &decorateWarpWSPhaseSwitcherImpl{ + phaseSwitcher: phaseSwitcher, + }, + PhaseVoltages: &decorateWarpWSPhaseVoltagesImpl{ + phaseVoltages: phaseVoltages, + }, + } + + case identifier != nil && meter != nil && meterEnergy != nil && phaseCurrents != nil && phaseGetter == nil && phaseSwitcher != nil && phaseVoltages != nil: + return &struct { + *WarpWS + api.Identifier + api.Meter + api.MeterEnergy + api.PhaseCurrents + api.PhaseSwitcher + api.PhaseVoltages + }{ + WarpWS: base, + Identifier: &decorateWarpWSIdentifierImpl{ + identifier: identifier, + }, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + MeterEnergy: &decorateWarpWSMeterEnergyImpl{ + meterEnergy: meterEnergy, + }, + PhaseCurrents: &decorateWarpWSPhaseCurrentsImpl{ + phaseCurrents: phaseCurrents, + }, + PhaseSwitcher: &decorateWarpWSPhaseSwitcherImpl{ + phaseSwitcher: phaseSwitcher, + }, + PhaseVoltages: &decorateWarpWSPhaseVoltagesImpl{ + phaseVoltages: phaseVoltages, + }, + } + + case identifier == nil && meter == nil && phaseGetter != nil && phaseSwitcher != nil: + return &struct { + *WarpWS + api.PhaseGetter + api.PhaseSwitcher + }{ + WarpWS: base, + PhaseGetter: &decorateWarpWSPhaseGetterImpl{ + phaseGetter: phaseGetter, + }, + PhaseSwitcher: &decorateWarpWSPhaseSwitcherImpl{ + phaseSwitcher: phaseSwitcher, + }, + } + + case identifier == nil && meter != nil && meterEnergy == nil && phaseCurrents == nil && phaseGetter != nil && phaseSwitcher != nil && phaseVoltages == nil: + return &struct { + *WarpWS + api.Meter + api.PhaseGetter + api.PhaseSwitcher + }{ + WarpWS: base, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + PhaseGetter: &decorateWarpWSPhaseGetterImpl{ + phaseGetter: phaseGetter, + }, + PhaseSwitcher: &decorateWarpWSPhaseSwitcherImpl{ + phaseSwitcher: phaseSwitcher, + }, + } + + case identifier == nil && meter != nil && meterEnergy != nil && phaseCurrents == nil && phaseGetter != nil && phaseSwitcher != nil && phaseVoltages == nil: + return &struct { + *WarpWS + api.Meter + api.MeterEnergy + api.PhaseGetter + api.PhaseSwitcher + }{ + WarpWS: base, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + MeterEnergy: &decorateWarpWSMeterEnergyImpl{ + meterEnergy: meterEnergy, + }, + PhaseGetter: &decorateWarpWSPhaseGetterImpl{ + phaseGetter: phaseGetter, + }, + PhaseSwitcher: &decorateWarpWSPhaseSwitcherImpl{ + phaseSwitcher: phaseSwitcher, + }, + } + + case identifier == nil && meter != nil && meterEnergy == nil && phaseCurrents != nil && phaseGetter != nil && phaseSwitcher != nil && phaseVoltages == nil: + return &struct { + *WarpWS + api.Meter + api.PhaseCurrents + api.PhaseGetter + api.PhaseSwitcher + }{ + WarpWS: base, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + PhaseCurrents: &decorateWarpWSPhaseCurrentsImpl{ + phaseCurrents: phaseCurrents, + }, + PhaseGetter: &decorateWarpWSPhaseGetterImpl{ + phaseGetter: phaseGetter, + }, + PhaseSwitcher: &decorateWarpWSPhaseSwitcherImpl{ + phaseSwitcher: phaseSwitcher, + }, + } + + case identifier == nil && meter != nil && meterEnergy != nil && phaseCurrents != nil && phaseGetter != nil && phaseSwitcher != nil && phaseVoltages == nil: + return &struct { + *WarpWS + api.Meter + api.MeterEnergy + api.PhaseCurrents + api.PhaseGetter + api.PhaseSwitcher + }{ + WarpWS: base, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + MeterEnergy: &decorateWarpWSMeterEnergyImpl{ + meterEnergy: meterEnergy, + }, + PhaseCurrents: &decorateWarpWSPhaseCurrentsImpl{ + phaseCurrents: phaseCurrents, + }, + PhaseGetter: &decorateWarpWSPhaseGetterImpl{ + phaseGetter: phaseGetter, + }, + PhaseSwitcher: &decorateWarpWSPhaseSwitcherImpl{ + phaseSwitcher: phaseSwitcher, + }, + } + + case identifier == nil && meter != nil && meterEnergy == nil && phaseCurrents == nil && phaseGetter != nil && phaseSwitcher != nil && phaseVoltages != nil: + return &struct { + *WarpWS + api.Meter + api.PhaseGetter + api.PhaseSwitcher + api.PhaseVoltages + }{ + WarpWS: base, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + PhaseGetter: &decorateWarpWSPhaseGetterImpl{ + phaseGetter: phaseGetter, + }, + PhaseSwitcher: &decorateWarpWSPhaseSwitcherImpl{ + phaseSwitcher: phaseSwitcher, + }, + PhaseVoltages: &decorateWarpWSPhaseVoltagesImpl{ + phaseVoltages: phaseVoltages, + }, + } + + case identifier == nil && meter != nil && meterEnergy != nil && phaseCurrents == nil && phaseGetter != nil && phaseSwitcher != nil && phaseVoltages != nil: + return &struct { + *WarpWS + api.Meter + api.MeterEnergy + api.PhaseGetter + api.PhaseSwitcher + api.PhaseVoltages + }{ + WarpWS: base, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + MeterEnergy: &decorateWarpWSMeterEnergyImpl{ + meterEnergy: meterEnergy, + }, + PhaseGetter: &decorateWarpWSPhaseGetterImpl{ + phaseGetter: phaseGetter, + }, + PhaseSwitcher: &decorateWarpWSPhaseSwitcherImpl{ + phaseSwitcher: phaseSwitcher, + }, + PhaseVoltages: &decorateWarpWSPhaseVoltagesImpl{ + phaseVoltages: phaseVoltages, + }, + } + + case identifier == nil && meter != nil && meterEnergy == nil && phaseCurrents != nil && phaseGetter != nil && phaseSwitcher != nil && phaseVoltages != nil: + return &struct { + *WarpWS + api.Meter + api.PhaseCurrents + api.PhaseGetter + api.PhaseSwitcher + api.PhaseVoltages + }{ + WarpWS: base, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + PhaseCurrents: &decorateWarpWSPhaseCurrentsImpl{ + phaseCurrents: phaseCurrents, + }, + PhaseGetter: &decorateWarpWSPhaseGetterImpl{ + phaseGetter: phaseGetter, + }, + PhaseSwitcher: &decorateWarpWSPhaseSwitcherImpl{ + phaseSwitcher: phaseSwitcher, + }, + PhaseVoltages: &decorateWarpWSPhaseVoltagesImpl{ + phaseVoltages: phaseVoltages, + }, + } + + case identifier == nil && meter != nil && meterEnergy != nil && phaseCurrents != nil && phaseGetter != nil && phaseSwitcher != nil && phaseVoltages != nil: + return &struct { + *WarpWS + api.Meter + api.MeterEnergy + api.PhaseCurrents + api.PhaseGetter + api.PhaseSwitcher + api.PhaseVoltages + }{ + WarpWS: base, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + MeterEnergy: &decorateWarpWSMeterEnergyImpl{ + meterEnergy: meterEnergy, + }, + PhaseCurrents: &decorateWarpWSPhaseCurrentsImpl{ + phaseCurrents: phaseCurrents, + }, + PhaseGetter: &decorateWarpWSPhaseGetterImpl{ + phaseGetter: phaseGetter, + }, + PhaseSwitcher: &decorateWarpWSPhaseSwitcherImpl{ + phaseSwitcher: phaseSwitcher, + }, + PhaseVoltages: &decorateWarpWSPhaseVoltagesImpl{ + phaseVoltages: phaseVoltages, + }, + } + + case identifier != nil && meter == nil && phaseGetter != nil && phaseSwitcher != nil: + return &struct { + *WarpWS + api.Identifier + api.PhaseGetter + api.PhaseSwitcher + }{ + WarpWS: base, + Identifier: &decorateWarpWSIdentifierImpl{ + identifier: identifier, + }, + PhaseGetter: &decorateWarpWSPhaseGetterImpl{ + phaseGetter: phaseGetter, + }, + PhaseSwitcher: &decorateWarpWSPhaseSwitcherImpl{ + phaseSwitcher: phaseSwitcher, + }, + } + + case identifier != nil && meter != nil && meterEnergy == nil && phaseCurrents == nil && phaseGetter != nil && phaseSwitcher != nil && phaseVoltages == nil: + return &struct { + *WarpWS + api.Identifier + api.Meter + api.PhaseGetter + api.PhaseSwitcher + }{ + WarpWS: base, + Identifier: &decorateWarpWSIdentifierImpl{ + identifier: identifier, + }, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + PhaseGetter: &decorateWarpWSPhaseGetterImpl{ + phaseGetter: phaseGetter, + }, + PhaseSwitcher: &decorateWarpWSPhaseSwitcherImpl{ + phaseSwitcher: phaseSwitcher, + }, + } + + case identifier != nil && meter != nil && meterEnergy != nil && phaseCurrents == nil && phaseGetter != nil && phaseSwitcher != nil && phaseVoltages == nil: + return &struct { + *WarpWS + api.Identifier + api.Meter + api.MeterEnergy + api.PhaseGetter + api.PhaseSwitcher + }{ + WarpWS: base, + Identifier: &decorateWarpWSIdentifierImpl{ + identifier: identifier, + }, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + MeterEnergy: &decorateWarpWSMeterEnergyImpl{ + meterEnergy: meterEnergy, + }, + PhaseGetter: &decorateWarpWSPhaseGetterImpl{ + phaseGetter: phaseGetter, + }, + PhaseSwitcher: &decorateWarpWSPhaseSwitcherImpl{ + phaseSwitcher: phaseSwitcher, + }, + } + + case identifier != nil && meter != nil && meterEnergy == nil && phaseCurrents != nil && phaseGetter != nil && phaseSwitcher != nil && phaseVoltages == nil: + return &struct { + *WarpWS + api.Identifier + api.Meter + api.PhaseCurrents + api.PhaseGetter + api.PhaseSwitcher + }{ + WarpWS: base, + Identifier: &decorateWarpWSIdentifierImpl{ + identifier: identifier, + }, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + PhaseCurrents: &decorateWarpWSPhaseCurrentsImpl{ + phaseCurrents: phaseCurrents, + }, + PhaseGetter: &decorateWarpWSPhaseGetterImpl{ + phaseGetter: phaseGetter, + }, + PhaseSwitcher: &decorateWarpWSPhaseSwitcherImpl{ + phaseSwitcher: phaseSwitcher, + }, + } + + case identifier != nil && meter != nil && meterEnergy != nil && phaseCurrents != nil && phaseGetter != nil && phaseSwitcher != nil && phaseVoltages == nil: + return &struct { + *WarpWS + api.Identifier + api.Meter + api.MeterEnergy + api.PhaseCurrents + api.PhaseGetter + api.PhaseSwitcher + }{ + WarpWS: base, + Identifier: &decorateWarpWSIdentifierImpl{ + identifier: identifier, + }, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + MeterEnergy: &decorateWarpWSMeterEnergyImpl{ + meterEnergy: meterEnergy, + }, + PhaseCurrents: &decorateWarpWSPhaseCurrentsImpl{ + phaseCurrents: phaseCurrents, + }, + PhaseGetter: &decorateWarpWSPhaseGetterImpl{ + phaseGetter: phaseGetter, + }, + PhaseSwitcher: &decorateWarpWSPhaseSwitcherImpl{ + phaseSwitcher: phaseSwitcher, + }, + } + + case identifier != nil && meter != nil && meterEnergy == nil && phaseCurrents == nil && phaseGetter != nil && phaseSwitcher != nil && phaseVoltages != nil: + return &struct { + *WarpWS + api.Identifier + api.Meter + api.PhaseGetter + api.PhaseSwitcher + api.PhaseVoltages + }{ + WarpWS: base, + Identifier: &decorateWarpWSIdentifierImpl{ + identifier: identifier, + }, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + PhaseGetter: &decorateWarpWSPhaseGetterImpl{ + phaseGetter: phaseGetter, + }, + PhaseSwitcher: &decorateWarpWSPhaseSwitcherImpl{ + phaseSwitcher: phaseSwitcher, + }, + PhaseVoltages: &decorateWarpWSPhaseVoltagesImpl{ + phaseVoltages: phaseVoltages, + }, + } + + case identifier != nil && meter != nil && meterEnergy != nil && phaseCurrents == nil && phaseGetter != nil && phaseSwitcher != nil && phaseVoltages != nil: + return &struct { + *WarpWS + api.Identifier + api.Meter + api.MeterEnergy + api.PhaseGetter + api.PhaseSwitcher + api.PhaseVoltages + }{ + WarpWS: base, + Identifier: &decorateWarpWSIdentifierImpl{ + identifier: identifier, + }, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + MeterEnergy: &decorateWarpWSMeterEnergyImpl{ + meterEnergy: meterEnergy, + }, + PhaseGetter: &decorateWarpWSPhaseGetterImpl{ + phaseGetter: phaseGetter, + }, + PhaseSwitcher: &decorateWarpWSPhaseSwitcherImpl{ + phaseSwitcher: phaseSwitcher, + }, + PhaseVoltages: &decorateWarpWSPhaseVoltagesImpl{ + phaseVoltages: phaseVoltages, + }, + } + + case identifier != nil && meter != nil && meterEnergy == nil && phaseCurrents != nil && phaseGetter != nil && phaseSwitcher != nil && phaseVoltages != nil: + return &struct { + *WarpWS + api.Identifier + api.Meter + api.PhaseCurrents + api.PhaseGetter + api.PhaseSwitcher + api.PhaseVoltages + }{ + WarpWS: base, + Identifier: &decorateWarpWSIdentifierImpl{ + identifier: identifier, + }, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + PhaseCurrents: &decorateWarpWSPhaseCurrentsImpl{ + phaseCurrents: phaseCurrents, + }, + PhaseGetter: &decorateWarpWSPhaseGetterImpl{ + phaseGetter: phaseGetter, + }, + PhaseSwitcher: &decorateWarpWSPhaseSwitcherImpl{ + phaseSwitcher: phaseSwitcher, + }, + PhaseVoltages: &decorateWarpWSPhaseVoltagesImpl{ + phaseVoltages: phaseVoltages, + }, + } + + case identifier != nil && meter != nil && meterEnergy != nil && phaseCurrents != nil && phaseGetter != nil && phaseSwitcher != nil && phaseVoltages != nil: + return &struct { + *WarpWS + api.Identifier + api.Meter + api.MeterEnergy + api.PhaseCurrents + api.PhaseGetter + api.PhaseSwitcher + api.PhaseVoltages + }{ + WarpWS: base, + Identifier: &decorateWarpWSIdentifierImpl{ + identifier: identifier, + }, + Meter: &decorateWarpWSMeterImpl{ + meter: meter, + }, + MeterEnergy: &decorateWarpWSMeterEnergyImpl{ + meterEnergy: meterEnergy, + }, + PhaseCurrents: &decorateWarpWSPhaseCurrentsImpl{ + phaseCurrents: phaseCurrents, + }, + PhaseGetter: &decorateWarpWSPhaseGetterImpl{ + phaseGetter: phaseGetter, + }, + PhaseSwitcher: &decorateWarpWSPhaseSwitcherImpl{ + phaseSwitcher: phaseSwitcher, + }, + PhaseVoltages: &decorateWarpWSPhaseVoltagesImpl{ + phaseVoltages: phaseVoltages, + }, + } + } + + return nil +} + +type decorateWarpWSIdentifierImpl struct { + identifier func() (string, error) +} + +func (impl *decorateWarpWSIdentifierImpl) Identify() (string, error) { + return impl.identifier() +} + +type decorateWarpWSMeterImpl struct { + meter func() (float64, error) +} + +func (impl *decorateWarpWSMeterImpl) CurrentPower() (float64, error) { + return impl.meter() +} + +type decorateWarpWSMeterEnergyImpl struct { + meterEnergy func() (float64, error) +} + +func (impl *decorateWarpWSMeterEnergyImpl) TotalEnergy() (float64, error) { + return impl.meterEnergy() +} + +type decorateWarpWSPhaseCurrentsImpl struct { + phaseCurrents func() (float64, float64, float64, error) +} + +func (impl *decorateWarpWSPhaseCurrentsImpl) Currents() (float64, float64, float64, error) { + return impl.phaseCurrents() +} + +type decorateWarpWSPhaseGetterImpl struct { + phaseGetter func() (int, error) +} + +func (impl *decorateWarpWSPhaseGetterImpl) GetPhases() (int, error) { + return impl.phaseGetter() +} + +type decorateWarpWSPhaseSwitcherImpl struct { + phaseSwitcher func(int) error +} + +func (impl *decorateWarpWSPhaseSwitcherImpl) Phases1p3p(p0 int) error { + return impl.phaseSwitcher(p0) +} + +type decorateWarpWSPhaseVoltagesImpl struct { + phaseVoltages func() (float64, float64, float64, error) +} + +func (impl *decorateWarpWSPhaseVoltagesImpl) Voltages() (float64, float64, float64, error) { + return impl.phaseVoltages() +} diff --git a/charger/warp/types.go b/charger/warp/types.go index 82f3c2500..bba36d19d 100644 --- a/charger/warp/types.go +++ b/charger/warp/types.go @@ -2,91 +2,71 @@ package warp const ( FeatureMeter = "meter" + FeatureMeters = "meters" FeatureMeterAllValues = "meter_all_values" FeatureMeterPhases = "meter_phases" FeatureNfc = "nfc" + FeaturePhaseSwitch = "phase_switch" ) // https://www.warp-charger.com/api.html#evse_state type EvseState struct { - Iec61851State int `json:"iec61851_state"` - ChargerState int `json:"charger_state"` - ContactorState int `json:"contactor_state"` - ContactorError int `json:"contactor_error"` - AllowedChargingCurrent int64 `json:"allowed_charging_current"` - ErrorState int `json:"error_state"` - LockState int `json:"lock_state"` + Iec61851State int `json:"iec61851_state"` } 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 +type EvseUserEnabled struct { + Enabled bool `json:"enabled"` } -// https://www.warp-charger.com/api.html#meter_state -type MeterState struct { - State int `json:"state"` // Warp 1 only +type Evse struct { + State EvseState + ExternalCurrent EvseExternalCurrent + UserCurrent EvseExternalCurrent + UserEnabled EvseUserEnabled } -// https://www.warp-charger.com/api.html#meter_values type MeterValues struct { - Power float64 `json:"power"` - EnergyRel float64 `json:"energy_rel"` - EnergyAbs float64 `json:"energy_abs"` - PhasesActive []bool `json:"phases_active"` - PhasesConnected []bool `json:"phases_connected"` + Power float64 `json:"power"` + EnergyRel float64 `json:"energy_rel"` + EnergyAbs float64 `json:"energy_abs"` + Currents [3]float64 + Voltages [3]float64 + TmpValues []float64 } -// https://www.warp-charger.com/api.html#meter_all_values -type MeterAllValues struct { - PhasesActive []bool `json:"phases_active"` - PhasesConnected []bool `json:"phases_connected"` +type PhasePair struct { + CurrentID int + VoltageID int } -type UsersConfig struct { - Users []User `json:"users"` +type MeterSchema struct { + PowerID int + EnergyAbsID int + Phases [3]PhasePair +} + +// Value IDs based on Tinkerforge's meter_value_id.csv +var DefaultSchema = MeterSchema{ + PowerID: 74, // Power Im-Ex Sum L1 L2 L3 + EnergyAbsID: 209, // Energy Im Sum L1 L2 L3 + Phases: [3]PhasePair{ + {CurrentID: 13, VoltageID: 1}, // Current L1 Im-Ex Sum, Voltage L1-N + {CurrentID: 17, VoltageID: 2}, // Current L2 Im-Ex Sum, Voltage L2-N + {CurrentID: 21, VoltageID: 3}, // Current L3 Im-Ex Sum, Voltage L3-N + }, } type ChargeTrackerCurrentCharge struct { - UserID int `json:"user_id"` - MeterStart float64 `json:"meter_start"` - AuthorizationType int `json:"authorization_type"` AuthorizationInfo struct { TagType int `json:"tag_type"` TagId string `json:"tag_id"` } `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 { - 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"` -} - //go:generate go tool enumer -type ExternalControl -trimprefix ExternalControl -transform whitespace type ExternalControl int @@ -97,15 +77,10 @@ const ( ExternalControlCurrentlySwitching ) -type EmState struct { +type PmState struct { ExternalControl ExternalControl `json:"external_control"` - PhasesSwitched int `json:"phases_switched"` - Input3State bool `json:"input3_state"` - Input4State bool `json:"input4_state"` - RelayState bool `json:"relay_state"` - ErrorFlags int `json:"error_flags"` } -type EmLowLevelState struct { +type PmLowLevelState struct { Is3phase bool `json:"is_3phase"` } diff --git a/charger/warp2.go b/charger/warp2-mqtt.go similarity index 97% rename from charger/warp2.go rename to charger/warp2-mqtt.go index 0b606002b..6ef1efd6d 100644 --- a/charger/warp2.go +++ b/charger/warp2-mqtt.go @@ -290,14 +290,14 @@ func (wb *Warp2) identify() (string, error) { return res.AuthorizationInfo.TagId, err } -func (wb *Warp2) emState() (warp.EmState, error) { - var res warp.EmState +func (wb *Warp2) emState() (warp.PmState, error) { + var res warp.PmState err := wb.emStateG(&res) return res, err } -func (wb *Warp2) emLowLevelState() (warp.EmLowLevelState, error) { - var res warp.EmLowLevelState +func (wb *Warp2) emLowLevelState() (warp.PmLowLevelState, error) { + var res warp.PmLowLevelState err := wb.emLowLevelG(&res) return res, err } diff --git a/charger/warp2_decorators.go b/charger/warp2-mqtt_decorators.go similarity index 100% rename from charger/warp2_decorators.go rename to charger/warp2-mqtt_decorators.go diff --git a/templates/definition/charger/tinkerforge-warp-ws.yaml b/templates/definition/charger/tinkerforge-warp-ws.yaml new file mode 100644 index 000000000..480213cea --- /dev/null +++ b/templates/definition/charger/tinkerforge-warp-ws.yaml @@ -0,0 +1,32 @@ +template: tinkerforge-warp3-ws +products: + - { brand: TinkerForge, description: { generic: WARP2 Smart } } + - { brand: TinkerForge, description: { generic: WARP2 Pro } } + - { brand: TinkerForge, description: { generic: WARP3 Smart } } + - { brand: TinkerForge, description: { generic: WARP3 Pro } } +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 + - name: energyMeterIndex + advanced: true + default: 0 + description: + generic: Energy Meter Index + help: + de: Index des in der Warp konfigurierten Meters dessen Werte genutzt werden sollen. + en: Index of the meter configured in the WARP charger whose values should be used. +render: | + type: warp-ws + uri: {{ .uri }} + user: {{ .user }} + password: {{ .password }} + energyMeterIndex: {{ .energyMeterIndex }} + disablePhaseAutoSwitch: true diff --git a/templates/definition/charger/tinkerforge-warp.yaml b/templates/definition/charger/tinkerforge-warp.yaml index 61d74a1f9..2f8fb8dee 100644 --- a/templates/definition/charger/tinkerforge-warp.yaml +++ b/templates/definition/charger/tinkerforge-warp.yaml @@ -1,4 +1,5 @@ template: tinkerforge-warp +deprecated: true covers: - tinkerforge-warp-pro products: diff --git a/templates/definition/charger/tinkerforge-warp1-ws.yaml b/templates/definition/charger/tinkerforge-warp1-ws.yaml new file mode 100644 index 000000000..139025e0d --- /dev/null +++ b/templates/definition/charger/tinkerforge-warp1-ws.yaml @@ -0,0 +1,29 @@ +template: tinkerforge-warp-ws +products: + - { brand: TinkerForge, description: { generic: WARP1 Smart } } + - { brand: TinkerForge, description: { generic: WARP1 Pro } } +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 + - name: energyMeterIndex + advanced: true + default: 0 + description: + generic: Energy Meter Index + help: + de: Index des in der Warp konfigurierten Meters dessen Werte genutzt werden sollen. + en: Index of the meter configured in the WARP charger whose values should be used. +render: | + type: warp-ws + uri: {{ .uri }} + user: {{ .user }} + password: {{ .password }} + energyMeterIndex: {{ .energyMeterIndex }} diff --git a/templates/definition/charger/tinkerforge-warp2-ws.yaml b/templates/definition/charger/tinkerforge-warp2-ws.yaml new file mode 100644 index 000000000..e3c75a3e1 --- /dev/null +++ b/templates/definition/charger/tinkerforge-warp2-ws.yaml @@ -0,0 +1,44 @@ +template: tinkerforge-warp2-ws +products: + - { brand: TinkerForge, description: { generic: WARP2 Smart + Energy Manager } } + - { brand: TinkerForge, description: { generic: WARP2 Pro + Energy Manager } } +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 }} + disablePhaseAutoSwitch: true diff --git a/templates/definition/charger/tinkerforge-warp3-smart.yaml b/templates/definition/charger/tinkerforge-warp3-smart.yaml index e28f2387f..04c00f452 100644 --- a/templates/definition/charger/tinkerforge-warp3-smart.yaml +++ b/templates/definition/charger/tinkerforge-warp3-smart.yaml @@ -1,4 +1,5 @@ template: tinkerforge-warp3-smart +deprecated: true products: - brand: TinkerForge description: diff --git a/templates/definition/charger/tinkerforge-warp3.yaml b/templates/definition/charger/tinkerforge-warp3.yaml index 656dd292f..f49b4cba5 100644 --- a/templates/definition/charger/tinkerforge-warp3.yaml +++ b/templates/definition/charger/tinkerforge-warp3.yaml @@ -1,4 +1,5 @@ template: tinkerforge-warp3 +deprecated: true products: - brand: TinkerForge description: