blob: bcb29d488b87cf8493274d6f067bcc756d34bc7f [file]
/*
*
* Copyright (c) 2025 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <app_options/AppOptions.h>
#include <device-factory/DeviceFactory.h>
#include <lib/support/CodeUtils.h>
#include <platform/CHIPDeviceConfig.h>
#include <algorithm>
#include <cstdlib>
#include <cstring>
using namespace chip;
using namespace chip::ArgParser;
namespace {
bool IsExcludedFromWildcard(const std::string & type)
{
// These device types are excluded from the wildcard (*) expansion to prevent redundant,
// invalid, or non-leaf endpoint structures.
static const std::vector<std::string> kExcludedDevices = {
"aggregator", // Top-level container representing a bridge; not a standalone leaf device.
"bridged-node", // Base class representing a bridged endpoint wrapper; not a standalone leaf device.
};
return std::any_of(kExcludedDevices.begin(), kExcludedDevices.end(),
[&type](const auto & excluded) { return excluded == type; });
}
} // namespace
// App custom argument handling
constexpr uint16_t kOptionDeviceType = 0xffd0;
constexpr uint16_t kOptionWiFi = 0xffd2;
constexpr uint16_t kOptionKVS = 0xffd3;
constexpr uint16_t kOptionDiscriminator = 0xffd4;
constexpr uint16_t kOptionVendorId = 0xffd5;
constexpr uint16_t kOptionProductId = 0xffd6;
constexpr uint16_t kOptionPort = 0xffd7;
constexpr uint16_t kOptionInterfaceId = 0xffd8;
constexpr uint16_t kOptionBLE = 0xffd9;
constexpr uint16_t kOptionGroupcast = 0xffda;
DeviceTypeParser AppOptions::sParser;
AppOptions::AppConfig AppOptions::mConfig;
const AppOptions::AppConfig & AppOptions::GetConfig()
{
// Default device fallback if no devices are configured
if (mConfig.deviceTypeEntries.empty())
{
mConfig.deviceTypeEntries.push_back({
.type = chip::app::DeviceFactory::GetInstance().GetDefaultDevice(),
.endpoint = 1,
.parentId = chip::kInvalidEndpointId,
});
return mConfig;
}
// Expand wildcards using the supported device types from DeviceFactory
std::vector<std::string> supportedTypes;
for (const auto & deviceType : chip::app::DeviceFactory::GetInstance().SupportedDeviceTypes())
{
if (!IsExcludedFromWildcard(deviceType))
{
supportedTypes.push_back(deviceType);
}
}
sParser.ExpandWildcards(supportedTypes);
mConfig.deviceTypeEntries = sParser.GetDeviceTypeEntries();
return mConfig;
}
bool AppOptions::AllDevicesAppOptionHandler(const char * program, OptionSet * options, int identifier, const char * name,
const char * value)
{
switch (identifier)
{
case kOptionDeviceType: {
if (sParser.ParseSingleDeviceString(value) != CHIP_NO_ERROR)
{
return false;
}
mConfig.deviceTypeEntries = sParser.GetDeviceTypeEntries();
return true;
}
case kOptionBLE:
if (!ParseInt(value, mConfig.bleController))
{
ChipLogError(Support, "Invalid BLE controller specified: %s", value);
return false;
}
return true;
case kOptionWiFi:
mConfig.enableWiFi = true;
ChipLogProgress(AppServer, "WiFi usage enabled");
return true;
case kOptionKVS:
mConfig.kvsPath = value;
return true;
case kOptionDiscriminator: {
char * endptr;
unsigned long val = strtoul(value, &endptr, 0);
if (*endptr != '\0' || val > 0xFFF)
{
ChipLogError(Support, "Invalid discriminator: %s", value);
return false;
}
mConfig.discriminator = static_cast<uint16_t>(val);
return true;
}
case kOptionVendorId:
mConfig.vendorId = static_cast<uint16_t>(strtoul(value, nullptr, 0));
return true;
case kOptionProductId:
mConfig.productId = static_cast<uint16_t>(strtoul(value, nullptr, 0));
return true;
case kOptionPort: {
char * endptr;
unsigned long val = strtoul(value, &endptr, 0);
if (*endptr != '\0' || val > 0xFFFF)
{
ChipLogError(Support, "Invalid port: %s", value);
return false;
}
mConfig.port = static_cast<uint16_t>(val);
ChipLogProgress(AppServer, "Port option set to %u", static_cast<uint16_t>(val));
return true;
}
case kOptionInterfaceId:
mConfig.interfaceId = static_cast<uint32_t>(strtoul(value, nullptr, 0));
return true;
case kOptionGroupcast:
mConfig.enableGroupcast = true;
ChipLogProgress(AppServer, "Groupcast usage enabled");
return true;
default:
ChipLogError(Support, "%s: INTERNAL ERROR: Unhandled option: %s\n", program, name);
return false;
}
return true;
}
OptionSet * AppOptions::GetOptions()
{
static OptionDef sAllDevicesAppOptionDefs[] = {
{ "device", kArgumentRequired, kOptionDeviceType },
#if CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE
{ "ble-controller", kArgumentRequired, kOptionBLE },
#endif
#if CHIP_DEVICE_CONFIG_ENABLE_WIFI
{ "wifi", kNoArgument, kOptionWiFi },
#endif
{ "KVS", kArgumentRequired, kOptionKVS },
{ "discriminator", kArgumentRequired, kOptionDiscriminator },
{ "vendor-id", kArgumentRequired, kOptionVendorId },
{ "product-id", kArgumentRequired, kOptionProductId },
{ "port", kArgumentRequired, kOptionPort },
{ "interface-id", kArgumentRequired, kOptionInterfaceId },
{ "groupcast", kNoArgument, kOptionGroupcast },
{}, // need empty terminator
};
static const std::string gHelpText = []() {
// Device option - this is dynamic
std::string result = " --device <";
for (auto & name : app::DeviceFactory::GetInstance().SupportedDeviceTypes())
{
result.append(name);
result.append("|");
}
result.append("*");
result.append(">");
result += "\n";
result += " Select the device to start up. Format: 'type' or 'type:endpoint' or 'type:endpoint,parent=parentId'.\n";
result += " Use '*' to select all supported leaf devices (e.g. --device \"*:1\").\n";
result += " Can be specified multiple times for multi-endpoint devices.\n";
result += " Example: --device chime:1 --device speaker:2,parent=1\n\n";
#if CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE
result += " --ble-controller <number>\n";
result += " Select the BLE controller to use (default: 0)\n\n";
#endif
#if CHIP_DEVICE_CONFIG_ENABLE_WIFI
result += " --wifi\n";
result += " Enable wifi support for commissioning\n\n";
#endif
return result;
}();
static OptionSet sCmdLineOptions = { AllDevicesAppOptionHandler, // handler function
sAllDevicesAppOptionDefs, // array of option definitions
"PROGRAM OPTIONS", // help group
gHelpText.c_str() };
return &sCmdLineOptions;
}