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
|
|
@ -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,63 +60,79 @@ 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 != nil {
|
||||
lp.stopVehicleDetection()
|
||||
|
||||
if vehicle := lp.selectVehicleByID(id); vehicle != nil {
|
||||
lp.stopVehicleDetection()
|
||||
|
||||
// already active via a different detection path - avoid reapplying its mode
|
||||
if lp.GetVehicle() != vehicle {
|
||||
lp.setActiveVehicle(vehicle)
|
||||
}
|
||||
// already active via a different detection path - avoid reapplying its mode
|
||||
if lp.GetVehicle() != vehicle {
|
||||
lp.setActiveVehicle(vehicle)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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 _, vehicle := range vehicles {
|
||||
for _, vid := range vehicle.Identifiers() {
|
||||
if strings.EqualFold(id, vid) {
|
||||
return vehicle
|
||||
for _, id := range ids {
|
||||
for _, vehicle := range vehicles {
|
||||
if slices.ContainsFunc(vehicle.Identifiers(), func(vid string) bool {
|
||||
return strings.EqualFold(id, vid)
|
||||
}) {
|
||||
return vehicle, id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// find placeholder match
|
||||
for _, vehicle := range vehicles {
|
||||
for _, vid := range vehicle.Identifiers() {
|
||||
// case insensitive match
|
||||
re, err := regexp.Compile("(?i)" + strings.ReplaceAll(vid, "*", ".*?"))
|
||||
if err != nil {
|
||||
lp.log.ERROR.Printf("vehicle id: %v", err)
|
||||
continue
|
||||
}
|
||||
for _, id := range ids {
|
||||
for _, vehicle := range vehicles {
|
||||
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)
|
||||
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
|
||||
res api.Vehicle
|
||||
prepare func(testcase)
|
||||
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.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue