Skip to content

Commit d386458

Browse files
authored
fix: Update vehicle awake state logic to treat as awake unless explicitly ASLEEP (#48)
1 parent 2fc0d64 commit d386458

3 files changed

Lines changed: 44 additions & 2 deletions

File tree

include/vehicle.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ class Vehicle {
152152
bool is_connected_ = false;
153153
bool is_vcsec_authenticated_ = false;
154154
bool is_infotainment_authenticated_ = false;
155-
bool is_vehicle_awake_ = false; // Track vehicle sleep state from VCSEC status
155+
bool is_vehicle_awake_ = false; // From VCSEC sleep status (inverted: true unless explicitly ASLEEP)
156156

157157

158158
// Constants

src/vehicle.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ void Vehicle::set_connected(bool connected) {
5959
is_vcsec_authenticated_ = false;
6060
is_infotainment_authenticated_ = false;
6161

62+
// Reset vehicle awake state - we don't know the state after disconnect
63+
// This prevents stale awake state from causing incorrect command handling
64+
is_vehicle_awake_ = false;
65+
6266
// Clear command queue to prevent stale commands from blocking
6367
// New commands will be enqueued after reconnection
6468
while (!command_queue_.empty()) {
@@ -239,6 +243,7 @@ void Vehicle::initiate_vcsec_auth(std::shared_ptr<Command> command) {
239243
void Vehicle::initiate_infotainment_auth(std::shared_ptr<Command> command) {
240244
// FIRST: If vehicle is asleep and command doesn't require wake, skip immediately
241245
// This avoids unnecessary auth attempts for optional polls when vehicle is sleeping
246+
// Note: is_vehicle_awake_ is true unless VCSEC explicitly reports ASLEEP
242247
if (!is_vehicle_awake_ && !command->requires_wake) {
243248
LOG_DEBUG("Vehicle is asleep and command doesn't require wake, skipping: %s", command->name.c_str());
244249
mark_command_completed(command); // Mark as completed (no-op when asleep)
@@ -617,7 +622,8 @@ void Vehicle::handle_vcsec_message(const UniversalMessage_RoutableMessage& msg)
617622
log_vehicle_status(TESLA_LOG_TAG, &vcsec_msg.sub_message.vehicleStatus);
618623

619624
// Track vehicle sleep state for command state machine
620-
is_vehicle_awake_ = (vcsec_msg.sub_message.vehicleStatus.vehicleSleepStatus == VCSEC_VehicleSleepStatus_E_VEHICLE_SLEEP_STATUS_AWAKE);
625+
// Assume awake unless explicitly asleep (charging vehicles may report UNKNOWN)
626+
is_vehicle_awake_ = (vcsec_msg.sub_message.vehicleStatus.vehicleSleepStatus != VCSEC_VehicleSleepStatus_E_VEHICLE_SLEEP_STATUS_ASLEEP);
621627

622628
if (vehicle_status_callback_) vehicle_status_callback_(vcsec_msg.sub_message.vehicleStatus);
623629

tests/test_vehicle.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,42 @@ TEST_F(VehicleTest, InfotainmentPollWithForceWakeSendsData) {
8989
EXPECT_GE(writes.size(), 1) << "Infotainment poll with force_wake should send data";
9090
}
9191

92+
// ============================================================================
93+
// Vehicle Sleep State Tests - Regression test for charging vehicle polling bug
94+
// Issue: Infotainment polls were skipped when vehicle was charging because
95+
// VCSEC reports UNKNOWN status (not AWAKE) for charging vehicles.
96+
// Fix: Inverted logic to treat vehicle as awake unless explicitly ASLEEP.
97+
// ============================================================================
98+
99+
TEST_F(VehicleTest, InfotainmentPollSkippedWhenAsleepByDefault) {
100+
// Verify default behavior: vehicle starts in asleep state (no VCSEC status received)
101+
// and infotainment polls without force_wake should be skipped
102+
103+
bool poll_callback_called = false;
104+
bool poll_success = false;
105+
106+
vehicle_->send_command(
107+
UniversalMessage_Domain_DOMAIN_INFOTAINMENT,
108+
"Optional Poll",
109+
[](Client* client, uint8_t* buff, size_t* len) {
110+
return client->buildCarServerGetVehicleDataMessage(buff, len, CarServer_GetVehicleData_getChargeState_tag);
111+
},
112+
[&](bool success) {
113+
poll_callback_called = true;
114+
poll_success = success;
115+
},
116+
false // requires_wake = false (optional poll)
117+
);
118+
vehicle_->loop();
119+
120+
// Poll should be skipped (no BLE writes) but callback invoked with success
121+
EXPECT_TRUE(poll_callback_called) << "Callback should be invoked for skipped poll";
122+
EXPECT_TRUE(poll_success) << "Skipped poll should report success (no-op)";
123+
124+
auto writes = mock_ble_->get_written_data();
125+
EXPECT_EQ(writes.size(), 0) << "Poll should be skipped when vehicle is asleep";
126+
}
127+
92128
TEST_F(VehicleTest, SetChargingAmpsSendsData) {
93129
vehicle_->set_charging_amps(16);
94130
vehicle_->loop();

0 commit comments

Comments
 (0)