Skip to content

Commit ea11ded

Browse files
committed
feat: add cabin overheat protection and software update commands
Implement three new CarServer commands: - setCabinOverheatProtection(on, fan_only): Control COP with optional fan-only mode - scheduleSoftwareUpdate(offset_sec): Schedule software update in N seconds - cancelSoftwareUpdate(): Cancel pending software update
1 parent 6b48665 commit ea11ded

5 files changed

Lines changed: 174 additions & 1 deletion

File tree

include/client.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,21 @@ namespace TeslaBLE
123123
int32_t which_vehicle_action,
124124
const void* action_data = nullptr);
125125

126+
int setCabinOverheatProtection(
127+
pb_byte_t* output_buffer,
128+
size_t* output_length,
129+
bool on,
130+
bool fan_only = false);
131+
132+
int scheduleSoftwareUpdate(
133+
pb_byte_t* output_buffer,
134+
size_t* output_length,
135+
int32_t offset_sec);
136+
137+
int cancelSoftwareUpdate(
138+
pb_byte_t* output_buffer,
139+
size_t* output_length);
140+
126141
// Session management (public for testing)
127142
Peer* getPeer(UniversalMessage_Domain domain);
128143
const Peer* getPeer(UniversalMessage_Domain domain) const;

include/message_builders.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ namespace TeslaBLE
5757
static int buildHvacTemperatureAdjustment(CarServer_VehicleAction& action, const void* data);
5858
static int buildHvacClimateKeeper(CarServer_VehicleAction& action, const void* data);
5959
static int buildHvacBioweaponMode(CarServer_VehicleAction& action, const void* data);
60+
static int buildVehicleControlScheduleSoftwareUpdate(CarServer_VehicleAction& action, const void* data);
61+
static int buildSetCabinOverheatProtection(CarServer_VehicleAction& action, const void* data);
6062

6163
// Map of action types to their builder functions
6264
static const std::unordered_map<pb_size_t, BuilderFunction> builders_;

src/client.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -933,6 +933,43 @@ namespace TeslaBLE
933933
return TeslaBLE_Status_E_OK;
934934
}
935935

936+
int Client::setCabinOverheatProtection(pb_byte_t *output_buffer,
937+
size_t *output_length,
938+
bool on,
939+
bool fan_only)
940+
{
941+
CarServer_SetCabinOverheatProtectionAction cop_action = CarServer_SetCabinOverheatProtectionAction_init_default;
942+
cop_action.on = on;
943+
cop_action.fan_only = fan_only;
944+
945+
return this->buildCarServerVehicleActionMessage(
946+
output_buffer,
947+
output_length,
948+
CarServer_VehicleAction_setCabinOverheatProtectionAction_tag,
949+
&cop_action);
950+
}
951+
952+
int Client::scheduleSoftwareUpdate(pb_byte_t *output_buffer,
953+
size_t *output_length,
954+
int32_t offset_sec)
955+
{
956+
return this->buildCarServerVehicleActionMessage(
957+
output_buffer,
958+
output_length,
959+
CarServer_VehicleAction_vehicleControlScheduleSoftwareUpdateAction_tag,
960+
&offset_sec);
961+
}
962+
963+
int Client::cancelSoftwareUpdate(pb_byte_t *output_buffer,
964+
size_t *output_length)
965+
{
966+
return this->buildCarServerVehicleActionMessage(
967+
output_buffer,
968+
output_length,
969+
CarServer_VehicleAction_vehicleControlCancelSoftwareUpdateAction_tag,
970+
nullptr);
971+
}
972+
936973
int Client::buildVCSECActionMessage(const VCSEC_RKEAction_E action, pb_byte_t *output_buffer,
937974
size_t *output_length)
938975
{

src/message_builders.cpp

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ namespace TeslaBLE
3232
static int buildHvacTemperatureAdjustment(CarServer_VehicleAction& action, const void* data);
3333
static int buildHvacClimateKeeper(CarServer_VehicleAction& action, const void* data);
3434
static int buildHvacBioweaponMode(CarServer_VehicleAction& action, const void* data);
35+
static int buildVehicleControlScheduleSoftwareUpdate(CarServer_VehicleAction& action, const void* data);
36+
static int buildSetCabinOverheatProtection(CarServer_VehicleAction& action, const void* data);
3537

3638
// Initialize the static builder map
3739
const std::unordered_map<pb_size_t, VehicleActionBuilder::BuilderFunction>
@@ -62,7 +64,9 @@ namespace TeslaBLE
6264
{CarServer_VehicleAction_hvacSetPreconditioningMaxAction_tag, buildHvacSetPreconditioningMax},
6365
{CarServer_VehicleAction_hvacTemperatureAdjustmentAction_tag, buildHvacTemperatureAdjustment},
6466
{CarServer_VehicleAction_hvacClimateKeeperAction_tag, buildHvacClimateKeeper},
65-
{CarServer_VehicleAction_hvacBioweaponModeAction_tag, buildHvacBioweaponMode}
67+
{CarServer_VehicleAction_hvacBioweaponModeAction_tag, buildHvacBioweaponMode},
68+
{CarServer_VehicleAction_vehicleControlScheduleSoftwareUpdateAction_tag, buildVehicleControlScheduleSoftwareUpdate},
69+
{CarServer_VehicleAction_setCabinOverheatProtectionAction_tag, buildSetCabinOverheatProtection}
6670
};
6771

