api: let Identify return multiple identities (#32949)
This commit is contained in:
parent
0ae85295dc
commit
0be2e0d803
51 changed files with 318 additions and 200 deletions
|
|
@ -130,9 +130,10 @@ type ChargeRater interface {
|
|||
ChargedEnergy() (float64, error)
|
||||
}
|
||||
|
||||
// Identifier identifies a vehicle and is implemented by the charger
|
||||
// Identifier identifies a vehicle and is implemented by the charger.
|
||||
// A charger may know more than one identity, e.g. an RFID tag and a vehicle id.
|
||||
type Identifier interface {
|
||||
Identify() (string, error)
|
||||
Identify() ([]string, error)
|
||||
}
|
||||
|
||||
// Authorizer authorizes a charging session by supplying RFID credentials
|
||||
|
|
|
|||
|
|
@ -228,7 +228,7 @@ func (i *iDimmer) Dimmed() (bool, error) {
|
|||
return i.dimmer1()
|
||||
}
|
||||
|
||||
func Identifier(identifier0 func() (string, error)) api.Identifier {
|
||||
func Identifier(identifier0 func() ([]string, error)) api.Identifier {
|
||||
if identifier0 == nil {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -236,10 +236,10 @@ func Identifier(identifier0 func() (string, error)) api.Identifier {
|
|||
}
|
||||
|
||||
type iIdentifier struct {
|
||||
identifier0 func() (string, error)
|
||||
identifier0 func() ([]string, error)
|
||||
}
|
||||
|
||||
func (i *iIdentifier) Identify() (string, error) {
|
||||
func (i *iIdentifier) Identify() ([]string, error) {
|
||||
return i.identifier0()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -396,10 +396,10 @@ func (m *MockIdentifier) EXPECT() *MockIdentifierMockRecorder {
|
|||
}
|
||||
|
||||
// Identify mocks base method.
|
||||
func (m *MockIdentifier) Identify() (string, error) {
|
||||
func (m *MockIdentifier) Identify() ([]string, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Identify")
|
||||
ret0, _ := ret[0].(string)
|
||||
ret0, _ := ret[0].([]string)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import (
|
|||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/evcc-io/evcc/api"
|
||||
|
|
@ -367,21 +368,24 @@ func (wb *AlpitronicHYC) TotalEnergy() (float64, error) {
|
|||
var _ api.Identifier = (*AlpitronicHYC)(nil)
|
||||
|
||||
// Identify implements the api.Identifier interface
|
||||
func (wb *AlpitronicHYC) Identify() (string, error) {
|
||||
func (wb *AlpitronicHYC) Identify() ([]string, error) {
|
||||
b, err := wb.inputG()
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var res []string
|
||||
|
||||
// the idTag register holds the rfid uid as null-padded ascii string
|
||||
if idTag := hycInput(b, hycRegIdTag, 20); !allZero(idTag) {
|
||||
res = append(res, strings.ToLower(strings.TrimRight(string(idTag), "\x00")))
|
||||
}
|
||||
|
||||
if vid := hycInput(b, hycRegVID, 8); !allZero(vid) {
|
||||
return hex.EncodeToString(vid), nil
|
||||
res = append(res, hex.EncodeToString(vid))
|
||||
}
|
||||
|
||||
if idTag := hycInput(b, hycRegIdTag, 20); !allZero(idTag) {
|
||||
return hex.EncodeToString(idTag), nil
|
||||
}
|
||||
|
||||
return "", nil
|
||||
return res, nil
|
||||
}
|
||||
|
||||
var _ api.Battery = (*AlpitronicHYC)(nil)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package charger
|
|||
import (
|
||||
"context"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
|
@ -218,7 +217,7 @@ func TestAlpitronicIdentify(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
assert.Empty(t, id)
|
||||
|
||||
// vehicle id takes precedence over the tag
|
||||
// tag before vehicle id
|
||||
regs := hycRegs(1, hycStateCharging)
|
||||
regs[hycReg(1, hycRegVID)+1] = 0xAABB
|
||||
regs[hycReg(1, hycRegVID)+2] = 0xCCDD
|
||||
|
|
@ -229,9 +228,9 @@ func TestAlpitronicIdentify(t *testing.T) {
|
|||
|
||||
id, err = wb.Identify()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "0000aabbccddeeff", id)
|
||||
assert.Equal(t, []string{"ab", "0000aabbccddeeff"}, id)
|
||||
|
||||
// tag as fallback
|
||||
// tag only
|
||||
regs = hycRegs(1, hycStateCharging)
|
||||
regs[hycReg(1, hycRegIdTag)] = 0x4142
|
||||
|
||||
|
|
@ -239,7 +238,17 @@ func TestAlpitronicIdentify(t *testing.T) {
|
|||
|
||||
id, err = wb.Identify()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "4142"+strings.Repeat("0", 36), id)
|
||||
assert.Equal(t, []string{"ab"}, id)
|
||||
|
||||
// vehicle id only
|
||||
regs = hycRegs(1, hycStateCharging)
|
||||
regs[hycReg(1, hycRegVID)+3] = 0xEEFF
|
||||
|
||||
wb, _ = hycTestCharger(t, 1, regs)
|
||||
|
||||
id, err = wb.Identify()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []string{"000000000000eeff"}, id)
|
||||
}
|
||||
|
||||
func TestAlpitronicEnable(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -299,13 +299,13 @@ func (wb *Amperfied) Voltages() (float64, float64, float64, error) {
|
|||
var _ api.Identifier = (*Amperfied)(nil)
|
||||
|
||||
// Identify implements the api.Identifier interface
|
||||
func (wb *Amperfied) Identify() (string, error) {
|
||||
func (wb *Amperfied) Identify() ([]string, error) {
|
||||
b, err := wb.conn.ReadInputRegisters(ampRegRfidUID, 6)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return hex.EncodeToString(b), nil
|
||||
return []string{hex.EncodeToString(b)}, nil
|
||||
}
|
||||
|
||||
var _ api.Diagnosis = (*Amperfied)(nil)
|
||||
|
|
|
|||
|
|
@ -504,24 +504,33 @@ func (wb *BenderCC) getPhases() (int, error) {
|
|||
}
|
||||
|
||||
// identify implements the api.Identifier interface
|
||||
func (wb *BenderCC) identify() (string, error) {
|
||||
func (wb *BenderCC) identify() ([]string, error) {
|
||||
var ids []string
|
||||
|
||||
if !wb.legacy && !wb.mennekes4 {
|
||||
b, err := wb.conn.ReadHoldingRegisters(bendRegSmartVehicleDetected, 1)
|
||||
if err == nil && binary.BigEndian.Uint16(b) != 0 {
|
||||
b, err = wb.conn.ReadHoldingRegisters(bendRegEVCCID, 6)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if id := bytesAsString(b); id != "" || err != nil {
|
||||
return id, err
|
||||
if id := bytesAsString(b); id != "" {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
}
|
||||
|
||||
b, err := wb.conn.ReadHoldingRegisters(bendRegUserID, 10)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return bytesAsString(b), nil
|
||||
if id := bytesAsString(b); id != "" {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
// soc implements the api.Battery interface
|
||||
|
|
|
|||
|
|
@ -242,11 +242,11 @@ func (wb *CfosPowerBrain) WakeUp() error {
|
|||
var _ api.Identifier = (*CfosPowerBrain)(nil)
|
||||
|
||||
// Identify implements the api.Identifier interface
|
||||
func (wb *CfosPowerBrain) Identify() (string, error) {
|
||||
func (wb *CfosPowerBrain) Identify() ([]string, error) {
|
||||
b, err := wb.conn.ReadHoldingRegisters(cfosRegLastRfid, 15)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return bytesAsString(b), nil
|
||||
return []string{bytesAsString(b)}, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -118,7 +118,12 @@ func NewConfigurableFromConfig(ctx context.Context, other map[string]any) (api.C
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("identify: %w", err)
|
||||
}
|
||||
implement.May(c, implement.Identifier(identify))
|
||||
if identify != nil {
|
||||
implement.Has(c, implement.Identifier(func() ([]string, error) {
|
||||
id, err := identify()
|
||||
return []string{id}, err
|
||||
}))
|
||||
}
|
||||
|
||||
// decorate wakeup
|
||||
if cc.Wakeup != nil {
|
||||
|
|
|
|||
|
|
@ -289,13 +289,13 @@ func (wb *Compleo) ChargeDuration() (time.Duration, error) {
|
|||
var _ api.Identifier = (*Compleo)(nil)
|
||||
|
||||
// Identify implements the api.Identifier interface
|
||||
func (wb *Compleo) Identify() (string, error) {
|
||||
func (wb *Compleo) Identify() ([]string, error) {
|
||||
b, err := wb.conn.ReadInputRegisters(compleoRegIdTag+wb.offset, 10)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return bytesAsString(b), nil
|
||||
return []string{bytesAsString(b)}, nil
|
||||
}
|
||||
|
||||
var _ loadpoint.Controller = (*Compleo)(nil)
|
||||
|
|
|
|||
|
|
@ -291,13 +291,13 @@ func (wb *Dadapower) Phases1p3p(phases int) error {
|
|||
var _ api.Identifier = (*Dadapower)(nil)
|
||||
|
||||
// Identify implements the api.Identifier interface
|
||||
func (wb *Dadapower) Identify() (string, error) {
|
||||
func (wb *Dadapower) Identify() ([]string, error) {
|
||||
u, err := wb.conn.ReadInputRegisters(dadapowerRegIdentification+wb.regOffset, 20)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return bytesAsString(u), nil
|
||||
return []string{bytesAsString(u)}, nil
|
||||
}
|
||||
|
||||
var _ api.Diagnosis = (*Dadapower)(nil)
|
||||
|
|
|
|||
|
|
@ -318,12 +318,14 @@ func (wb *DaheimLaden) Voltages() (float64, float64, float64, error) {
|
|||
var _ api.Identifier = (*DaheimLaden)(nil)
|
||||
|
||||
// Identify implements the api.Identifier interface. Only usable with PRO
|
||||
func (wb *DaheimLaden) Identify() (string, error) {
|
||||
func (wb *DaheimLaden) Identify() ([]string, error) {
|
||||
b, err := wb.conn.ReadHoldingRegisters(dlRegCardId, 16)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
return utf16BEBytesAsString(b)
|
||||
|
||||
id, err := utf16BEBytesAsString(b)
|
||||
return []string{id}, err
|
||||
}
|
||||
|
||||
// phases1p3p implements the api.PhaseSwitcher interface
|
||||
|
|
|
|||
|
|
@ -354,13 +354,13 @@ func (wb *Delta) CurrentPower() (float64, error) {
|
|||
var _ api.Identifier = (*Delta)(nil)
|
||||
|
||||
// Identify implements the api.Identifier interface
|
||||
func (wb *Delta) Identify() (string, error) {
|
||||
func (wb *Delta) Identify() ([]string, error) {
|
||||
b, err := wb.conn.ReadInputRegisters(wb.base+deltaRegEvseRfidUID, 20)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return bytesAsString(b), nil
|
||||
return []string{bytesAsString(b)}, nil
|
||||
}
|
||||
|
||||
var _ api.Diagnosis = (*Delta)(nil)
|
||||
|
|
|
|||
|
|
@ -748,16 +748,17 @@ var _ api.Identifier = (*E3dc)(nil)
|
|||
|
||||
// Identify implements the api.Identifier interface
|
||||
// Returns the RFID tag ID from WB_SESSION_AUTH_DATA if a session is active
|
||||
func (wb *E3dc) Identify() (string, error) {
|
||||
func (wb *E3dc) Identify() ([]string, error) {
|
||||
wb.mu.Lock()
|
||||
defer wb.mu.Unlock()
|
||||
|
||||
msg, found, err := wb.sessionMessage(rscp.WB_SESSION_AUTH_DATA)
|
||||
if err != nil || !found {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return rscpString(msg)
|
||||
id, err := rscpString(msg)
|
||||
return []string{id}, err
|
||||
}
|
||||
|
||||
var _ api.PhaseSwitcher = (*E3dc)(nil)
|
||||
|
|
|
|||
|
|
@ -850,11 +850,11 @@ func (c *Easee) StatusReason() (api.Reason, error) {
|
|||
var _ api.Identifier = (*Easee)(nil)
|
||||
|
||||
// Currents implements the api.PhaseCurrents interface
|
||||
func (c *Easee) Identify() (string, error) {
|
||||
func (c *Easee) Identify() ([]string, error) {
|
||||
c.mux.Lock()
|
||||
defer c.mux.Unlock()
|
||||
|
||||
return c.rfid, nil
|
||||
return []string{c.rfid}, nil
|
||||
}
|
||||
|
||||
// Set smart charging status to update the chargers led (smart=blue, fast=white)
|
||||
|
|
|
|||
|
|
@ -520,19 +520,26 @@ func (c *EEBus) currents() (float64, float64, float64, error) {
|
|||
var _ api.Identifier = (*EEBus)(nil)
|
||||
|
||||
// Identify implements the api.Identifier interface
|
||||
func (c *EEBus) Identify() (string, error) {
|
||||
func (c *EEBus) Identify() ([]string, error) {
|
||||
evEntity, ok := c.isEvConnected()
|
||||
if !ok {
|
||||
return "", nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if identification, err := c.cem.EvCC.Identifications(evEntity); err == nil && len(identification) > 0 {
|
||||
// return the first identification for now
|
||||
// later this could be multiple, e.g. MAC Address and PCID
|
||||
return identification[0].Value, nil
|
||||
identification, err := c.cem.EvCC.Identifications(evEntity)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return "", nil
|
||||
// may be multiple, e.g. MAC address and PCID
|
||||
var res []string
|
||||
for _, i := range identification {
|
||||
if i.Value != "" {
|
||||
res = append(res, i.Value)
|
||||
}
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
var _ api.Battery = (*EEBus)(nil)
|
||||
|
|
|
|||
|
|
@ -233,13 +233,13 @@ func (wb *Enovates) Voltages() (float64, float64, float64, error) {
|
|||
var _ api.Identifier = (*Enovates)(nil)
|
||||
|
||||
// Identify implements the api.Identifier interface
|
||||
func (wb *Enovates) Identify() (string, error) {
|
||||
func (wb *Enovates) Identify() ([]string, error) {
|
||||
b, err := wb.conn.ReadHoldingRegisters(enovatesRegToken, 16)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return trimModbusString(b), nil
|
||||
return []string{trimModbusString(b)}, nil
|
||||
}
|
||||
|
||||
var _ api.Diagnosis = (*Enovates)(nil)
|
||||
|
|
|
|||
|
|
@ -378,11 +378,11 @@ func (wb *EVECUBE) phases1p3p(phases int) error {
|
|||
}
|
||||
|
||||
// identify implements the api.Identifier interface
|
||||
func (wb *EVECUBE) identify() (string, error) {
|
||||
func (wb *EVECUBE) identify() ([]string, error) {
|
||||
status, err := wb.getAutomationStatus()
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return status.AuthTag.Tag, nil
|
||||
return []string{status.AuthTag.Tag}, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -229,14 +229,14 @@ func (wb *EVSEWifi) voltages() (float64, float64, float64, error) {
|
|||
}
|
||||
|
||||
// Identify implements the api.Identifier interface
|
||||
func (wb *EVSEWifi) identify() (string, error) {
|
||||
func (wb *EVSEWifi) identify() ([]string, error) {
|
||||
params, err := wb.paramG.Get()
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// we can rely on RFIDUID != nil here since identify() is only exposed if the EVSE API supports that property
|
||||
return *params.RFIDUID, nil
|
||||
return []string{*params.RFIDUID}, nil
|
||||
}
|
||||
|
||||
var _ api.Resurrector = (*EVSEWifi)(nil)
|
||||
|
|
|
|||
|
|
@ -599,17 +599,17 @@ func (wb *FoxESSEVC) Voltages() (float64, float64, float64, error) {
|
|||
var _ api.Identifier = (*FoxESSEVC)(nil)
|
||||
|
||||
// Identify implements the api.Identifier interface
|
||||
func (wb *FoxESSEVC) Identify() (string, error) {
|
||||
func (wb *FoxESSEVC) Identify() ([]string, error) {
|
||||
id, err := wb.readUint32(foxRegRFID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if id == 0 {
|
||||
return "", nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%08X", id), nil
|
||||
return []string{fmt.Sprintf("%08X", id)}, nil
|
||||
}
|
||||
|
||||
// phases1p3p implements the api.PhaseSwitcher interface
|
||||
|
|
|
|||
|
|
@ -135,8 +135,9 @@ func (c *Wattpilot) Voltages() (float64, float64, float64, error) {
|
|||
var _ api.Identifier = (*Wattpilot)(nil)
|
||||
|
||||
// Identify implements the api.Identifier interface
|
||||
func (c *Wattpilot) Identify() (string, error) {
|
||||
return c.api.GetRFID()
|
||||
func (c *Wattpilot) Identify() ([]string, error) {
|
||||
id, err := c.api.GetRFID()
|
||||
return []string{id}, err
|
||||
}
|
||||
|
||||
var _ api.PhaseSwitcher = (*Wattpilot)(nil)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"slices"
|
||||
|
||||
"github.com/enbility/spine-go/model"
|
||||
"github.com/evcc-io/evcc/api"
|
||||
|
|
@ -112,14 +113,22 @@ func NewGhostEEBus(ctx context.Context, ski, ip, user, password string, hasMeter
|
|||
|
||||
var _ api.Identifier = (*GhostEEBus)(nil)
|
||||
|
||||
// Identify implements api.Identifier, preferring RFID over EEBUS identification
|
||||
func (wb *GhostEEBus) Identify() (string, error) {
|
||||
// Identify implements api.Identifier, reporting RFID before EEBUS identification
|
||||
func (wb *GhostEEBus) Identify() ([]string, error) {
|
||||
var res []string
|
||||
|
||||
if wb.hasRFID {
|
||||
if id, err := wb.identify(); err == nil && id != "" {
|
||||
return id, nil
|
||||
if ids, err := wb.identify(); err == nil {
|
||||
res = append(res, ids...)
|
||||
}
|
||||
}
|
||||
return wb.EEBus.Identify()
|
||||
|
||||
ids, err := wb.EEBus.Identify()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return slices.DeleteFunc(append(res, ids...), func(id string) bool { return id == "" }), nil
|
||||
}
|
||||
|
||||
// getJSONCtx executes a context-aware GET request and decodes the JSON response.
|
||||
|
|
@ -215,8 +224,8 @@ func (wb *GhostEEBus) getPhases() (int, error) {
|
|||
}
|
||||
|
||||
// identify implements RFID identification via REST API.
|
||||
func (wb *GhostEEBus) identify() (string, error) {
|
||||
func (wb *GhostEEBus) identify() ([]string, error) {
|
||||
var res ghostone.RfidCardLastRead
|
||||
err := wb.GetJSON(wb.uri+"/rfid-cards/last-read", &res)
|
||||
return res.UUID, err
|
||||
return []string{res.UUID}, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -332,17 +332,17 @@ func TestGhostEEBus_Identify(t *testing.T) {
|
|||
tests := []struct {
|
||||
name string
|
||||
uuid string
|
||||
wantID string
|
||||
wantID []string
|
||||
}{
|
||||
{
|
||||
name: "with_uuid",
|
||||
uuid: "ABC123",
|
||||
wantID: "ABC123",
|
||||
wantID: []string{"ABC123"},
|
||||
},
|
||||
{
|
||||
name: "empty",
|
||||
uuid: "",
|
||||
wantID: "",
|
||||
wantID: []string{""},
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -378,13 +378,17 @@ func TestGhostEEBus_IdentifyFallback(t *testing.T) {
|
|||
|
||||
id, err := wb.Identify()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "MAC-001", id)
|
||||
assert.Equal(t, []string{"MAC-001"}, id)
|
||||
})
|
||||
|
||||
t.Run("rfid_returns_id", func(t *testing.T) {
|
||||
wb, _, _ := newTestGhostEEBusWithEEBus(t)
|
||||
t.Run("rfid_and_eebus", func(t *testing.T) {
|
||||
wb, evccMock, evEntity := newTestGhostEEBusWithEEBus(t)
|
||||
wb.hasRFID = true
|
||||
|
||||
// both identities are reported
|
||||
evccMock.EXPECT().EVConnected(evEntity).Return(true)
|
||||
evccMock.EXPECT().Identifications(evEntity).Return([]ucapi.IdentificationItem{{Value: "MAC-001"}}, nil)
|
||||
|
||||
httpmock.ActivateNonDefault(wb.Client)
|
||||
defer httpmock.DeactivateAndReset()
|
||||
|
||||
|
|
@ -397,7 +401,7 @@ func TestGhostEEBus_IdentifyFallback(t *testing.T) {
|
|||
|
||||
id, err := wb.Identify()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "RFID-42", id)
|
||||
assert.Equal(t, []string{"RFID-42", "MAC-001"}, id)
|
||||
})
|
||||
|
||||
t.Run("rfid_empty_falls_back_to_eebus", func(t *testing.T) {
|
||||
|
|
@ -421,6 +425,6 @@ func TestGhostEEBus_IdentifyFallback(t *testing.T) {
|
|||
|
||||
id, err := wb.Identify()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "MAC-001", id)
|
||||
assert.Equal(t, []string{"MAC-001"}, id)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -199,12 +199,12 @@ func (c *GoE) Voltages() (float64, float64, float64, error) {
|
|||
var _ api.Identifier = (*GoE)(nil)
|
||||
|
||||
// Identify implements the api.Identifier interface
|
||||
func (c *GoE) Identify() (string, error) {
|
||||
func (c *GoE) Identify() ([]string, error) {
|
||||
resp, err := c.api.Status()
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
return resp.Identify(), nil
|
||||
return []string{resp.Identify()}, nil
|
||||
}
|
||||
|
||||
var _ api.MeterEnergy = (*GoE)(nil)
|
||||
|
|
|
|||
|
|
@ -270,12 +270,12 @@ func (wb *GoodWe) phases1p3p(phases int) error {
|
|||
var _ api.Identifier = (*GoodWe)(nil)
|
||||
|
||||
// Identify implements api.Identifier (RFID UID, 14-byte NUL-padded ASCII).
|
||||
func (wb *GoodWe) Identify() (string, error) {
|
||||
func (wb *GoodWe) Identify() ([]string, error) {
|
||||
b, err := wb.conn.ReadHoldingRegisters(goodweRegRfid, 7)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
return bytesAsString(bytes.Trim(b, "\x00")), nil
|
||||
return []string{bytesAsString(bytes.Trim(b, "\x00"))}, nil
|
||||
}
|
||||
|
||||
var _ loadpoint.Controller = (*GoodWe)(nil)
|
||||
|
|
|
|||
|
|
@ -248,6 +248,6 @@ func (wb *HardyBarth) Voltages() (float64, float64, float64, error) {
|
|||
// var _ api.Identifier = (*HardyBarth)(nil)
|
||||
|
||||
// // Identify implements the api.Identifier interface
|
||||
// func (wb *HardyBarth) Identify() (string, error) {
|
||||
// return "", api.ErrNotAvailable
|
||||
// func (wb *HardyBarth) Identify() ([]string, error) {
|
||||
// return nil, api.ErrNotAvailable
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -279,12 +279,12 @@ func (wb *Salia) currents() (float64, float64, float64, error) {
|
|||
return i.L1.Actual / 1e3, i.L2.Actual / 1e3, i.L3.Actual / 1e3, err
|
||||
}
|
||||
|
||||
func (wb *Salia) Identify() (string, error) {
|
||||
func (wb *Salia) Identify() ([]string, error) {
|
||||
res, err := wb.apiG.Get()
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
return res.Secc.Port0.RFID.AuthorizationRequest.Key, nil
|
||||
return []string{res.Secc.Port0.RFID.AuthorizationRequest.Key}, nil
|
||||
}
|
||||
|
||||
func (wb *Salia) getPhases() (int, error) {
|
||||
|
|
|
|||
|
|
@ -399,29 +399,29 @@ func (wb *Kathrein) StatusReason() (api.Reason, error) {
|
|||
var _ api.Identifier = (*Kathrein)(nil)
|
||||
|
||||
// Identify implements the api.Identifier interface
|
||||
func (wb *Kathrein) Identify() (string, error) {
|
||||
func (wb *Kathrein) Identify() ([]string, error) {
|
||||
s, err := wb.conn.ReadHoldingRegisters(kathreinRegChargingState, 1)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
state := binary.BigEndian.Uint16(s)
|
||||
if state < 3 || state > 6 {
|
||||
return "", nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
b, err := wb.conn.ReadHoldingRegisters(kathreinRegRfid, 24)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rfid := string(bytes.TrimRight(b, "\x00"))
|
||||
|
||||
if strings.HasPrefix(rfid, "RFID:") {
|
||||
return rfid[5:], nil
|
||||
return []string{rfid[5:]}, nil
|
||||
}
|
||||
|
||||
return rfid, nil
|
||||
return []string{rfid}, nil
|
||||
}
|
||||
|
||||
var _ api.Diagnosis = (*Kathrein)(nil)
|
||||
|
|
|
|||
|
|
@ -358,10 +358,10 @@ func (wb *Keba) currents() (float64, float64, float64, error) {
|
|||
}
|
||||
|
||||
// identify implements the api.Identifier interface
|
||||
func (wb *Keba) identify() (string, error) {
|
||||
func (wb *Keba) identify() ([]string, error) {
|
||||
b, err := wb.conn.ReadHoldingRegisters(kebaRegRfid, 2)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
id := hex.EncodeToString(b)
|
||||
|
|
@ -369,7 +369,7 @@ func (wb *Keba) identify() (string, error) {
|
|||
id = ""
|
||||
}
|
||||
|
||||
return id, nil
|
||||
return []string{id}, nil
|
||||
}
|
||||
|
||||
// phases1p3p implements the api.PhaseSwitcher interface
|
||||
|
|
|
|||
|
|
@ -305,10 +305,10 @@ func (c *KebaUdp) currents() (float64, float64, float64, error) {
|
|||
var _ api.Identifier = (*KebaUdp)(nil)
|
||||
|
||||
// Identify implements the api.Identifier interface
|
||||
func (c *KebaUdp) Identify() (string, error) {
|
||||
func (c *KebaUdp) Identify() ([]string, error) {
|
||||
var kr keba.Report100
|
||||
err := c.roundtrip("report", 100, &kr)
|
||||
return kr.RFIDTag, err
|
||||
return []string{kr.RFIDTag}, err
|
||||
}
|
||||
|
||||
var _ api.Diagnosis = (*KebaUdp)(nil)
|
||||
|
|
|
|||
|
|
@ -263,13 +263,13 @@ func (wb *KSE) getPhases() (int, error) {
|
|||
}
|
||||
|
||||
// Identify implements the api.Identifier interface
|
||||
func (wb *KSE) identify() (string, error) {
|
||||
func (wb *KSE) identify() ([]string, error) {
|
||||
b, err := wb.conn.ReadHoldingRegisters(kseRegNFCTransactionID, 4)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return bytesAsString(b), nil
|
||||
return []string{bytesAsString(b)}, nil
|
||||
}
|
||||
|
||||
var _ api.Diagnosis = (*KSE)(nil)
|
||||
|
|
|
|||
|
|
@ -414,8 +414,8 @@ func (c *OCPP) phases1p3p(phases int) error {
|
|||
var _ api.Identifier = (*OCPP)(nil)
|
||||
|
||||
// Identify implements the api.Identifier interface
|
||||
func (c *OCPP) Identify() (string, error) {
|
||||
return c.conn.IdTag(), nil
|
||||
func (c *OCPP) Identify() ([]string, error) {
|
||||
return []string{c.conn.IdTag()}, nil
|
||||
}
|
||||
|
||||
var _ api.Diagnosis = (*OCPP)(nil)
|
||||
|
|
|
|||
|
|
@ -322,7 +322,7 @@ func (suite *ocppTestSuite) TestAutoStart() {
|
|||
|
||||
id, err := c1.Identify()
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().Equal(expectedIdTag, id)
|
||||
suite.Require().Equal([]string{expectedIdTag}, id)
|
||||
|
||||
conn1 := c1.Connector()
|
||||
_, err = conn1.TransactionID()
|
||||
|
|
|
|||
|
|
@ -227,10 +227,10 @@ func (wb *OpenWB20) WakeUp() error {
|
|||
}
|
||||
|
||||
// Identify implements the api.Identifier interface
|
||||
func (wb *OpenWB20) identify() (string, error) {
|
||||
func (wb *OpenWB20) identify() ([]string, error) {
|
||||
b, err := wb.conn.ReadInputRegisters(wb.base+openwbRegRfid, 10)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
return bytesAsString(b), nil
|
||||
return []string{bytesAsString(b)}, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -202,6 +202,6 @@ func (wb *OpenWbNative) gpioSwitchPhases(phases int) error {
|
|||
}
|
||||
|
||||
// Identify implements the api.Identifier interface
|
||||
func (wb *OpenWbNative) identify() (string, error) {
|
||||
return wb.rfId.Get(), nil
|
||||
func (wb *OpenWbNative) identify() ([]string, error) {
|
||||
return []string{wb.rfId.Get()}, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -232,17 +232,21 @@ func (wb *OpenWBPro) Phases1p3p(phases int) error {
|
|||
var _ api.Identifier = (*OpenWBPro)(nil)
|
||||
|
||||
// Identify implements the api.Identifier interface
|
||||
func (wb *OpenWBPro) Identify() (string, error) {
|
||||
func (wb *OpenWBPro) Identify() ([]string, error) {
|
||||
res, err := wb.statusG.Get()
|
||||
if err != nil || res.VehicleID == "--" {
|
||||
return "", err
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if res.VehicleID != "" {
|
||||
return res.VehicleID, nil
|
||||
var ids []string
|
||||
if res.VehicleID != "" && res.VehicleID != "--" {
|
||||
ids = append(ids, res.VehicleID)
|
||||
}
|
||||
if res.RfidTag != "" {
|
||||
ids = append(ids, res.RfidTag)
|
||||
}
|
||||
|
||||
return res.RfidTag, nil
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
func (wb *OpenWBPro) wakeup() error {
|
||||
|
|
|
|||
|
|
@ -267,22 +267,27 @@ func (wb *PhoenixCharx) getPhaseValues(reg uint16) (float64, float64, float64, e
|
|||
var _ api.Identifier = (*PhoenixCharx)(nil)
|
||||
|
||||
// Identify implements the api.Identifier interface
|
||||
func (wb *PhoenixCharx) Identify() (string, error) {
|
||||
func (wb *PhoenixCharx) Identify() ([]string, error) {
|
||||
b, err := wb.conn.ReadHoldingRegisters(wb.register(charxRegEvid), 10)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if res := bytesAsString(b); res != "" {
|
||||
return res, nil
|
||||
var ids []string
|
||||
if evid := bytesAsString(b); evid != "" {
|
||||
ids = append(ids, evid)
|
||||
}
|
||||
|
||||
b, err = wb.conn.ReadHoldingRegisters(wb.register(charxRegRfid), 10)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return bytesAsString(b), nil
|
||||
if rfid := bytesAsString(b); rfid != "" {
|
||||
ids = append(ids, rfid)
|
||||
}
|
||||
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
var _ api.Diagnosis = (*PhoenixCharx)(nil)
|
||||
|
|
|
|||
|
|
@ -240,13 +240,13 @@ func (wb *PhoenixEVEth) getPhaseValues(reg uint16) (float64, float64, float64, e
|
|||
}
|
||||
|
||||
// identify implements the api.Identifier interface
|
||||
func (wb *PhoenixEVEth) identify() (string, error) {
|
||||
func (wb *PhoenixEVEth) identify() ([]string, error) {
|
||||
b, err := wb.conn.ReadHoldingRegisters(phxRegCardUID, 16)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return bytesAsString(b), nil
|
||||
return []string{bytesAsString(b)}, nil
|
||||
}
|
||||
|
||||
var _ api.Diagnosis = (*PhoenixEVEth)(nil)
|
||||
|
|
|
|||
|
|
@ -381,13 +381,13 @@ func (wb *SmartEVSE3) statusReason() (api.Reason, error) {
|
|||
}
|
||||
|
||||
// identify implements the api.Identifier interface
|
||||
func (wb *SmartEVSE3) identify() (string, error) {
|
||||
func (wb *SmartEVSE3) identify() ([]string, error) {
|
||||
res, err := wb.apiG.Get()
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res.Evse.RFIDLastRead, nil
|
||||
return []string{res.Evse.RFIDLastRead}, nil
|
||||
}
|
||||
|
||||
var _ api.Diagnosis = (*SmartEVSE3)(nil)
|
||||
|
|
|
|||
|
|
@ -319,12 +319,14 @@ func (wb *Vestel) getPhases() (int, error) {
|
|||
}
|
||||
|
||||
// Identify implements the api.Identifier interface
|
||||
func (wb *Vestel) identify() (string, error) {
|
||||
func (wb *Vestel) identify() ([]string, error) {
|
||||
b, err := wb.conn.ReadInputRegisters(vestelRegRFID, 15)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
return utf16BEBytesAsString(b)
|
||||
|
||||
id, err := utf16BEBytesAsString(b)
|
||||
return []string{id}, err
|
||||
}
|
||||
|
||||
var _ api.Diagnosis = (*Vestel)(nil)
|
||||
|
|
|
|||
|
|
@ -455,14 +455,20 @@ func (w *WarpWS) voltages() (float64, float64, float64, error) {
|
|||
return w.meter.Voltages[0], w.meter.Voltages[1], w.meter.Voltages[2], nil
|
||||
}
|
||||
|
||||
// identify prefers the vehicle mac read via ISO 15118 over the RFID tag
|
||||
func (w *WarpWS) identify() (string, error) {
|
||||
// identify reports the vehicle mac read via ISO 15118 before the RFID tag
|
||||
func (w *WarpWS) identify() ([]string, error) {
|
||||
w.mu.RLock()
|
||||
defer w.mu.RUnlock()
|
||||
|
||||
var ids []string
|
||||
if w.evState != nil && w.evState.Mac != "" {
|
||||
return w.evState.Mac, nil
|
||||
ids = append(ids, w.evState.Mac)
|
||||
}
|
||||
return w.chargeTracker.AuthorizationInfo.TagId, nil
|
||||
if tag := w.chargeTracker.AuthorizationInfo.TagId; tag != "" {
|
||||
ids = append(ids, tag)
|
||||
}
|
||||
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
// soc implements the api.Battery interface
|
||||
|
|
|
|||
|
|
@ -282,10 +282,10 @@ func (wb *Warp2) voltages() (float64, float64, float64, error) {
|
|||
return res[0], res[1], res[2], nil
|
||||
}
|
||||
|
||||
func (wb *Warp2) identify() (string, error) {
|
||||
func (wb *Warp2) identify() ([]string, error) {
|
||||
var res warp.ChargeTrackerCurrentCharge
|
||||
err := wb.chargeG(&res)
|
||||
return res.AuthorizationInfo.TagId, err
|
||||
return []string{res.AuthorizationInfo.TagId}, err
|
||||
}
|
||||
|
||||
func (wb *Warp2) emState() (warp.PmState, error) {
|
||||
|
|
|
|||
|
|
@ -242,13 +242,13 @@ func (wb *WebastoNext) Currents() (float64, float64, float64, error) {
|
|||
var _ api.Identifier = (*WebastoNext)(nil)
|
||||
|
||||
// Identify implements the api.Identifier interface
|
||||
func (wb *WebastoNext) Identify() (string, error) {
|
||||
func (wb *WebastoNext) Identify() ([]string, error) {
|
||||
b, err := wb.conn.ReadHoldingRegisters(tqRegUserID, 10)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return bytesAsString(b), nil
|
||||
return []string{bytesAsString(b)}, nil
|
||||
}
|
||||
|
||||
var _ api.Diagnosis = (*WebastoNext)(nil)
|
||||
|
|
|
|||
|
|
@ -245,12 +245,12 @@ func (wb *Weidmüller) Voltages() (float64, float64, float64, error) {
|
|||
var _ api.Identifier = (*Weidmüller)(nil)
|
||||
|
||||
// Identify implements the api.Identifier interface
|
||||
func (wb *Weidmüller) Identify() (string, error) {
|
||||
func (wb *Weidmüller) Identify() ([]string, error) {
|
||||
b, err := wb.conn.ReadHoldingRegisters(wmRegCardId, 11)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
return bytesAsString(b), nil
|
||||
return []string{bytesAsString(b)}, nil
|
||||
}
|
||||
|
||||
var _ api.PhaseSwitcher = (*Weidmüller)(nil)
|
||||
|
|
|
|||
|
|
@ -444,17 +444,17 @@ func (c *Zaptec) switchPhases(phases int) error {
|
|||
var _ api.Identifier = (*Zaptec)(nil)
|
||||
|
||||
// Identify implements the api.Identifier interface
|
||||
func (c *Zaptec) Identify() (string, error) {
|
||||
func (c *Zaptec) Identify() ([]string, error) {
|
||||
res, err := c.statusG.Get()
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if id := res.ObservationByID(zaptec.ChargerCurrentUserUuid); id != nil {
|
||||
return id.ValueAsString, nil
|
||||
return []string{id.ValueAsString}, nil
|
||||
}
|
||||
|
||||
return "", nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (c *Zaptec) installation() (zaptec.Installation, error) {
|
||||
|
|
|
|||
|
|
@ -313,7 +313,8 @@ func (d *dumper) Dump(name string, v any) {
|
|||
|
||||
if v, ok := api.Cap[api.Identifier](v); ok {
|
||||
d.measureTime(w, "Identifier", func() (string, error) {
|
||||
id, err := v.Identify()
|
||||
ids, err := v.Identify()
|
||||
id := strings.Join(ids, ", ")
|
||||
if err == nil && id == "" {
|
||||
id = "<none>"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,10 @@ type typeStruct struct {
|
|||
|
||||
func getTypeImport(t reflect.Type) string {
|
||||
n := t.Name()
|
||||
if n == "" {
|
||||
// unnamed type, e.g. []string
|
||||
return t.String()
|
||||
}
|
||||
if p := t.PkgPath(); p != "" {
|
||||
if s := strings.Split(p, "github.com/evcc-io/evcc/"); len(s) == 2 {
|
||||
return fmt.Sprintf("%s.%s", s[1], n)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package core
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"github.com/evcc-io/evcc/api"
|
||||
|
|
@ -48,8 +49,10 @@ func (lp *Loadpoint) createSession() {
|
|||
}
|
||||
|
||||
if c, ok := api.Cap[api.Identifier](lp.charger); ok {
|
||||
if id, err := c.Identify(); err == nil {
|
||||
lp.session.Identifier = id
|
||||
if ids, err := c.Identify(); err == nil {
|
||||
if idx := slices.IndexFunc(ids, func(id string) bool { return id != "" }); idx >= 0 {
|
||||
lp.session.Identifier = ids[idx]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package core
|
|||
import (
|
||||
"errors"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
|
@ -59,23 +60,36 @@ func (lp *Loadpoint) identifyVehicle() {
|
|||
return
|
||||
}
|
||||
|
||||
id, err := identifier.Identify()
|
||||
ids, err := identifier.Identify()
|
||||
if err != nil {
|
||||
lp.log.ERROR.Println("charger vehicle id:", err)
|
||||
return
|
||||
}
|
||||
|
||||
if lp.vehicleIdentifier == id {
|
||||
ids = slices.DeleteFunc(ids, func(id string) bool { return id == "" })
|
||||
|
||||
// vehicle removed
|
||||
if len(ids) == 0 {
|
||||
lp.setVehicleIdentifier("")
|
||||
return
|
||||
}
|
||||
|
||||
// vehicle found or removed
|
||||
// identity unchanged
|
||||
if slices.Contains(ids, lp.vehicleIdentifier) {
|
||||
return
|
||||
}
|
||||
|
||||
lp.log.DEBUG.Println("charger vehicle id:", strings.Join(ids, ", "))
|
||||
|
||||
// the charger may report multiple identities, e.g. rfid tag and vehicle id
|
||||
vehicle, id := lp.selectVehicleByID(ids...)
|
||||
if id == "" {
|
||||
id = ids[0]
|
||||
}
|
||||
|
||||
lp.setVehicleIdentifier(id)
|
||||
|
||||
if id != "" {
|
||||
lp.log.DEBUG.Println("charger vehicle id:", id)
|
||||
|
||||
if vehicle := lp.selectVehicleByID(id); vehicle != nil {
|
||||
if vehicle != nil {
|
||||
lp.stopVehicleDetection()
|
||||
|
||||
// already active via a different detection path - avoid reapplying its mode
|
||||
|
|
@ -84,38 +98,41 @@ func (lp *Loadpoint) identifyVehicle() {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// selectVehicleByID selects the vehicle with the given ID
|
||||
func (lp *Loadpoint) selectVehicleByID(id string) api.Vehicle {
|
||||
// selectVehicleByID selects the vehicle matching any of the given IDs and returns the matching ID
|
||||
func (lp *Loadpoint) selectVehicleByID(ids ...string) (api.Vehicle, string) {
|
||||
vehicles := lp.coordinatedVehicles()
|
||||
|
||||
// find exact match
|
||||
for _, id := range ids {
|
||||
for _, vehicle := range vehicles {
|
||||
for _, vid := range vehicle.Identifiers() {
|
||||
if strings.EqualFold(id, vid) {
|
||||
return vehicle
|
||||
if slices.ContainsFunc(vehicle.Identifiers(), func(vid string) bool {
|
||||
return strings.EqualFold(id, vid)
|
||||
}) {
|
||||
return vehicle, id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// find placeholder match
|
||||
for _, id := range ids {
|
||||
for _, vehicle := range vehicles {
|
||||
for _, vid := range vehicle.Identifiers() {
|
||||
if slices.ContainsFunc(vehicle.Identifiers(), func(vid string) bool {
|
||||
// case insensitive match
|
||||
re, err := regexp.Compile("(?i)" + strings.ReplaceAll(vid, "*", ".*?"))
|
||||
if err != nil {
|
||||
lp.log.ERROR.Printf("vehicle id: %v", err)
|
||||
continue
|
||||
return false
|
||||
}
|
||||
|
||||
if re.MatchString(id) {
|
||||
return vehicle
|
||||
return re.MatchString(id)
|
||||
}) {
|
||||
return vehicle, id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return nil, ""
|
||||
}
|
||||
|
||||
// setActiveVehicle assigns currently active vehicle, configures soc estimator
|
||||
|
|
|
|||
|
|
@ -201,42 +201,62 @@ func TestVehicleDetectByID(t *testing.T) {
|
|||
|
||||
type testcase struct {
|
||||
string
|
||||
id, i1, i2 string
|
||||
ids []string
|
||||
i1, i2 string
|
||||
res api.Vehicle
|
||||
match string
|
||||
prepare func(testcase)
|
||||
}
|
||||
tc := []testcase{
|
||||
{"1/_/_->0", "1", "", "", nil, func(tc testcase) {
|
||||
{"1/_/_->0", []string{"1"}, "", "", nil, "", func(tc testcase) {
|
||||
v1.EXPECT().Identifiers().Return(nil)
|
||||
v2.EXPECT().Identifiers().Return(nil)
|
||||
v1.EXPECT().Identifiers().Return(nil)
|
||||
v2.EXPECT().Identifiers().Return(nil)
|
||||
}},
|
||||
{"1/1/2->1", "1", "1", "2", v1, func(tc testcase) {
|
||||
{"1/1/2->1", []string{"1"}, "1", "2", v1, "1", func(tc testcase) {
|
||||
v1.EXPECT().Identifiers().Return([]string{tc.i1})
|
||||
}},
|
||||
{"2/1/2->2", "2", "1", "2", v2, func(tc testcase) {
|
||||
{"2/1/2->2", []string{"2"}, "1", "2", v2, "2", func(tc testcase) {
|
||||
v1.EXPECT().Identifiers().Return([]string{tc.i1})
|
||||
v2.EXPECT().Identifiers().Return([]string{tc.i2})
|
||||
}},
|
||||
{"11/1*/2->1", "11", "1*", "2", v1, func(tc testcase) {
|
||||
{"11/1*/2->1", []string{"11"}, "1*", "2", v1, "11", func(tc testcase) {
|
||||
v1.EXPECT().Identifiers().Return([]string{tc.i1})
|
||||
v2.EXPECT().Identifiers().Return([]string{tc.i2})
|
||||
v1.EXPECT().Identifiers().Return([]string{tc.i1})
|
||||
// v2.EXPECT().Identifiers().Return([]string{tc.i2})
|
||||
}},
|
||||
{"22/1*/2*->2", "22", "1*", "2*", v2, func(tc testcase) {
|
||||
{"22/1*/2*->2", []string{"22"}, "1*", "2*", v2, "22", func(tc testcase) {
|
||||
v1.EXPECT().Identifiers().Return([]string{tc.i1})
|
||||
v2.EXPECT().Identifiers().Return([]string{tc.i2})
|
||||
v1.EXPECT().Identifiers().Return([]string{tc.i1})
|
||||
v2.EXPECT().Identifiers().Return([]string{tc.i2})
|
||||
}},
|
||||
{"2/_/*->2", "2", "", "*", v2, func(tc testcase) {
|
||||
{"2/_/*->2", []string{"2"}, "", "*", v2, "2", func(tc testcase) {
|
||||
v1.EXPECT().Identifiers().Return(nil)
|
||||
v2.EXPECT().Identifiers().Return([]string{tc.i2})
|
||||
v1.EXPECT().Identifiers().Return(nil)
|
||||
v2.EXPECT().Identifiers().Return([]string{tc.i2})
|
||||
}},
|
||||
// second identity matches, e.g. rfid tag and vehicle id
|
||||
{"x,2/1/2->2", []string{"x", "2"}, "1", "2", v2, "2", func(tc testcase) {
|
||||
v1.EXPECT().Identifiers().Return([]string{tc.i1})
|
||||
v2.EXPECT().Identifiers().Return([]string{tc.i2})
|
||||
v1.EXPECT().Identifiers().Return([]string{tc.i1})
|
||||
v2.EXPECT().Identifiers().Return([]string{tc.i2})
|
||||
}},
|
||||
// wildcard matches the second identity
|
||||
{"x,22/1/2*->2", []string{"x", "22"}, "1", "2*", v2, "22", func(tc testcase) {
|
||||
v1.EXPECT().Identifiers().Return([]string{tc.i1})
|
||||
v2.EXPECT().Identifiers().Return([]string{tc.i2})
|
||||
v1.EXPECT().Identifiers().Return([]string{tc.i1})
|
||||
v2.EXPECT().Identifiers().Return([]string{tc.i2})
|
||||
v1.EXPECT().Identifiers().Return([]string{tc.i1})
|
||||
v2.EXPECT().Identifiers().Return([]string{tc.i2})
|
||||
v1.EXPECT().Identifiers().Return([]string{tc.i1})
|
||||
v2.EXPECT().Identifiers().Return([]string{tc.i2})
|
||||
}},
|
||||
}
|
||||
|
||||
for _, tc := range tc {
|
||||
|
|
@ -252,9 +272,13 @@ func TestVehicleDetectByID(t *testing.T) {
|
|||
tc.prepare(tc)
|
||||
}
|
||||
|
||||
if res := lp.selectVehicleByID(tc.id); tc.res != res {
|
||||
res, match := lp.selectVehicleByID(tc.ids...)
|
||||
if tc.res != res {
|
||||
t.Errorf("expected %v, got %v", tc.res, res)
|
||||
}
|
||||
if tc.match != match {
|
||||
t.Errorf("expected match %q, got %q", tc.match, match)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -332,7 +356,7 @@ func (c *idCharger) Status() (api.ChargeStatus, error) { return api.StatusB, nil
|
|||
func (c *idCharger) Enabled() (bool, error) { return false, nil }
|
||||
func (c *idCharger) Enable(bool) error { return nil }
|
||||
func (c *idCharger) MaxCurrent(int64) error { return nil }
|
||||
func (c *idCharger) Identify() (string, error) { return c.id, nil }
|
||||
func (c *idCharger) Identify() ([]string, error) { return []string{c.id}, nil }
|
||||
|
||||
// TestReidentifyActiveVehicleKeepsMode is a regression test for #31499:
|
||||
// re-identifying the already-active vehicle must not reapply its default mode.
|
||||
|
|
|
|||
|
|
@ -470,7 +470,7 @@ func testInstance(ctx context.Context, instance any) map[string]testResult {
|
|||
wg.Go(func() {
|
||||
if dev, ok := api.Cap[api.Identifier](instance); ok {
|
||||
val, err := dev.Identify()
|
||||
makeResult("identifier", val, err)
|
||||
makeResult("identifier", strings.Join(val, ", "), err)
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue