Skip to content

Conversation

@jadhavrohit924
Copy link
Contributor

Summary

Add shell commands to add/remove/rename/toggle bridged device endpoints at runtime for esp32.

Related issues

Dynamic addition/removal of bridged endpoints was not supported for testing.

Testing

Manually tested and verified using shell commands and chip tool that device types are correctly populate into the parts-list of the descriptor.

Copilot AI review requested due to automatic review settings December 5, 2025 09:58
@jadhavrohit924 jadhavrohit924 requested a review from a team as a code owner December 5, 2025 09:58
@CLAassistant
Copy link

CLAassistant commented Dec 5, 2025

CLA assistant check
All committers have signed the CLA.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces valuable shell commands for dynamically managing bridged device endpoints on ESP32, which is a great feature for testing purposes. The implementation is mostly sound, but I have identified a few areas for improvement to enhance code quality, robustness, and maintainability. My feedback focuses on reducing code duplication by using arrays, improving input validation by replacing atoi with strtol, and adding necessary error handling for endpoint management functions in InitServer to prevent inconsistent states. Addressing these points will make the code more robust and easier to maintain.

// Add lights 1..3 --> will be mapped to ZCL endpoints 3, 4, 5
AddDeviceEndpoint(&gLight1, &bridgedLightEndpoint, Span<const EmberAfDeviceType>(gBridgedOnOffDeviceTypes),
Span<DataVersion>(gLight1DataVersions), 1);
gLight1Added = true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The return value of AddDeviceEndpoint on the preceding lines is not checked. If AddDeviceEndpoint fails, gLight1Added will be incorrectly set to true, leading to an inconsistent state. The return value should be checked before updating the flag. This applies to all similar calls in this function (e.g., for gLight2Added, gLight3Added, etc., and also for RemoveDeviceEndpoint).

For example:

if (AddDeviceEndpoint(...) >= 0) {
    gLight1Added = true;
}
References
  1. Always check the return value of initialization functions that can fail to ensure robust initialization and prevent unstable states.

Comment on lines +100 to +103
static bool gLight1Added = false;
static bool gLight2Added = false;
static bool gLight3Added = false;
static bool gLight4Added = false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using separate boolean variables for each light's status leads to repetitive code in the command handlers. For example, BridgeRenameHandler and BridgeToggleHandler have nearly identical blocks of code for each light. This can be improved by using arrays for both the Device objects and their 'added' status. This would allow you to use loops, which would significantly reduce code duplication and improve maintainability.

For example, with arrays gLights and gLightsAdded, BridgeToggleHandler could be simplified to a single loop:

static CHIP_ERROR BridgeToggleHandler(int argc, char ** argv)
{
    // This assumes gLights and gLightsAdded are arrays of size 4.
    for (size_t i = 0; i < 4; ++i)
    {
        if (gLightsAdded[i])
        {
            gLights[i].SetOnOff(!gLights[i].IsOn());
            ESP_LOGI(TAG, "Toggled Light %zu: now %s", i + 1, gLights[i].IsOn() ? "ON" : "OFF");
        }
    }
    return CHIP_NO_ERROR;
}
References
  1. To improve maintainability and reduce code duplication, extract common logic from similar methods into private helper functions.

return CHIP_ERROR_INVALID_ARGUMENT;
}

int lightNum = atoi(argv[0]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

atoi does not perform error checking. If the input string is not a valid number, it returns 0, which could be misinterpreted. Using strtol is safer as it allows for robust error handling. This also applies to BridgeRemoveHandler.

Example of safe parsing:

char *end;
long lightNum_long = strtol(argv[0], &end, 10);
if (end == argv[0] || *end != '\0') {
    ESP_LOGE(TAG, "Invalid light number: %s", argv[0]);
    return CHIP_ERROR_INVALID_ARGUMENT;
}
int lightNum = static_cast<int>(lightNum_long);

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds shell command infrastructure for dynamically managing bridged device endpoints in the ESP32 bridge application at runtime. The changes enable testing of dynamic endpoint addition/removal scenarios for Matter bridge certification testing (specifically TC-BR-3).

Key Changes:

  • Added shell commands (add, remove, list, rename, toggle) under the bridge command namespace
  • Introduced state tracking flags (gLight1Added through gLight4Added) to monitor which lights are currently added
  • Integrated with existing shell command infrastructure following the Matter SDK's shell command patterns

return CHIP_ERROR_INVALID_ARGUMENT;
}

int lightNum = atoi(argv[0]);
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The atoi function returns 0 for non-numeric strings and doesn't distinguish between "0" and invalid input. This could allow invalid values like negative numbers or non-numeric strings to pass through. Consider validating that the input is a valid number between 1-4 before calling atoi, or check if the result is within the valid range.

Copilot uses AI. Check for mistakes.
return CHIP_ERROR_INVALID_ARGUMENT;
}

int lightNum = atoi(argv[0]);
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The atoi function returns 0 for non-numeric strings and doesn't distinguish between "0" and invalid input. This could allow invalid values like negative numbers or non-numeric strings to pass through. Consider validating that the input is a valid number between 1-4 before calling atoi, or check if the result is within the valid range.

Copilot uses AI. Check for mistakes.
Comment on lines +559 to +579
if (gLight1Added)
{
gLight1.SetOnOff(!gLight1.IsOn());
ESP_LOGI(TAG, "Toggled Light 1: now %s", gLight1.IsOn() ? "ON" : "OFF");
}
if (gLight2Added)
{
gLight2.SetOnOff(!gLight2.IsOn());
ESP_LOGI(TAG, "Toggled Light 2: now %s", gLight2.IsOn() ? "ON" : "OFF");
}
if (gLight3Added)
{
gLight3.SetOnOff(!gLight3.IsOn());
ESP_LOGI(TAG, "Toggled Light 3: now %s", gLight3.IsOn() ? "ON" : "OFF");
}
if (gLight4Added)
{
gLight4.SetOnOff(!gLight4.IsOn());
ESP_LOGI(TAG, "Toggled Light 4: now %s", gLight4.IsOn() ? "ON" : "OFF");
}

Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] These functions contain significant code duplication. Consider refactoring to use a loop over the lights array (similar to BridgeListHandler) instead of having separate if blocks for each light. This would make the code more maintainable and reduce the risk of inconsistencies when modifying the behavior.

Suggested change
if (gLight1Added)
{
gLight1.SetOnOff(!gLight1.IsOn());
ESP_LOGI(TAG, "Toggled Light 1: now %s", gLight1.IsOn() ? "ON" : "OFF");
}
if (gLight2Added)
{
gLight2.SetOnOff(!gLight2.IsOn());
ESP_LOGI(TAG, "Toggled Light 2: now %s", gLight2.IsOn() ? "ON" : "OFF");
}
if (gLight3Added)
{
gLight3.SetOnOff(!gLight3.IsOn());
ESP_LOGI(TAG, "Toggled Light 3: now %s", gLight3.IsOn() ? "ON" : "OFF");
}
if (gLight4Added)
{
gLight4.SetOnOff(!gLight4.IsOn());
ESP_LOGI(TAG, "Toggled Light 4: now %s", gLight4.IsOn() ? "ON" : "OFF");
}
struct LightInfo {
bool * added;
DeviceLight * light;
const char * name;
};
LightInfo lights[] = {
{ &gLight1Added, &gLight1, "Light 1" },
{ &gLight2Added, &gLight2, "Light 2" },
{ &gLight3Added, &gLight3, "Light 3" },
{ &gLight4Added, &gLight4, "Light 4" },
};
for (const auto & info : lights)
{
if (*(info.added))
{
info.light->SetOnOff(!info.light->IsOn());
ESP_LOGI(TAG, "Toggled %s: now %s", info.name, info.light->IsOn() ? "ON" : "OFF");
}
}

Copilot uses AI. Check for mistakes.
@github-actions
Copy link

github-actions bot commented Dec 5, 2025

PR #42300: Size comparison from 4579de1 to a53aa7c

Full report (35 builds for bl602, bl702, bl702l, cc13x4_26x4, cc32xx, efr32, esp32, nrfconnect, nxp, psoc6, qpg, realtek, stm32, telink)
platform target config section 4579de1 a53aa7c change % change
bl602 lighting-app bl602+mfd+littlefs+rpc FLASH 1105860 1105860 0 0.0
RAM 178954 178954 0 0.0
bl702 lighting-app bl702+eth FLASH 660680 660680 0 0.0
RAM 135025 135025 0 0.0
bl702+wifi FLASH 836530 836530 0 0.0
RAM 124485 124485 0 0.0
bl706+mfd+rpc+littlefs FLASH 1070174 1070174 0 0.0
RAM 117341 117341 0 0.0
bl702l contact-sensor-app bl702l+mfd+littlefs FLASH 902384 902384 0 0.0
RAM 105932 105932 0 0.0
lighting-app bl702l+mfd+littlefs FLASH 982920 982920 0 0.0
RAM 109844 109844 0 0.0
cc13x4_26x4 lighting-app LP_EM_CC1354P10_6 FLASH 770768 770768 0 0.0
RAM 103392 103392 0 0.0
lock-ftd LP_EM_CC1354P10_6 FLASH 783588 783588 0 0.0
RAM 108712 108712 0 0.0
pump-app LP_EM_CC1354P10_6 FLASH 728608 728608 0 0.0
RAM 97452 97452 0 0.0
pump-controller-app LP_EM_CC1354P10_6 FLASH 713064 713064 0 0.0
RAM 97660 97660 0 0.0
cc32xx air-purifier CC3235SF_LAUNCHXL FLASH 554588 554588 0 0.0
RAM 205472 205472 0 0.0
lock CC3235SF_LAUNCHXL FLASH 588528 588528 0 0.0
RAM 205720 205720 0 0.0
efr32 lock-app BRD4187C FLASH 964748 964748 0 0.0
RAM 123776 123776 0 0.0
BRD4338a FLASH 759072 759072 0 0.0
RAM 254388 254388 0 0.0
window-app BRD4187C FLASH 1059888 1059888 0 0.0
RAM 120004 120004 0 0.0
esp32 all-clusters-app c3devkit DRAM 102804 102804 0 0.0
FLASH 1831058 1831058 0 0.0
IRAM 93540 93540 0 0.0
nrfconnect all-clusters-app nrf52840dk_nrf52840 FLASH 936404 936404 0 0.0
RAM 161720 161720 0 0.0
nxp contact mcxw71+release FLASH 695104 695104 0 0.0
RAM 61744 61744 0 0.0
psoc6 all-clusters cy8ckit_062s2_43012 FLASH 1681188 1681188 0 0.0
RAM 214188 214188 0 0.0
all-clusters-minimal cy8ckit_062s2_43012 FLASH 1595684 1595684 0 0.0
RAM 211356 211356 0 0.0
light cy8ckit_062s2_43012 FLASH 1460052 1460052 0 0.0
RAM 197800 197800 0 0.0
lock cy8ckit_062s2_43012 FLASH 1494004 1494004 0 0.0
RAM 225672 225672 0 0.0
qpg lighting-app qpg6200+debug FLASH 838368 838368 0 0.0
RAM 127976 127976 0 0.0
lock-app qpg6200+debug FLASH 775984 775984 0 0.0
RAM 118920 118920 0 0.0
realtek light-switch-app rtl8777g FLASH 708688 708688 0 0.0
RAM 107188 107188 0 0.0
lighting-app rtl8777g FLASH 757384 757384 0 0.0
RAM 127320 127320 0 0.0
stm32 light STM32WB5MM-DK FLASH 470236 470236 0 0.0
RAM 141392 141392 0 0.0
telink bridge-app tl7218x FLASH 703898 703898 0 0.0
RAM 90760 90760 0 0.0
light-app-ota-compress-lzma-shell-factory-data tl3218x FLASH 790246 790246 0 0.0
RAM 41176 41176 0 0.0
light-app-ota-shell-factory-data tl7218x FLASH 781558 781558 0 0.0
RAM 93860 93860 0 0.0
light-switch-app-ota-compress-lzma-factory-data tl7218x_retention FLASH 710042 710042 0 0.0
RAM 52232 52232 0 0.0
light-switch-app-ota-compress-lzma-shell-factory-data tlsr9528a FLASH 745874 745874 0 0.0
RAM 71256 71256 0 0.0
light-switch-app-ota-factory-data tl3218x_retention FLASH 720554 720554 0 0.0
RAM 34956 34956 0 0.0
lighting-app-ota-factory-data tlsr9118bdk40d FLASH 602456 602456 0 0.0
RAM 117320 117320 0 0.0
lighting-app-ota-rpc-factory-data-4mb tlsr9518adk80d FLASH 815066 815070 4 0.0
RAM 92248 92248 0 0.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants