diff --git a/examples/chef/common/DeviceTypes.cpp b/examples/chef/common/DeviceTypes.cpp new file mode 100644 index 00000000000000..2a1d8e17d660d6 --- /dev/null +++ b/examples/chef/common/DeviceTypes.cpp @@ -0,0 +1,69 @@ +/* + * + * 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 "DeviceTypes.h" +#include +#include +#include + +using namespace chef; +using namespace chip; +using namespace chip::app; + +bool DeviceTypes::EndpointHasDeviceType(EndpointId endpoint, DeviceTypeId deviceTypeId) +{ + DataModel::ListBuilder deviceTypesList; + CHIP_ERROR err = InteractionModelEngine::GetInstance()->GetDataModelProvider()->DeviceTypes(endpoint, deviceTypesList); + if (err != CHIP_NO_ERROR) + { + ChipLogError(DeviceLayer, "GetDataModelProvider DeviceTypes returned error: %" CHIP_ERROR_FORMAT, err.Format()); + return false; + } + auto deviceTypes = deviceTypesList.TakeBuffer(); + for (const auto & type : deviceTypes) + { + if (type.deviceTypeId == deviceTypeId) + { + return true; + } + } + return false; +} + +DataModel::ListBuilder DeviceTypes::GetAllEndpointsHavingDeviceType(DeviceTypeId deviceTypeId) +{ + DataModel::ListBuilder endpointsList; + CHIP_ERROR err = InteractionModelEngine::GetInstance()->GetDataModelProvider()->Endpoints(endpointsList); + if (err != CHIP_NO_ERROR) + { + ChipLogError(DeviceLayer, "GetDataModelProvider Endpoints returned error: %" CHIP_ERROR_FORMAT, err.Format()); + return DataModel::ListBuilder(); + } + auto allEndpoints = endpointsList.TakeBuffer(); + + DataModel::ListBuilder endpoints; + + for (const auto & ep : allEndpoints) + { + if (EndpointHasDeviceType(ep.id, deviceTypeId)) + { + endpoints.Append(ep.id); + } + } + return endpoints; +} diff --git a/examples/chef/common/DeviceTypes.h b/examples/chef/common/DeviceTypes.h new file mode 100644 index 00000000000000..cf827ac8d4acbd --- /dev/null +++ b/examples/chef/common/DeviceTypes.h @@ -0,0 +1,69 @@ +/* + * + * 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 +#include +#include + +namespace chef { +namespace DeviceTypes { + +// Common location to store all device type IDs +// Official list is in the spec and a complete copy is in - +// https://github.com/project-chip/connectedhomeip/blob/master/src/app/zap-templates/zcl/data-model/chip/matter-devices.xml +// TODO: Device type IDs must be code generated from matter-devices.xml +constexpr chip::DeviceTypeId kCookSurfaceDeviceId = 0x0077; +constexpr chip::DeviceTypeId kCooktopDeviceId = 0x0078; +constexpr chip::DeviceTypeId kOvenDeviceId = 0x007B; +constexpr chip::DeviceTypeId kRefrigeratorDeviceId = 0x0070; +constexpr chip::DeviceTypeId kTemperatureControlledCabinetDeviceId = 0x0071; + +// Expected endpoint IDs for different device types +namespace ExpectedEndpointId { +// Oven +constexpr chip::EndpointId kOven = 1; +constexpr chip::EndpointId kTopCabinetPartOfOven = 2; +constexpr chip::EndpointId kCooktopPartOfOven = 3; +constexpr chip::EndpointId kCookSurfacePartOfCooktopOven = 4; + +// Cooktop +constexpr chip::EndpointId kCooktopStandAlone = 1; +constexpr chip::EndpointId kCookSurfacePartOfCooktop = 2; + +// Refrigerator +constexpr chip::EndpointId kRefrigerator = 1; +constexpr chip::EndpointId kColdCabinetPartOfRefrigerator = 2; +constexpr chip::EndpointId kFreezeCabinetPartOfRefrigerator = 3; +} // namespace ExpectedEndpointId + +// Devicetype APIs + +/** + * Returns true if the endpoint has the specified device type in its device types list. + * Device types list for the given endpoint is fetched using DataModelProvider. + */ +bool EndpointHasDeviceType(chip::EndpointId endpoint, chip::DeviceTypeId deviceTypeId); + +/** + * Returns a list of all endpoints that have the specified device type in their respective device types list. + * Endpoints list is fetched using DataModelProvider. Device type match is checked using EndpointHasDeviceType. + */ +chip::app::DataModel::ListBuilder GetAllEndpointsHavingDeviceType(chip::DeviceTypeId deviceTypeId); + +} // namespace DeviceTypes +} // namespace chef diff --git a/examples/chef/common/clusters/temperature-control/static-supported-temperature-levels.cpp b/examples/chef/common/clusters/temperature-control/static-supported-temperature-levels.cpp index 479cf8965251fb..b09ae5192681fb 100644 --- a/examples/chef/common/clusters/temperature-control/static-supported-temperature-levels.cpp +++ b/examples/chef/common/clusters/temperature-control/static-supported-temperature-levels.cpp @@ -21,6 +21,7 @@ #ifdef MATTER_DM_PLUGIN_TEMPERATURE_CONTROL_SERVER #include "static-supported-temperature-levels.h" #include +#include #include using namespace chip; @@ -30,12 +31,17 @@ using chip::Protocols::InteractionModel::Status; app::Clusters::TemperatureControl::AppSupportedTemperatureLevelsDelegate sAppSupportedTemperatureLevelsDelegate; -CharSpan AppSupportedTemperatureLevelsDelegate::temperatureLevelOptions[] = { "Low"_span, "Medium"_span, "High"_span }; +namespace chef { +namespace Configuration { +namespace TemperatureControl { -const AppSupportedTemperatureLevelsDelegate::EndpointPair AppSupportedTemperatureLevelsDelegate::supportedOptionsByEndpoints - [MATTER_DM_TEMPERATURE_CONTROL_CLUSTER_SERVER_ENDPOINT_COUNT] = { EndpointPair( - 1 /* endpointId */, AppSupportedTemperatureLevelsDelegate::temperatureLevelOptions, - MATTER_ARRAY_SIZE(AppSupportedTemperatureLevelsDelegate::temperatureLevelOptions)) }; +static const CharSpan kTemperatureLevelOptions[3] = { "Low"_span, "Medium"_span, "High"_span }; +} // namespace TemperatureControl +} // namespace Configuration +} // namespace chef + +chef::Configuration::TemperatureControl::EndpointPair + AppSupportedTemperatureLevelsDelegate::supportedOptionsByEndpoints[MATTER_DM_TEMPERATURE_CONTROL_CLUSTER_SERVER_ENDPOINT_COUNT]; uint8_t AppSupportedTemperatureLevelsDelegate::Size() { @@ -43,7 +49,7 @@ uint8_t AppSupportedTemperatureLevelsDelegate::Size() { if (endpointPair.mEndpointId == mEndpoint) { - return endpointPair.mSize; + return endpointPair.mTemperatureLevels.size(); } } return 0; @@ -55,7 +61,7 @@ CHIP_ERROR AppSupportedTemperatureLevelsDelegate::Next(MutableCharSpan & item) { if (endpointPair.mEndpointId == mEndpoint) { - if (endpointPair.mSize > mIndex) + if (endpointPair.mTemperatureLevels.size() > mIndex) { CHIP_ERROR err = CopyCharSpanToMutableCharSpan(endpointPair.mTemperatureLevels[mIndex], item); if (err != CHIP_NO_ERROR) @@ -72,7 +78,13 @@ CHIP_ERROR AppSupportedTemperatureLevelsDelegate::Next(MutableCharSpan & item) } void emberAfTemperatureControlClusterInitCallback(EndpointId endpoint) { - static_assert(MATTER_DM_TEMPERATURE_CONTROL_CLUSTER_SERVER_ENDPOINT_COUNT == 1, "This cluster is only enabled for endpoint 1"); + ChipLogDetail(DeviceLayer, "Initializing TemperatureControl cluster for Endpoint: %d", endpoint); + uint16_t epIndex = emberAfGetClusterServerEndpointIndex(endpoint, TemperatureControl::Id, + MATTER_DM_TEMPERATURE_CONTROL_CLUSTER_SERVER_ENDPOINT_COUNT); + sAppSupportedTemperatureLevelsDelegate.SetSupportedEndpointPair( + epIndex, + chef::Configuration::TemperatureControl::EndpointPair( + endpoint /* endpointId */, Span(chef::Configuration::TemperatureControl::kTemperatureLevelOptions))); chip::app::Clusters::TemperatureControl::SetInstance(&sAppSupportedTemperatureLevelsDelegate); } diff --git a/examples/chef/common/clusters/temperature-control/static-supported-temperature-levels.h b/examples/chef/common/clusters/temperature-control/static-supported-temperature-levels.h index c39bb171c93c58..84c09a248ca228 100644 --- a/examples/chef/common/clusters/temperature-control/static-supported-temperature-levels.h +++ b/examples/chef/common/clusters/temperature-control/static-supported-temperature-levels.h @@ -21,6 +21,33 @@ #include #include +namespace chef { +namespace Configuration { +namespace TemperatureControl { + +/** + * @brief Endpoint to temperature levels mapping. The endpoint must have a temperature control cluster. + * Represents a pair of endpoints and temperature levels supported by that endpoint. + */ +struct EndpointPair +{ + /// An endpoint having temperature control cluster. + chip::EndpointId mEndpointId; + + /// Temperature levels supported by the temperature control cluster at this endpoint. + /// This should point to a const char span array initialized statically. + chip::Span mTemperatureLevels; + + EndpointPair() : mEndpointId(chip::kInvalidEndpointId), mTemperatureLevels() {} + + EndpointPair(chip::EndpointId aEndpointId, chip::Span TemperatureLevels) : + mEndpointId(aEndpointId), mTemperatureLevels(TemperatureLevels) + {} +}; +} // namespace TemperatureControl +} // namespace Configuration +} // namespace chef + namespace chip { namespace app { namespace Clusters { @@ -28,25 +55,19 @@ namespace TemperatureControl { class AppSupportedTemperatureLevelsDelegate : public SupportedTemperatureLevelsIteratorDelegate { - struct EndpointPair - { - EndpointId mEndpointId; - CharSpan * mTemperatureLevels; - uint8_t mSize; - - EndpointPair(EndpointId aEndpointId, CharSpan * TemperatureLevels, uint8_t size) : - mEndpointId(aEndpointId), mTemperatureLevels(TemperatureLevels), mSize(size) - {} - }; - - static CharSpan temperatureLevelOptions[3]; - static const EndpointPair supportedOptionsByEndpoints[MATTER_DM_TEMPERATURE_CONTROL_CLUSTER_SERVER_ENDPOINT_COUNT]; + static chef::Configuration::TemperatureControl::EndpointPair + supportedOptionsByEndpoints[MATTER_DM_TEMPERATURE_CONTROL_CLUSTER_SERVER_ENDPOINT_COUNT]; public: uint8_t Size() override; CHIP_ERROR Next(MutableCharSpan & item) override; + static void SetSupportedEndpointPair(uint16_t index, chef::Configuration::TemperatureControl::EndpointPair endpointPair) + { + supportedOptionsByEndpoints[index] = endpointPair; + } + ~AppSupportedTemperatureLevelsDelegate() {} }; diff --git a/examples/chef/common/stubs.cpp b/examples/chef/common/stubs.cpp index bacad113512764..046c8bf71205c8 100644 --- a/examples/chef/common/stubs.cpp +++ b/examples/chef/common/stubs.cpp @@ -1,3 +1,4 @@ +#include "DeviceTypes.h" #include #include #include @@ -7,6 +8,7 @@ using chip::app::DataModel::Nullable; +using namespace chef; using namespace chip; using namespace chip::app; using namespace chip::app::Clusters; @@ -38,7 +40,6 @@ using namespace chip::app::Clusters; #include "refrigerator-and-temperature-controlled-cabinet-mode/tcc-mode.h" #endif // MATTER_DM_PLUGIN_REFRIGERATOR_AND_TEMPERATURE_CONTROLLED_CABINET_MODE_SERVER -#ifdef MATTER_DM_PLUGIN_REFRIGERATOR_ALARM_SERVER namespace { // Please refer to https://github.com/CHIP-Specifications/connectedhomeip-spec/blob/master/src/namespaces @@ -46,13 +47,25 @@ constexpr const uint8_t kNamespaceRefrigerator = 0x41; // Refrigerator Namespace: 0x41, tag 0x00 (Refrigerator) constexpr const uint8_t kTagRefrigerator = 0x00; // Refrigerator Namespace: 0x41, tag 0x01 (Freezer) -constexpr const uint8_t kTagFreezer = 0x01; -const Clusters::Descriptor::Structs::SemanticTagStruct::Type refrigeratorTagList[] = { { .namespaceID = kNamespaceRefrigerator, - .tag = kTagRefrigerator } }; -const Clusters::Descriptor::Structs::SemanticTagStruct::Type freezerTagList[] = { { .namespaceID = kNamespaceRefrigerator, - .tag = kTagFreezer } }; +constexpr const uint8_t kTagFreezer = 0x01; +const Clusters::Descriptor::Structs::SemanticTagStruct::Type gRefrigeratorTagList[] = { { .namespaceID = kNamespaceRefrigerator, + .tag = kTagRefrigerator } }; +const Clusters::Descriptor::Structs::SemanticTagStruct::Type gFreezerTagList[] = { { .namespaceID = kNamespaceRefrigerator, + .tag = kTagFreezer } }; } // namespace -#endif // MATTER_DM_PLUGIN_REFRIGERATOR_ALARM_SERVER + +namespace PostionSemanticTag { + +constexpr const uint8_t kNamespace = 0x08; // Common Position Namespace +const Clusters::Descriptor::Structs::SemanticTagStruct::Type kLeft = { .namespaceID = kNamespace, .tag = 0x00 }; +const Clusters::Descriptor::Structs::SemanticTagStruct::Type kRight = { .namespaceID = kNamespace, .tag = 0x01 }; +const Clusters::Descriptor::Structs::SemanticTagStruct::Type kTop = { .namespaceID = kNamespace, .tag = 0x02 }; +const Clusters::Descriptor::Structs::SemanticTagStruct::Type kBottom = { .namespaceID = kNamespace, .tag = 0x03 }; +const Clusters::Descriptor::Structs::SemanticTagStruct::Type kMiddle = { .namespaceID = kNamespace, .tag = 0x04 }; + +const Clusters::Descriptor::Structs::SemanticTagStruct::Type kTopTagList[] = { PostionSemanticTag::kTop }; +const Clusters::Descriptor::Structs::SemanticTagStruct::Type kLeftTagList[] = { PostionSemanticTag::kLeft }; +} // namespace PostionSemanticTag #ifdef MATTER_DM_PLUGIN_RVC_OPERATIONAL_STATE_SERVER #include "chef-rvc-operational-state-delegate.h" @@ -347,22 +360,115 @@ void emberAfWakeOnLanClusterInitCallback(EndpointId endpoint) } #endif +/** + * This initializer is for the application having refrigerator on EP1 and child + * temperatureControlledCabinet endpoints on EP2 and EP3. + */ +void RefrigeratorTemperatureControlledCabinetInit() +{ + EndpointId kRefEndpointId = DeviceTypes::ExpectedEndpointId::kRefrigerator; + EndpointId kColdCabinetEndpointId = DeviceTypes::ExpectedEndpointId::kColdCabinetPartOfRefrigerator; + EndpointId kFreezeCabinetEndpointId = DeviceTypes::ExpectedEndpointId::kFreezeCabinetPartOfRefrigerator; + if (!DeviceTypes::EndpointHasDeviceType(kRefEndpointId, DeviceTypes::kRefrigeratorDeviceId)) + { + return; + } + ChipLogDetail(NotSpecified, "Refrigerator device type on EP: %d", kRefEndpointId); + SetTreeCompositionForEndpoint(kRefEndpointId); + + if (DeviceTypes::EndpointHasDeviceType(kColdCabinetEndpointId, DeviceTypes::kTemperatureControlledCabinetDeviceId)) + { + ChipLogDetail(NotSpecified, "Temperature controlled cabinet device type on EP: %d", kColdCabinetEndpointId); + SetParentEndpointForEndpoint(kColdCabinetEndpointId, kRefEndpointId); + SetTagList(kColdCabinetEndpointId, + Span(gRefrigeratorTagList)); + } + + if (DeviceTypes::EndpointHasDeviceType(kFreezeCabinetEndpointId, DeviceTypes::kTemperatureControlledCabinetDeviceId)) + { + ChipLogDetail(NotSpecified, "Temperature controlled cabinet device type on EP: %d", kFreezeCabinetEndpointId); + SetParentEndpointForEndpoint(kFreezeCabinetEndpointId, kRefEndpointId); + SetTagList(kFreezeCabinetEndpointId, Span(gFreezerTagList)); + } +} + +/** + * This initializer is for the application having cooktop. The cooktop can be a part of an oven + * or standalone cooktop. + * Standalone Cooktop: Cooktop on EP1 and optional CookSurface on EP2. + * Cooktop part of Oven: Oven on EP1, Cooktop on EP3 and optional CookSurface on EP4. + */ +void CooktopCookSurfaceInit(EndpointId kCooktopEpId) +{ + SetTreeCompositionForEndpoint(kCooktopEpId); + switch (kCooktopEpId) + { + case DeviceTypes::ExpectedEndpointId::kCooktopStandAlone: + if (DeviceTypes::EndpointHasDeviceType(kCooktopEpId, DeviceTypes::kCooktopDeviceId)) + { + ChipLogDetail(NotSpecified, "Cooktop device type on EP: %d", kCooktopEpId); + EndpointId kCookSurfaceEpId = DeviceTypes::ExpectedEndpointId::kCookSurfacePartOfCooktop; + if (DeviceTypes::EndpointHasDeviceType(kCookSurfaceEpId, DeviceTypes::kCookSurfaceDeviceId)) + { + ChipLogDetail(NotSpecified, "Cook Surface device type on EP: %d", kCookSurfaceEpId); + SetParentEndpointForEndpoint(kCookSurfaceEpId, kCooktopEpId); + SetTagList(kCookSurfaceEpId, + Span(PostionSemanticTag::kLeftTagList)); + } + } + break; + case DeviceTypes::ExpectedEndpointId::kCooktopPartOfOven: + EndpointId kOvenEpId = DeviceTypes::ExpectedEndpointId::kOven; + if (DeviceTypes::EndpointHasDeviceType(kCooktopEpId, DeviceTypes::kCooktopDeviceId) && + DeviceTypes::EndpointHasDeviceType(kOvenEpId, DeviceTypes::kOvenDeviceId)) + { + ChipLogDetail(NotSpecified, "Cooktop device type on EP: %d", kCooktopEpId); + SetParentEndpointForEndpoint(kCooktopEpId, kOvenEpId); + EndpointId kCookSurfaceEpId = DeviceTypes::ExpectedEndpointId::kCookSurfacePartOfCooktopOven; + if (DeviceTypes::EndpointHasDeviceType(kCookSurfaceEpId, DeviceTypes::kCookSurfaceDeviceId)) + { + ChipLogDetail(NotSpecified, "Cook Surface device type on EP: %d", kCookSurfaceEpId); + SetParentEndpointForEndpoint(kCookSurfaceEpId, kCooktopEpId); + SetTagList(kCookSurfaceEpId, + Span(PostionSemanticTag::kLeftTagList)); + } + } + } +} + +/** + * This initializer is for the application having oven on EP1 and child endpoints - + * temperatureControlledCabinet on EP2, cooktop on EP3 and cooksurface on EP4. + */ +void OvenTemperatureControlledCabinetCooktopCookSurfaceInit() +{ + EndpointId kOvenEpId = DeviceTypes::ExpectedEndpointId::kOven; + EndpointId kTemperatureControlledCabinetEpId = DeviceTypes::ExpectedEndpointId::kTopCabinetPartOfOven; + EndpointId kCooktopEpId = DeviceTypes::ExpectedEndpointId::kCooktopPartOfOven; + if (!DeviceTypes::EndpointHasDeviceType(kOvenEpId, DeviceTypes::kOvenDeviceId)) + { + return; + } + + ChipLogDetail(NotSpecified, "Oven device type on EP: %d", kOvenEpId); + SetTreeCompositionForEndpoint(kOvenEpId); + + if (DeviceTypes::EndpointHasDeviceType(kTemperatureControlledCabinetEpId, DeviceTypes::kTemperatureControlledCabinetDeviceId)) + { + ChipLogDetail(NotSpecified, "Temperature controlled cabinet device type on EP: %d", kTemperatureControlledCabinetEpId); + SetParentEndpointForEndpoint(kTemperatureControlledCabinetEpId, kOvenEpId); + SetTagList(kTemperatureControlledCabinetEpId, + Span(PostionSemanticTag::kTopTagList)); + } + CooktopCookSurfaceInit(kCooktopEpId); +} + void ApplicationInit() { ChipLogProgress(NotSpecified, "Chef Application Init !!!"); -#ifdef MATTER_DM_PLUGIN_REFRIGERATOR_ALARM_SERVER - // set Parent Endpoint and Composition Type for an Endpoint - EndpointId kRefEndpointId = 1; - EndpointId kColdCabinetEndpointId = 2; - EndpointId kFreezeCabinetEndpointId = 3; - SetTreeCompositionForEndpoint(kRefEndpointId); - SetParentEndpointForEndpoint(kColdCabinetEndpointId, kRefEndpointId); - SetParentEndpointForEndpoint(kFreezeCabinetEndpointId, kRefEndpointId); - // set TagList - SetTagList(kColdCabinetEndpointId, Span(refrigeratorTagList)); - SetTagList(kFreezeCabinetEndpointId, Span(freezerTagList)); -#endif // MATTER_DM_PLUGIN_REFRIGERATOR_ALARM_SERVER + RefrigeratorTemperatureControlledCabinetInit(); + OvenTemperatureControlledCabinetCooktopCookSurfaceInit(); #ifdef MATTER_DM_PLUGIN_WINDOW_COVERING_SERVER ChipLogProgress(NotSpecified, "Initializing WindowCovering cluster delegate."); diff --git a/examples/chef/linux/BUILD.gn b/examples/chef/linux/BUILD.gn index d70a0423162bf4..3330ff8f7cd9f1 100644 --- a/examples/chef/linux/BUILD.gn +++ b/examples/chef/linux/BUILD.gn @@ -40,6 +40,7 @@ chip_data_model("chef-data-model") { executable("${sample_name}") { sources = [ + "${project_dir}/common/DeviceTypes.cpp", "${project_dir}/common/chef-air-quality.cpp", "${project_dir}/common/chef-concentration-measurement.cpp", "${project_dir}/common/chef-dishwasher-alarm-delegate-impl.cpp", diff --git a/examples/chef/nrfconnect/CMakeLists.txt b/examples/chef/nrfconnect/CMakeLists.txt index e1c286f9372d5b..72eebf484cf44b 100644 --- a/examples/chef/nrfconnect/CMakeLists.txt +++ b/examples/chef/nrfconnect/CMakeLists.txt @@ -78,6 +78,7 @@ if (CONFIG_CHIP_LIB_SHELL) endif() target_sources(app PRIVATE + ${CHEF}/common/DeviceTypes.cpp ${CHEF}/common/chef-air-quality.cpp ${CHEF}/common/chef-concentration-measurement.cpp ${CHEF}/common/chef-fan-control-manager.cpp