6872
// Builder implementations
@@ -366,6 +370,31 @@ namespace TeslaBLE
366370
return TeslaBLE_Status_E_OK;
367371
}
368372

373+
int VehicleActionBuilder::buildVehicleControlScheduleSoftwareUpdate(CarServer_VehicleAction& action, const void* data)
374+
{
375+
if (!data) {
376+
LOG_ERROR("Schedule software update action requires int32_t data");
377+
return TeslaBLE_Status_E_ERROR_INVALID_PARAMS;
378+
}
379+
380+
int32_t offset_sec = *static_cast<const int32_t*>(data);
381+
action.vehicle_action_msg.vehicleControlScheduleSoftwareUpdateAction = CarServer_VehicleControlScheduleSoftwareUpdateAction_init_default;
382+
action.vehicle_action_msg.vehicleControlScheduleSoftwareUpdateAction.offset_sec = offset_sec;
383+
return TeslaBLE_Status_E_OK;
384+
}
385+
386+
int VehicleActionBuilder::buildSetCabinOverheatProtection(CarServer_VehicleAction& action, const void* data)
387+
{
388+
if (!data) {
389+
LOG_ERROR("Set cabin overheat protection action requires data");
390+
return TeslaBLE_Status_E_ERROR_INVALID_PARAMS;
391+
}
392+
393+
const auto* cop_data = static_cast<const CarServer_SetCabinOverheatProtectionAction*>(data);
394+
action.vehicle_action_msg.setCabinOverheatProtectionAction = *cop_data;
395+
return TeslaBLE_Status_E_OK;
396+
}
397+
369398
// Helper functions
370399
int VehicleActionBuilder::validateInputParameters(pb_byte_t* output_buffer, size_t* output_length)
371400
{

tests/test_message_building.cpp

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,4 +481,94 @@ TEST_F(MessageBuildingTest, VehicleActionCoverageReport) {
481481
}
482482
}
483483

