Vehicle: don't instantiate disabled vehicles (#32919)

This commit is contained in:
Michael Geers 2026-08-18 10:41:47 +02:00 • committed by GitHub
parent 8cf5a44a32
commit adfbd057b0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 74 additions and 12 deletions

View file

@ -538,9 +538,14 @@ func configureVehicles(static []config.Named, names ...string) error {
}
}
instance, err := vehicleInstance(cc)
if err != nil {
return fmt.Errorf("cannot create vehicle '%s': %w", cc.Name, err)
// disabled vehicles are not instantiated
var instance api.Vehicle
if !conf.Disable {
var err error
instance, err = vehicleInstance(cc)
if err != nil {
return fmt.Errorf("cannot create vehicle '%s': %w", cc.Name, err)
}
}
mu.Lock()

View file

@ -253,7 +253,8 @@ func NewLoadpointFromConfig(log *util.Logger, settings settings.Settings, collec
}
lp.defaultVehicle = dev.Instance()
if lp.defaultVehicle == nil {
return lp, errors.New("missing default vehicle instance")
// disabled vehicle
lp.log.DEBUG.Printf("default vehicle '%s' is disabled", lp.VehicleRef)
}
}

View file

@ -12,7 +12,9 @@ import (
"github.com/evcc-io/evcc/core/soc"
"github.com/evcc-io/evcc/messenger"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/config"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/mock/gomock"
)
@ -882,3 +884,26 @@ func TestBatteryBoostHold(t *testing.T) {
// which is what keeps the sitePower priority adjustment applied to the loadpoint
assert.NotEqual(t, boostDisabled, lp.GetBatteryBoost(), "hold is active")
}
// default vehicle referencing a disabled vehicle must not fail loadpoint creation
func TestNewLoadpointFromConfigDisabledVehicle(t *testing.T) {
config.Reset()
t.Cleanup(config.Reset)
ctrl := gomock.NewController(t)
// disabled vehicle: device registered without instance
var vehicle api.Vehicle
require.NoError(t, config.Vehicles().Add(config.NewStaticDevice(config.Named{Name: "vehicle"}, vehicle)))
require.NoError(t, config.Chargers().Add(config.NewStaticDevice(config.Named{Name: "charger"}, api.Charger(api.NewMockCharger(ctrl)))))
lp, err := NewLoadpointFromConfig(util.NewLogger("foo"), nil, nil, map[string]any{
"charger": "charger",
"vehicle": "vehicle",
})
require.NoError(t, err)
require.Nil(t, lp.defaultVehicle)
// disabled vehicle is filtered from instances
require.Empty(t, config.Instances(config.Vehicles().Devices()))
}

View file

@ -104,14 +104,7 @@ type vehicles struct {
}
func (vv *vehicles) Instances() []api.Vehicle {
devs := config.Vehicles().Devices()
res := make([]api.Vehicle, 0, len(devs))
for _, dev := range devs {
res = append(res, dev.Instance())
}
return res
return config.Instances(config.Vehicles().Devices())
}
func (vv *vehicles) Settings() []vehicle.API {
@ -119,6 +112,10 @@ func (vv *vehicles) Settings() []vehicle.API {
res := make([]vehicle.API, 0, len(devs))
for _, dev := range devs {
// skip disabled vehicles
if dev.Instance() == nil {
continue
}
res = append(res, vehicle.Adapter(vv.log, dev))
}

View file

@ -239,6 +239,10 @@ test.describe("disable / enable", async () => {
await start();
await page.goto("/#/config");
// loadpoint so the vehicle shows up on the dashboard
await createLoadpoint(page, "Carport");
await page.reload();
// add user-defined vehicle
await page.getByTestId("add-vehicle").click();
const modal = page.getByTestId("vehicle-modal");
@ -277,6 +281,23 @@ soc:
await expectNoFatal(page);
await expect(disabledBadge(vehicleCard)).toBeVisible();
// disabled vehicle is hidden from main ui
await page.goto("/");
await expect(page.getByTestId("loadpoint")).toBeVisible();
await expect(
page.getByTestId("change-vehicle").getByRole("option", { name: "blue Honda" })
).toHaveCount(0);
// no vehicles entry in more menu
const moreTab = page.getByTestId("tab-more");
await moreTab.click();
await expect(moreTab.getByRole("button", { name: "User Interface" })).toBeVisible();
await expect(moreTab.getByRole("button", { name: "Vehicles" })).not.toBeVisible();
// close more menu
await moreTab.click();
await page.goto("/#/config");
// re-enable by clicking the disabled card
await disabledBadge(vehicleCard).click();
await expect(disabledBadge(vehicleCard)).toHaveCount(0);
@ -286,6 +307,19 @@ soc:
await page.reload();
await expectNoFatal(page);
await expect(vehicleCard).toContainText("blue Honda");
// vehicle is selectable on the dashboard again
await page.goto("/");
await expect(
page.getByTestId("change-vehicle").getByRole("option", { name: "blue Honda" })
).toHaveCount(1);
// vehicle is back in the more menu vehicles modal
await moreTab.click();
await moreTab.getByRole("button", { name: "Vehicles" }).click();
const settingsModal = page.getByTestId("vehicle-settings-modal");
await expectModalVisible(settingsModal);
await expect(settingsModal.getByRole("group", { name: "blue Honda" })).toBeVisible();
});
});