484+
TEST_F(MessageBuildingTest, SetCabinOverheatProtection_On) {
485+
pb_byte_t buffer[UniversalMessage_RoutableMessage_size];
486+
size_t length = 0;
487+
488+
int result = client->setCabinOverheatProtection(buffer, &length, true, false);
489+
EXPECT_EQ(result, 0) << "Setting cabin overheat protection ON should succeed";
490+
EXPECT_GT(length, 0) << "Message should have non-zero length";
491+
EXPECT_LE(length, sizeof(buffer)) << "Message should fit in buffer";
492+
}
493+
494+
TEST_F(MessageBuildingTest, SetCabinOverheatProtection_Off) {
495+
pb_byte_t buffer[UniversalMessage_RoutableMessage_size];
496+
size_t length = 0;
497+
498+
int result = client->setCabinOverheatProtection(buffer, &length, false, false);
499+
EXPECT_EQ(result, 0) << "Setting cabin overheat protection OFF should succeed";
500+
EXPECT_GT(length, 0) << "Message should have non-zero length";
501+
}
502+
503+
TEST_F(MessageBuildingTest, SetCabinOverheatProtection_FanOnly) {
504+
pb_byte_t buffer[UniversalMessage_RoutableMessage_size];
505+
size_t length = 0;
506+
507+
int result = client->setCabinOverheatProtection(buffer, &length, true, true);
508+
EXPECT_EQ(result, 0) << "Setting cabin overheat protection fan_only should succeed";
509+
EXPECT_GT(length, 0) << "Message should have non-zero length";
510+
}
511+
512+
TEST_F(MessageBuildingTest, ScheduleSoftwareUpdate) {
513+
pb_byte_t buffer[UniversalMessage_RoutableMessage_size];
514+
size_t length = 0;
515+
516+
int32_t offset_sec = 3600; // 1 hour from now
517+
int result = client->scheduleSoftwareUpdate(buffer, &length, offset_sec);
518+
EXPECT_EQ(result, 0) << "Scheduling software update should succeed";
519+
EXPECT_GT(length, 0) << "Message should have non-zero length";
520+
EXPECT_LE(length, sizeof(buffer)) << "Message should fit in buffer";
521+
}
522+
523+
TEST_F(MessageBuildingTest, ScheduleSoftwareUpdate_Delay) {
524+
pb_byte_t buffer[UniversalMessage_RoutableMessage_size];
525+
size_t length = 0;
526+
527+
int32_t offset_sec = 86400; // 24 hours from now
528+
int result = client->scheduleSoftwareUpdate(buffer, &length, offset_sec);
529+
EXPECT_EQ(result, 0) << "Scheduling software update with 24h delay should succeed";
530+
EXPECT_GT(length, 0) << "Message should have non-zero length";
531+
}
532+
533+
TEST_F(MessageBuildingTest, CancelSoftwareUpdate) {
534+
pb_byte_t buffer[UniversalMessage_RoutableMessage_size];
535+
size_t length = 0;
536+
537+
int result = client->cancelSoftwareUpdate(buffer, &length);
538+
EXPECT_EQ(result, 0) << "Canceling software update should succeed";
539+
EXPECT_GT(length, 0) << "Message should have non-zero length";
540+
EXPECT_LE(length, sizeof(buffer)) << "Message should fit in buffer";
541+
}
542+
543+
TEST_F(MessageBuildingTest, BuildScheduleSoftwareUpdateViaBuilder) {
544+
pb_byte_t buffer[UniversalMessage_RoutableMessage_size];
545+
size_t length = 0;
546+
547+
int32_t offset_sec = 7200; // 2 hours
548+
int result = client->buildCarServerVehicleActionMessage(
549+
buffer,
550+
&length,
551+
CarServer_VehicleAction_vehicleControlScheduleSoftwareUpdateAction_tag,
552+
&offset_sec);
553+
EXPECT_EQ(result, 0) << "Building schedule software update via builder should succeed";
554+
EXPECT_GT(length, 0) << "Message should have non-zero length";
555+
}
556+
557+
TEST_F(MessageBuildingTest, BuildSetCabinOverheatProtectionViaBuilder) {
558+
pb_byte_t buffer[UniversalMessage_RoutableMessage_size];
559+
size_t length = 0;
560+
561+
CarServer_SetCabinOverheatProtectionAction cop_action = CarServer_SetCabinOverheatProtectionAction_init_default;
562+
cop_action.on = true;
563+
cop_action.fan_only = false;
564+
565+
int result = client->buildCarServerVehicleActionMessage(
566+
buffer,
567+
&length,
568+
CarServer_VehicleAction_setCabinOverheatProtectionAction_tag,
569+
&cop_action);
570+
EXPECT_EQ(result, 0) << "Building set cabin overheat protection via builder should succeed";
571+
EXPECT_GT(length, 0) << "Message should have non-zero length";
572+
}
573+
484574

0 commit comments

Comments
 (0)