From 78cdef02a8e8864a089c9ddd6894a7c4f684fa7c Mon Sep 17 00:00:00 2001 From: lpbeliveau-silabs <112982107+lpbeliveau-silabs@users.noreply.github.com> Date: Tue, 30 May 2023 19:23:04 -0400 Subject: [PATCH] [Scenes] Enabling the scene-cluster (#26186) * Merge for rebase onto master * Restyled by whitespace * fixed shadowing in InvokeCommand, modified scenes to scenes-server in CMakeList.txt * Regenerated zap files and .matter files for lighting app with Scenes * Modified CMakeList for lighting app to include scene src files * Apply suggestions from code review, swapped checks on groupId != 0, replaced static_cast with to underlying, used StatusIB to convert CHIP_ERROR to status, refactored names of callbacks called from other clusters, got rid of unused functions, fixed integer promotion truncation, remove un-neccessary copies, added checks on sceneHandler failures and assertion on size for static casts. Co-authored-by: Boris Zbarsky * Removed scenes-tokens.h * removed scenes-token.h for test's BUID.gn * Regenerated zap files * Rework of endpoint scope in progress * Completed refactor of SceneTable to have per endpoint flash storage, adapted test to function with it, needs to add multi-endpoints tests * WIP for Attributes handling, needs to add checks on status for Sets and Gets * Refactored the scenes table and scenes server to be endpoint scoped and removed attribute interface override * Fix multi-endpoint on scenes-server cluster, updated attribute access override, added function to add status to response and response to handler in the event of failure to shortent code * Restyled by whitespace * Restyled by clang-format * Applied changes to build after rebase and regenerated zap files * Restyled by shfmt * Fixed uint16 to uint8 conversion issues in SceneTableImpl * Apply suggestions from code review Co-authored-by: Boris Zbarsky * Update src/app/clusters/groups-server/groups-server.cpp Co-authored-by: Boris Zbarsky * Apply suggestions from code review Co-authored-by: Boris Zbarsky * Removed un-necessary attribute assignation, added attribute dirtying instead. Created method to avoid code dupplication on LastConfiguredId update, addressed low hanging fruits in code style and missing checks * Added missing description for scene storage key allocators and removed un necessary config for max transition time * Restyled by clang-format * Restyled by prettier-json * Shadowing fix * Update src/lib/support/DefaultStorageKeyAllocator.h Co-authored-by: Boris Zbarsky * Update src/app/clusters/scenes-server/SceneTableImpl.cpp Co-authored-by: Boris Zbarsky * Add comment on Storage Keys, removed unused variables in comments and moved transition time add to response to after success * Restyled by clang-format * Apply suggestions from code review Co-authored-by: Boris Zbarsky * Removed un-necessary check on transition time * Restyled by whitespace * Restyled by clang-format * Added missing uint16_t in loop to mMaxScenesPerFabric * Added check on nullptr in TestSceneTable * Moved init to MatterScenesPluginServerInitCallback to allow all-cluster-app to initialize for tests * Update src/app/clusters/scenes-server/scenes-server.cpp Co-authored-by: Boris Zbarsky * Update src/app/clusters/scenes-server/scenes-server.cpp Co-authored-by: Boris Zbarsky * Added using for ScenesServer in Matter init callback --------- Co-authored-by: Restyled.io Co-authored-by: Boris Zbarsky --- .../all-clusters-app.matter | 4 +- .../esp32/main/CMakeLists.txt | 2 +- .../all-clusters-minimal-app.matter | 4 +- .../esp32/main/CMakeLists.txt | 2 +- .../lighting-app/esp32/main/CMakeLists.txt | 1 + .../lighting-common/lighting-app.matter | 161 ++ .../lighting-common/lighting-app.zap | 132 +- .../data_model/lighting-thread-app.matter | 182 +++ .../silabs/data_model/lighting-thread-app.zap | 156 +- .../data_model/lighting-wifi-app.matter | 161 ++ .../silabs/data_model/lighting-wifi-app.zap | 132 +- examples/lighting-app/silabs/src/AppTask.cpp | 1 + .../placeholder/linux/apps/app1/config.matter | 2 +- .../placeholder/linux/apps/app2/config.matter | 2 +- .../thermostat-common/thermostat.matter | 2 +- examples/window-app/common/window-app.matter | 4 +- .../tests/inputs/cluster_with_commands.matter | 3 +- .../inputs/large_all_clusters_app.matter | 4 +- .../tests/inputs/large_lighting_app.matter | 4 +- .../app-templates/IMClusterCommandHandler.cpp | 94 -- .../app-templates/endpoint_config.h | 27 +- src/app/chip_data_model.gni | 26 +- .../clusters/groups-server/groups-server.cpp | 10 +- .../clusters/level-control/level-control.cpp | 4 +- .../clusters/on-off-server/on-off-server.cpp | 20 +- .../{scenes => scenes-server}/BUILD.gn | 0 .../ExtensionFieldSets.h | 0 .../ExtensionFieldSetsImpl.cpp | 0 .../ExtensionFieldSetsImpl.h | 2 +- .../{scenes => scenes-server}/SceneTable.h | 50 +- .../SceneTableImpl.cpp | 287 ++-- .../SceneTableImpl.h | 56 +- .../clusters/scenes-server/scenes-server.cpp | 866 +++++++++++ .../clusters/scenes-server/scenes-server.h | 82 + src/app/clusters/scenes/scenes-tokens.h | 41 - src/app/clusters/scenes/scenes.cpp | 1335 ----------------- src/app/clusters/scenes/scenes.h | 182 --- .../window-covering-server.cpp | 2 +- src/app/common/templates/config-data.yaml | 2 +- src/app/tests/BUILD.gn | 13 +- src/app/tests/TestExtensionFieldSets.cpp | 2 +- src/app/tests/TestSceneTable.cpp | 559 ++++++- src/app/util/mock/attribute-storage.cpp | 41 + .../include/zap-generated/endpoint_config.h | 2 + .../zcl/data-model/chip/scene.xml | 1 - .../zcl/zcl-with-test-extensions.json | 1 + src/app/zap-templates/zcl/zcl.json | 1 + src/app/zap_cluster_list.json | 2 +- src/lib/core/CHIPConfig.h | 6 +- src/lib/support/DefaultStorageKeyAllocator.h | 19 +- .../zap-generated/attributes/Accessors.cpp | 62 - .../zap-generated/attributes/Accessors.h | 10 - .../app-common/zap-generated/callback.h | 60 - 53 files changed, 2710 insertions(+), 2114 deletions(-) rename src/app/clusters/{scenes => scenes-server}/BUILD.gn (100%) rename src/app/clusters/{scenes => scenes-server}/ExtensionFieldSets.h (100%) rename src/app/clusters/{scenes => scenes-server}/ExtensionFieldSetsImpl.cpp (100%) rename src/app/clusters/{scenes => scenes-server}/ExtensionFieldSetsImpl.h (98%) rename src/app/clusters/{scenes => scenes-server}/SceneTable.h (87%) rename src/app/clusters/{scenes => scenes-server}/SceneTableImpl.cpp (75%) rename src/app/clusters/{scenes => scenes-server}/SceneTableImpl.h (79%) create mode 100644 src/app/clusters/scenes-server/scenes-server.cpp create mode 100644 src/app/clusters/scenes-server/scenes-server.h delete mode 100644 src/app/clusters/scenes/scenes-tokens.h delete mode 100644 src/app/clusters/scenes/scenes.cpp delete mode 100644 src/app/clusters/scenes/scenes.h diff --git a/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter b/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter index cb63dd3b8dcf89..4ba3c051decf46 100644 --- a/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter +++ b/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter @@ -5072,14 +5072,14 @@ endpoint 1 { } server cluster Scenes { - ram attribute sceneCount default = 0x00; + callback attribute sceneCount default = 0x00; ram attribute currentScene default = 0x00; ram attribute currentGroup default = 0x0000; ram attribute sceneValid default = 0x00; ram attribute nameSupport; ram attribute lastConfiguredBy; ram attribute sceneTableSize; - ram attribute remainingCapacity; + callback attribute remainingCapacity; ram attribute featureMap default = 0; ram attribute clusterRevision default = 4; } diff --git a/examples/all-clusters-app/esp32/main/CMakeLists.txt b/examples/all-clusters-app/esp32/main/CMakeLists.txt index a2f72015840bae..ed62e67b6d26f7 100644 --- a/examples/all-clusters-app/esp32/main/CMakeLists.txt +++ b/examples/all-clusters-app/esp32/main/CMakeLists.txt @@ -70,7 +70,7 @@ set(SRC_DIRS_LIST "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/thermostat-user-interface-configuration-server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/thread-network-diagnostics-server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/channel-server" - "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/scenes" + "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/scenes-server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/software-diagnostics-server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/switch-server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/general-diagnostics-server" diff --git a/examples/all-clusters-minimal-app/all-clusters-common/all-clusters-minimal-app.matter b/examples/all-clusters-minimal-app/all-clusters-common/all-clusters-minimal-app.matter index 8281f563aa69a1..5c7c4817015bfc 100644 --- a/examples/all-clusters-minimal-app/all-clusters-common/all-clusters-minimal-app.matter +++ b/examples/all-clusters-minimal-app/all-clusters-common/all-clusters-minimal-app.matter @@ -4181,14 +4181,14 @@ endpoint 1 { } server cluster Scenes { - ram attribute sceneCount default = 0x00; + callback attribute sceneCount default = 0x00; ram attribute currentScene default = 0x00; ram attribute currentGroup default = 0x0000; ram attribute sceneValid default = 0x00; ram attribute nameSupport; ram attribute lastConfiguredBy; ram attribute sceneTableSize; - ram attribute remainingCapacity; + callback attribute remainingCapacity; ram attribute featureMap default = 0; ram attribute clusterRevision default = 4; } diff --git a/examples/all-clusters-minimal-app/esp32/main/CMakeLists.txt b/examples/all-clusters-minimal-app/esp32/main/CMakeLists.txt index 9587e26f048d7d..d8d00a950ba892 100644 --- a/examples/all-clusters-minimal-app/esp32/main/CMakeLists.txt +++ b/examples/all-clusters-minimal-app/esp32/main/CMakeLists.txt @@ -67,7 +67,7 @@ set(SRC_DIRS_LIST "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/thermostat-user-interface-configuration-server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/thread-network-diagnostics-server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/channel-server" - "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/scenes" + "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/scenes-server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/software-diagnostics-server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/switch-server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/general-diagnostics-server" diff --git a/examples/lighting-app/esp32/main/CMakeLists.txt b/examples/lighting-app/esp32/main/CMakeLists.txt index 6856a3582349dc..b6e29691bacd23 100644 --- a/examples/lighting-app/esp32/main/CMakeLists.txt +++ b/examples/lighting-app/esp32/main/CMakeLists.txt @@ -64,6 +64,7 @@ set(SRC_DIRS_LIST "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/ota-requestor" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/groups-server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/group-key-mgmt-server" + "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/scenes-server" ) set(PRIV_REQUIRES_LIST chip QRCode bt led_strip app_update openthread driver nvs_flash spi_flash) diff --git a/examples/lighting-app/lighting-common/lighting-app.matter b/examples/lighting-app/lighting-common/lighting-app.matter index d16211088b2605..47b3d43e5d8bee 100644 --- a/examples/lighting-app/lighting-common/lighting-app.matter +++ b/examples/lighting-app/lighting-common/lighting-app.matter @@ -126,6 +126,150 @@ server cluster Groups = 4 { fabric command access(invoke: manage) AddGroupIfIdentifying(AddGroupIfIdentifyingRequest): DefaultSuccess = 5; } +/** Attributes and commands for scene configuration and manipulation. */ +server cluster Scenes = 5 { + bitmap Feature : BITMAP32 { + kSceneNames = 0x1; + } + + bitmap ScenesCopyMode : BITMAP8 { + kCopyAllScenes = 0x1; + } + + struct AttributeValuePair { + optional attrib_id attributeID = 0; + int32u attributeValue = 1; + } + + struct ExtensionFieldSet { + cluster_id clusterID = 0; + AttributeValuePair attributeValueList[] = 1; + } + + readonly attribute int8u sceneCount = 0; + readonly attribute int8u currentScene = 1; + readonly attribute group_id currentGroup = 2; + readonly attribute boolean sceneValid = 3; + readonly attribute bitmap8 nameSupport = 4; + readonly attribute nullable node_id lastConfiguredBy = 5; + readonly attribute int16u sceneTableSize = 6; + readonly attribute int8u remainingCapacity = 7; + readonly attribute command_id generatedCommandList[] = 65528; + readonly attribute command_id acceptedCommandList[] = 65529; + readonly attribute event_id eventList[] = 65530; + readonly attribute attrib_id attributeList[] = 65531; + readonly attribute bitmap32 featureMap = 65532; + readonly attribute int16u clusterRevision = 65533; + + request struct AddSceneRequest { + group_id groupID = 0; + INT8U sceneID = 1; + INT16U transitionTime = 2; + CHAR_STRING sceneName = 3; + ExtensionFieldSet extensionFieldSets[] = 4; + } + + request struct ViewSceneRequest { + group_id groupID = 0; + INT8U sceneID = 1; + } + + request struct RemoveSceneRequest { + group_id groupID = 0; + INT8U sceneID = 1; + } + + request struct RemoveAllScenesRequest { + group_id groupID = 0; + } + + request struct StoreSceneRequest { + group_id groupID = 0; + INT8U sceneID = 1; + } + + request struct RecallSceneRequest { + group_id groupID = 0; + INT8U sceneID = 1; + optional nullable INT16U transitionTime = 2; + } + + request struct GetSceneMembershipRequest { + group_id groupID = 0; + } + + request struct EnhancedAddSceneRequest { + group_id groupID = 0; + INT8U sceneID = 1; + INT16U transitionTime = 2; + CHAR_STRING sceneName = 3; + ExtensionFieldSet extensionFieldSets[] = 4; + } + + request struct EnhancedViewSceneRequest { + group_id groupID = 0; + INT8U sceneID = 1; + } + + request struct CopySceneRequest { + ScenesCopyMode mode = 0; + group_id groupIdentifierFrom = 1; + INT8U sceneIdentifierFrom = 2; + group_id groupIdentifierTo = 3; + INT8U sceneIdentifierTo = 4; + } + + response struct AddSceneResponse = 0 { + status status = 0; + group_id groupID = 1; + INT8U sceneID = 2; + } + + response struct ViewSceneResponse = 1 { + status status = 0; + group_id groupID = 1; + INT8U sceneID = 2; + optional INT16U transitionTime = 3; + optional CHAR_STRING sceneName = 4; + optional ExtensionFieldSet extensionFieldSets[] = 5; + } + + response struct RemoveSceneResponse = 2 { + status status = 0; + group_id groupID = 1; + INT8U sceneID = 2; + } + + response struct RemoveAllScenesResponse = 3 { + status status = 0; + group_id groupID = 1; + } + + response struct StoreSceneResponse = 4 { + status status = 0; + group_id groupID = 1; + INT8U sceneID = 2; + } + + response struct GetSceneMembershipResponse = 6 { + status status = 0; + nullable INT8U capacity = 1; + group_id groupID = 2; + optional INT8U sceneList[] = 3; + } + + fabric command access(invoke: manage) AddScene(AddSceneRequest): AddSceneResponse = 0; + fabric command ViewScene(ViewSceneRequest): ViewSceneResponse = 1; + fabric command access(invoke: manage) RemoveScene(RemoveSceneRequest): RemoveSceneResponse = 2; + fabric command access(invoke: manage) RemoveAllScenes(RemoveAllScenesRequest): RemoveAllScenesResponse = 3; + fabric command access(invoke: manage) StoreScene(StoreSceneRequest): StoreSceneResponse = 4; + fabric command RecallScene(RecallSceneRequest): DefaultSuccess = 5; + fabric command GetSceneMembership(GetSceneMembershipRequest): GetSceneMembershipResponse = 6; + fabric command EnhancedAddScene(EnhancedAddSceneRequest): EnhancedAddSceneResponse = 64; + fabric command EnhancedViewScene(EnhancedViewSceneRequest): EnhancedViewSceneResponse = 65; + fabric command CopyScene(CopySceneRequest): CopySceneResponse = 66; +} + /** Attributes and commands for switching devices between 'On' and 'Off' states. */ server cluster OnOff = 6 { enum OnOffDelayedAllOffEffectVariant : ENUM8 { @@ -2252,6 +2396,23 @@ endpoint 1 { ram attribute clusterRevision default = 4; } + server cluster Scenes { + callback attribute sceneCount default = 0x00; + ram attribute currentScene default = 0x00; + ram attribute currentGroup default = 0x0000; + ram attribute sceneValid default = 0x00; + ram attribute nameSupport default = 0x80; + ram attribute lastConfiguredBy; + ram attribute sceneTableSize; + callback attribute remainingCapacity; + callback attribute generatedCommandList; + callback attribute acceptedCommandList; + callback attribute eventList; + callback attribute attributeList; + ram attribute featureMap default = 1; + ram attribute clusterRevision default = 4; + } + server cluster OnOff { persist attribute onOff default = 0x00; ram attribute globalSceneControl default = 0x01; diff --git a/examples/lighting-app/lighting-common/lighting-app.zap b/examples/lighting-app/lighting-common/lighting-app.zap index f2529981be20d3..2b566814b97140 100644 --- a/examples/lighting-app/lighting-common/lighting-app.zap +++ b/examples/lighting-app/lighting-common/lighting-app.zap @@ -5843,7 +5843,7 @@ "mfgCode": null, "define": "SCENES_CLUSTER", "side": "server", - "enabled": 0, + "enabled": 1, "commands": [ { "name": "AddSceneResponse", @@ -5969,12 +5969,140 @@ "storageOption": "RAM", "singleton": 0, "bounded": 0, - "defaultValue": "", + "defaultValue": "0x80", "reportable": 1, "minInterval": 0, "maxInterval": 65344, "reportableChange": 0 }, + { + "name": "LastConfiguredBy", + "code": 5, + "mfgCode": null, + "side": "server", + "type": "node_id", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "SceneTableSize", + "code": 6, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "RemainingCapacity", + "code": 7, + "mfgCode": null, + "side": "server", + "type": "int8u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "GeneratedCommandList", + "code": 65528, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AcceptedCommandList", + "code": 65529, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "EventList", + "code": 65530, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AttributeList", + "code": 65531, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "FeatureMap", + "code": 65532, + "mfgCode": null, + "side": "server", + "type": "bitmap32", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "1", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, { "name": "ClusterRevision", "code": 65533, diff --git a/examples/lighting-app/silabs/data_model/lighting-thread-app.matter b/examples/lighting-app/silabs/data_model/lighting-thread-app.matter index 9cb04a62b385d9..5467cc7eae1ef4 100644 --- a/examples/lighting-app/silabs/data_model/lighting-thread-app.matter +++ b/examples/lighting-app/silabs/data_model/lighting-thread-app.matter @@ -126,6 +126,171 @@ server cluster Groups = 4 { fabric command access(invoke: manage) AddGroupIfIdentifying(AddGroupIfIdentifyingRequest): DefaultSuccess = 5; } +/** Attributes and commands for scene configuration and manipulation. */ +server cluster Scenes = 5 { + bitmap Feature : BITMAP32 { + kSceneNames = 0x1; + } + + bitmap ScenesCopyMode : BITMAP8 { + kCopyAllScenes = 0x1; + } + + struct AttributeValuePair { + optional attrib_id attributeID = 0; + int32u attributeValue = 1; + } + + struct ExtensionFieldSet { + cluster_id clusterID = 0; + AttributeValuePair attributeValueList[] = 1; + } + + readonly attribute int8u sceneCount = 0; + readonly attribute int8u currentScene = 1; + readonly attribute group_id currentGroup = 2; + readonly attribute boolean sceneValid = 3; + readonly attribute bitmap8 nameSupport = 4; + readonly attribute nullable node_id lastConfiguredBy = 5; + readonly attribute int16u sceneTableSize = 6; + readonly attribute int8u remainingCapacity = 7; + readonly attribute command_id generatedCommandList[] = 65528; + readonly attribute command_id acceptedCommandList[] = 65529; + readonly attribute event_id eventList[] = 65530; + readonly attribute attrib_id attributeList[] = 65531; + readonly attribute bitmap32 featureMap = 65532; + readonly attribute int16u clusterRevision = 65533; + + request struct AddSceneRequest { + group_id groupID = 0; + INT8U sceneID = 1; + INT16U transitionTime = 2; + CHAR_STRING sceneName = 3; + ExtensionFieldSet extensionFieldSets[] = 4; + } + + request struct ViewSceneRequest { + group_id groupID = 0; + INT8U sceneID = 1; + } + + request struct RemoveSceneRequest { + group_id groupID = 0; + INT8U sceneID = 1; + } + + request struct RemoveAllScenesRequest { + group_id groupID = 0; + } + + request struct StoreSceneRequest { + group_id groupID = 0; + INT8U sceneID = 1; + } + + request struct RecallSceneRequest { + group_id groupID = 0; + INT8U sceneID = 1; + optional nullable INT16U transitionTime = 2; + } + + request struct GetSceneMembershipRequest { + group_id groupID = 0; + } + + request struct EnhancedAddSceneRequest { + group_id groupID = 0; + INT8U sceneID = 1; + INT16U transitionTime = 2; + CHAR_STRING sceneName = 3; + ExtensionFieldSet extensionFieldSets[] = 4; + } + + request struct EnhancedViewSceneRequest { + group_id groupID = 0; + INT8U sceneID = 1; + } + + request struct CopySceneRequest { + ScenesCopyMode mode = 0; + group_id groupIdentifierFrom = 1; + INT8U sceneIdentifierFrom = 2; + group_id groupIdentifierTo = 3; + INT8U sceneIdentifierTo = 4; + } + + response struct AddSceneResponse = 0 { + status status = 0; + group_id groupID = 1; + INT8U sceneID = 2; + } + + response struct ViewSceneResponse = 1 { + status status = 0; + group_id groupID = 1; + INT8U sceneID = 2; + optional INT16U transitionTime = 3; + optional CHAR_STRING sceneName = 4; + optional ExtensionFieldSet extensionFieldSets[] = 5; + } + + response struct RemoveSceneResponse = 2 { + status status = 0; + group_id groupID = 1; + INT8U sceneID = 2; + } + + response struct RemoveAllScenesResponse = 3 { + status status = 0; + group_id groupID = 1; + } + + response struct StoreSceneResponse = 4 { + status status = 0; + group_id groupID = 1; + INT8U sceneID = 2; + } + + response struct GetSceneMembershipResponse = 6 { + status status = 0; + nullable INT8U capacity = 1; + group_id groupID = 2; + optional INT8U sceneList[] = 3; + } + + response struct EnhancedAddSceneResponse = 64 { + status status = 0; + group_id groupID = 1; + INT8U sceneID = 2; + } + + response struct EnhancedViewSceneResponse = 65 { + status status = 0; + group_Id groupID = 1; + INT8U sceneID = 2; + optional INT16U transitionTime = 3; + optional CHAR_STRING sceneName = 4; + optional ExtensionFieldSet extensionFieldSets[] = 5; + } + + response struct CopySceneResponse = 66 { + status status = 0; + group_Id groupIdentifierFrom = 1; + INT8U sceneIdentifierFrom = 2; + } + + fabric command access(invoke: manage) AddScene(AddSceneRequest): AddSceneResponse = 0; + fabric command ViewScene(ViewSceneRequest): ViewSceneResponse = 1; + fabric command access(invoke: manage) RemoveScene(RemoveSceneRequest): RemoveSceneResponse = 2; + fabric command access(invoke: manage) RemoveAllScenes(RemoveAllScenesRequest): RemoveAllScenesResponse = 3; + fabric command access(invoke: manage) StoreScene(StoreSceneRequest): StoreSceneResponse = 4; + fabric command RecallScene(RecallSceneRequest): DefaultSuccess = 5; + fabric command GetSceneMembership(GetSceneMembershipRequest): GetSceneMembershipResponse = 6; + fabric command EnhancedAddScene(EnhancedAddSceneRequest): EnhancedAddSceneResponse = 64; + fabric command EnhancedViewScene(EnhancedViewSceneRequest): EnhancedViewSceneResponse = 65; + fabric command CopyScene(CopySceneRequest): CopySceneResponse = 66; +} + /** Attributes and commands for switching devices between 'On' and 'Off' states. */ server cluster OnOff = 6 { enum OnOffDelayedAllOffEffectVariant : ENUM8 { @@ -2059,6 +2224,23 @@ endpoint 1 { ram attribute clusterRevision default = 4; } + server cluster Scenes { + callback attribute sceneCount default = 0x00; + ram attribute currentScene default = 0x00; + ram attribute currentGroup default = 0x0000; + ram attribute sceneValid default = 0x00; + ram attribute nameSupport default = 0x80; + ram attribute lastConfiguredBy; + ram attribute sceneTableSize default = 16; + callback attribute remainingCapacity default = 8; + callback attribute generatedCommandList; + callback attribute acceptedCommandList; + callback attribute eventList; + callback attribute attributeList; + ram attribute featureMap default = 0; + ram attribute clusterRevision default = 4; + } + server cluster OnOff { persist attribute onOff default = 0x00; ram attribute globalSceneControl default = 0x01; diff --git a/examples/lighting-app/silabs/data_model/lighting-thread-app.zap b/examples/lighting-app/silabs/data_model/lighting-thread-app.zap index ab56bfdc47aab7..61638b8dd4d8df 100644 --- a/examples/lighting-app/silabs/data_model/lighting-thread-app.zap +++ b/examples/lighting-app/silabs/data_model/lighting-thread-app.zap @@ -5950,7 +5950,7 @@ "mfgCode": null, "define": "SCENES_CLUSTER", "side": "server", - "enabled": 0, + "enabled": 1, "commands": [ { "name": "AddSceneResponse", @@ -5999,6 +5999,30 @@ "source": "server", "incoming": 0, "outgoing": 1 + }, + { + "name": "EnhancedAddSceneResponse", + "code": 64, + "mfgCode": null, + "source": "server", + "incoming": 0, + "outgoing": 1 + }, + { + "name": "EnhancedViewSceneResponse", + "code": 65, + "mfgCode": null, + "source": "server", + "incoming": 0, + "outgoing": 1 + }, + { + "name": "CopySceneResponse", + "code": 66, + "mfgCode": null, + "source": "server", + "incoming": 0, + "outgoing": 1 } ], "attributes": [ @@ -6076,12 +6100,140 @@ "storageOption": "RAM", "singleton": 0, "bounded": 0, - "defaultValue": "", + "defaultValue": "0x80", "reportable": 1, "minInterval": 0, "maxInterval": 65344, "reportableChange": 0 }, + { + "name": "LastConfiguredBy", + "code": 5, + "mfgCode": null, + "side": "server", + "type": "node_id", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "SceneTableSize", + "code": 6, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "16", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "RemainingCapacity", + "code": 7, + "mfgCode": null, + "side": "server", + "type": "int8u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "8", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "GeneratedCommandList", + "code": 65528, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AcceptedCommandList", + "code": 65529, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "EventList", + "code": 65530, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AttributeList", + "code": 65531, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "FeatureMap", + "code": 65532, + "mfgCode": null, + "side": "server", + "type": "bitmap32", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, { "name": "ClusterRevision", "code": 65533, diff --git a/examples/lighting-app/silabs/data_model/lighting-wifi-app.matter b/examples/lighting-app/silabs/data_model/lighting-wifi-app.matter index 9ab75d756817d7..96d954f3260c02 100644 --- a/examples/lighting-app/silabs/data_model/lighting-wifi-app.matter +++ b/examples/lighting-app/silabs/data_model/lighting-wifi-app.matter @@ -126,6 +126,150 @@ server cluster Groups = 4 { fabric command access(invoke: manage) AddGroupIfIdentifying(AddGroupIfIdentifyingRequest): DefaultSuccess = 5; } +/** Attributes and commands for scene configuration and manipulation. */ +server cluster Scenes = 5 { + bitmap Feature : BITMAP32 { + kSceneNames = 0x1; + } + + bitmap ScenesCopyMode : BITMAP8 { + kCopyAllScenes = 0x1; + } + + struct AttributeValuePair { + optional attrib_id attributeID = 0; + int32u attributeValue = 1; + } + + struct ExtensionFieldSet { + cluster_id clusterID = 0; + AttributeValuePair attributeValueList[] = 1; + } + + readonly attribute int8u sceneCount = 0; + readonly attribute int8u currentScene = 1; + readonly attribute group_id currentGroup = 2; + readonly attribute boolean sceneValid = 3; + readonly attribute bitmap8 nameSupport = 4; + readonly attribute nullable node_id lastConfiguredBy = 5; + readonly attribute int16u sceneTableSize = 6; + readonly attribute int8u remainingCapacity = 7; + readonly attribute command_id generatedCommandList[] = 65528; + readonly attribute command_id acceptedCommandList[] = 65529; + readonly attribute event_id eventList[] = 65530; + readonly attribute attrib_id attributeList[] = 65531; + readonly attribute bitmap32 featureMap = 65532; + readonly attribute int16u clusterRevision = 65533; + + request struct AddSceneRequest { + group_id groupID = 0; + INT8U sceneID = 1; + INT16U transitionTime = 2; + CHAR_STRING sceneName = 3; + ExtensionFieldSet extensionFieldSets[] = 4; + } + + request struct ViewSceneRequest { + group_id groupID = 0; + INT8U sceneID = 1; + } + + request struct RemoveSceneRequest { + group_id groupID = 0; + INT8U sceneID = 1; + } + + request struct RemoveAllScenesRequest { + group_id groupID = 0; + } + + request struct StoreSceneRequest { + group_id groupID = 0; + INT8U sceneID = 1; + } + + request struct RecallSceneRequest { + group_id groupID = 0; + INT8U sceneID = 1; + optional nullable INT16U transitionTime = 2; + } + + request struct GetSceneMembershipRequest { + group_id groupID = 0; + } + + request struct EnhancedAddSceneRequest { + group_id groupID = 0; + INT8U sceneID = 1; + INT16U transitionTime = 2; + CHAR_STRING sceneName = 3; + ExtensionFieldSet extensionFieldSets[] = 4; + } + + request struct EnhancedViewSceneRequest { + group_id groupID = 0; + INT8U sceneID = 1; + } + + request struct CopySceneRequest { + ScenesCopyMode mode = 0; + group_id groupIdentifierFrom = 1; + INT8U sceneIdentifierFrom = 2; + group_id groupIdentifierTo = 3; + INT8U sceneIdentifierTo = 4; + } + + response struct AddSceneResponse = 0 { + status status = 0; + group_id groupID = 1; + INT8U sceneID = 2; + } + + response struct ViewSceneResponse = 1 { + status status = 0; + group_id groupID = 1; + INT8U sceneID = 2; + optional INT16U transitionTime = 3; + optional CHAR_STRING sceneName = 4; + optional ExtensionFieldSet extensionFieldSets[] = 5; + } + + response struct RemoveSceneResponse = 2 { + status status = 0; + group_id groupID = 1; + INT8U sceneID = 2; + } + + response struct RemoveAllScenesResponse = 3 { + status status = 0; + group_id groupID = 1; + } + + response struct StoreSceneResponse = 4 { + status status = 0; + group_id groupID = 1; + INT8U sceneID = 2; + } + + response struct GetSceneMembershipResponse = 6 { + status status = 0; + nullable INT8U capacity = 1; + group_id groupID = 2; + optional INT8U sceneList[] = 3; + } + + fabric command access(invoke: manage) AddScene(AddSceneRequest): AddSceneResponse = 0; + fabric command ViewScene(ViewSceneRequest): ViewSceneResponse = 1; + fabric command access(invoke: manage) RemoveScene(RemoveSceneRequest): RemoveSceneResponse = 2; + fabric command access(invoke: manage) RemoveAllScenes(RemoveAllScenesRequest): RemoveAllScenesResponse = 3; + fabric command access(invoke: manage) StoreScene(StoreSceneRequest): StoreSceneResponse = 4; + fabric command RecallScene(RecallSceneRequest): DefaultSuccess = 5; + fabric command GetSceneMembership(GetSceneMembershipRequest): GetSceneMembershipResponse = 6; + fabric command EnhancedAddScene(EnhancedAddSceneRequest): EnhancedAddSceneResponse = 64; + fabric command EnhancedViewScene(EnhancedViewSceneRequest): EnhancedViewSceneResponse = 65; + fabric command CopyScene(CopySceneRequest): CopySceneResponse = 66; +} + /** Attributes and commands for switching devices between 'On' and 'Off' states. */ server cluster OnOff = 6 { enum OnOffDelayedAllOffEffectVariant : ENUM8 { @@ -1858,6 +2002,23 @@ endpoint 1 { ram attribute clusterRevision default = 4; } + server cluster Scenes { + callback attribute sceneCount default = 0x00; + ram attribute currentScene default = 0x00; + ram attribute currentGroup default = 0x0000; + ram attribute sceneValid default = 0x00; + ram attribute nameSupport default = 0x80; + ram attribute lastConfiguredBy; + ram attribute sceneTableSize; + callback attribute remainingCapacity; + callback attribute generatedCommandList; + callback attribute acceptedCommandList; + callback attribute eventList; + callback attribute attributeList; + ram attribute featureMap default = 1; + ram attribute clusterRevision default = 4; + } + server cluster OnOff { persist attribute onOff default = 0x00; ram attribute globalSceneControl default = 0x01; diff --git a/examples/lighting-app/silabs/data_model/lighting-wifi-app.zap b/examples/lighting-app/silabs/data_model/lighting-wifi-app.zap index db1814e9758af8..d0948184a5d991 100644 --- a/examples/lighting-app/silabs/data_model/lighting-wifi-app.zap +++ b/examples/lighting-app/silabs/data_model/lighting-wifi-app.zap @@ -5696,7 +5696,7 @@ "mfgCode": null, "define": "SCENES_CLUSTER", "side": "server", - "enabled": 0, + "enabled": 1, "commands": [ { "name": "AddSceneResponse", @@ -5822,12 +5822,140 @@ "storageOption": "RAM", "singleton": 0, "bounded": 0, - "defaultValue": "", + "defaultValue": "0x80", "reportable": 1, "minInterval": 0, "maxInterval": 65344, "reportableChange": 0 }, + { + "name": "LastConfiguredBy", + "code": 5, + "mfgCode": null, + "side": "server", + "type": "node_id", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "SceneTableSize", + "code": 6, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "RemainingCapacity", + "code": 7, + "mfgCode": null, + "side": "server", + "type": "int8u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "GeneratedCommandList", + "code": 65528, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AcceptedCommandList", + "code": 65529, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "EventList", + "code": 65530, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AttributeList", + "code": 65531, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "FeatureMap", + "code": 65532, + "mfgCode": null, + "side": "server", + "type": "bitmap32", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "1", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, { "name": "ClusterRevision", "code": 65533, diff --git a/examples/lighting-app/silabs/src/AppTask.cpp b/examples/lighting-app/silabs/src/AppTask.cpp index 9194f4a1e9de08..4e48006ff4c919 100644 --- a/examples/lighting-app/silabs/src/AppTask.cpp +++ b/examples/lighting-app/silabs/src/AppTask.cpp @@ -25,6 +25,7 @@ #include #include +#include #include #include #include diff --git a/examples/placeholder/linux/apps/app1/config.matter b/examples/placeholder/linux/apps/app1/config.matter index 351a34c255320d..ffda88810287a3 100644 --- a/examples/placeholder/linux/apps/app1/config.matter +++ b/examples/placeholder/linux/apps/app1/config.matter @@ -6990,7 +6990,7 @@ endpoint 1 { } server cluster Scenes { - ram attribute sceneCount default = 0x00; + callback attribute sceneCount default = 0x00; ram attribute currentScene default = 0x00; ram attribute currentGroup default = 0x0000; ram attribute sceneValid default = 0x00; diff --git a/examples/placeholder/linux/apps/app2/config.matter b/examples/placeholder/linux/apps/app2/config.matter index c358c036735cac..387d171c8d2d14 100644 --- a/examples/placeholder/linux/apps/app2/config.matter +++ b/examples/placeholder/linux/apps/app2/config.matter @@ -6966,7 +6966,7 @@ endpoint 1 { } server cluster Scenes { - ram attribute sceneCount default = 0x00; + callback attribute sceneCount default = 0x00; ram attribute currentScene default = 0x00; ram attribute currentGroup default = 0x0000; ram attribute sceneValid default = 0x00; diff --git a/examples/thermostat/thermostat-common/thermostat.matter b/examples/thermostat/thermostat-common/thermostat.matter index dc19d42e74fc87..bf4da8b17df22d 100644 --- a/examples/thermostat/thermostat-common/thermostat.matter +++ b/examples/thermostat/thermostat-common/thermostat.matter @@ -2048,7 +2048,7 @@ endpoint 1 { } server cluster Scenes { - ram attribute sceneCount default = 0x00; + callback attribute sceneCount default = 0x00; ram attribute currentScene default = 0x00; ram attribute currentGroup default = 0x0000; ram attribute sceneValid default = 0x00; diff --git a/examples/window-app/common/window-app.matter b/examples/window-app/common/window-app.matter index 5404b09b067edc..4cc2e2a7941789 100644 --- a/examples/window-app/common/window-app.matter +++ b/examples/window-app/common/window-app.matter @@ -2251,7 +2251,7 @@ endpoint 1 { } server cluster Scenes { - ram attribute sceneCount default = 0x00; + callback attribute sceneCount default = 0x00; ram attribute currentScene default = 0x00; ram attribute currentGroup default = 0x0000; ram attribute sceneValid default = 0x00; @@ -2328,7 +2328,7 @@ endpoint 2 { } server cluster Scenes { - ram attribute sceneCount default = 0x00; + callback attribute sceneCount default = 0x00; ram attribute currentScene default = 0x00; ram attribute currentGroup default = 0x0000; ram attribute sceneValid default = 0x00; diff --git a/scripts/py_matter_idl/matter_idl/tests/inputs/cluster_with_commands.matter b/scripts/py_matter_idl/matter_idl/tests/inputs/cluster_with_commands.matter index c6128be7ffd870..d978642f26ef1b 100644 --- a/scripts/py_matter_idl/matter_idl/tests/inputs/cluster_with_commands.matter +++ b/scripts/py_matter_idl/matter_idl/tests/inputs/cluster_with_commands.matter @@ -29,7 +29,7 @@ server cluster OnOff = 6 { kLighting = 0x1; } - bitmap SceneFeatures : BITMAP32 { + bitmap ScenesFeature : BITMAP32 { kSceneNames = 0x1; } @@ -41,4 +41,3 @@ server cluster OnOff = 6 { command On(): DefaultSuccess = 1; command Toggle(): DefaultSuccess = 2; } - diff --git a/scripts/py_matter_idl/matter_idl/tests/inputs/large_all_clusters_app.matter b/scripts/py_matter_idl/matter_idl/tests/inputs/large_all_clusters_app.matter index 0e601eb46e84e7..def4f763b0ff3c 100644 --- a/scripts/py_matter_idl/matter_idl/tests/inputs/large_all_clusters_app.matter +++ b/scripts/py_matter_idl/matter_idl/tests/inputs/large_all_clusters_app.matter @@ -245,7 +245,7 @@ server cluster OnOff = 6 { kLighting = 0x1; } - bitmap SceneFeatures : BITMAP32 { + bitmap ScenesFeature : BITMAP32 { kSceneNames = 0x1; } @@ -4854,5 +4854,3 @@ endpoint 65534 { callback attribute clusterRevision default = 1; } } - - diff --git a/scripts/py_matter_idl/matter_idl/tests/inputs/large_lighting_app.matter b/scripts/py_matter_idl/matter_idl/tests/inputs/large_lighting_app.matter index d0295f3ef832d8..6020196c85103e 100644 --- a/scripts/py_matter_idl/matter_idl/tests/inputs/large_lighting_app.matter +++ b/scripts/py_matter_idl/matter_idl/tests/inputs/large_lighting_app.matter @@ -137,7 +137,7 @@ server cluster OnOff = 6 { kLighting = 0x1; } - bitmap SceneFeatures : BITMAP32 { + bitmap ScenesFeature : BITMAP32 { kSceneNames = 0x1; } @@ -2063,5 +2063,3 @@ endpoint 1 { ram attribute clusterRevision default = 3; } } - - diff --git a/scripts/tools/zap/tests/outputs/all-clusters-app/app-templates/IMClusterCommandHandler.cpp b/scripts/tools/zap/tests/outputs/all-clusters-app/app-templates/IMClusterCommandHandler.cpp index 12d30bf0ce350d..4c41228787c270 100644 --- a/scripts/tools/zap/tests/outputs/all-clusters-app/app-templates/IMClusterCommandHandler.cpp +++ b/scripts/tools/zap/tests/outputs/all-clusters-app/app-templates/IMClusterCommandHandler.cpp @@ -1398,97 +1398,6 @@ void DispatchServerCommand(CommandHandler * apCommandObj, const ConcreteCommandP } // namespace OperationalCredentials -namespace Scenes { - -void DispatchServerCommand(CommandHandler * apCommandObj, const ConcreteCommandPath & aCommandPath, TLV::TLVReader & aDataTlv) -{ - CHIP_ERROR TLVError = CHIP_NO_ERROR; - bool wasHandled = false; - { - switch (aCommandPath.mCommandId) - { - case Commands::AddScene::Id: { - Commands::AddScene::DecodableType commandData; - TLVError = DataModel::Decode(aDataTlv, commandData); - if (TLVError == CHIP_NO_ERROR) - { - wasHandled = emberAfScenesClusterAddSceneCallback(apCommandObj, aCommandPath, commandData); - } - break; - } - case Commands::ViewScene::Id: { - Commands::ViewScene::DecodableType commandData; - TLVError = DataModel::Decode(aDataTlv, commandData); - if (TLVError == CHIP_NO_ERROR) - { - wasHandled = emberAfScenesClusterViewSceneCallback(apCommandObj, aCommandPath, commandData); - } - break; - } - case Commands::RemoveScene::Id: { - Commands::RemoveScene::DecodableType commandData; - TLVError = DataModel::Decode(aDataTlv, commandData); - if (TLVError == CHIP_NO_ERROR) - { - wasHandled = emberAfScenesClusterRemoveSceneCallback(apCommandObj, aCommandPath, commandData); - } - break; - } - case Commands::RemoveAllScenes::Id: { - Commands::RemoveAllScenes::DecodableType commandData; - TLVError = DataModel::Decode(aDataTlv, commandData); - if (TLVError == CHIP_NO_ERROR) - { - wasHandled = emberAfScenesClusterRemoveAllScenesCallback(apCommandObj, aCommandPath, commandData); - } - break; - } - case Commands::StoreScene::Id: { - Commands::StoreScene::DecodableType commandData; - TLVError = DataModel::Decode(aDataTlv, commandData); - if (TLVError == CHIP_NO_ERROR) - { - wasHandled = emberAfScenesClusterStoreSceneCallback(apCommandObj, aCommandPath, commandData); - } - break; - } - case Commands::RecallScene::Id: { - Commands::RecallScene::DecodableType commandData; - TLVError = DataModel::Decode(aDataTlv, commandData); - if (TLVError == CHIP_NO_ERROR) - { - wasHandled = emberAfScenesClusterRecallSceneCallback(apCommandObj, aCommandPath, commandData); - } - break; - } - case Commands::GetSceneMembership::Id: { - Commands::GetSceneMembership::DecodableType commandData; - TLVError = DataModel::Decode(aDataTlv, commandData); - if (TLVError == CHIP_NO_ERROR) - { - wasHandled = emberAfScenesClusterGetSceneMembershipCallback(apCommandObj, aCommandPath, commandData); - } - break; - } - default: { - // Unrecognized command ID, error status will apply. - apCommandObj->AddStatus(aCommandPath, Protocols::InteractionModel::Status::UnsupportedCommand); - ChipLogError(Zcl, "Unknown command " ChipLogFormatMEI " for cluster " ChipLogFormatMEI, - ChipLogValueMEI(aCommandPath.mCommandId), ChipLogValueMEI(aCommandPath.mClusterId)); - return; - } - } - } - - if (CHIP_NO_ERROR != TLVError || !wasHandled) - { - apCommandObj->AddStatus(aCommandPath, Protocols::InteractionModel::Status::InvalidCommand); - ChipLogProgress(Zcl, "Failed to dispatch command, TLVError=%" CHIP_ERROR_FORMAT, TLVError.Format()); - } -} - -} // namespace Scenes - namespace SoftwareDiagnostics { void DispatchServerCommand(CommandHandler * apCommandObj, const ConcreteCommandPath & aCommandPath, TLV::TLVReader & aDataTlv) @@ -2027,9 +1936,6 @@ void DispatchSingleClusterCommand(const ConcreteCommandPath & aCommandPath, TLV: case Clusters::OperationalCredentials::Id: Clusters::OperationalCredentials::DispatchServerCommand(apCommandObj, aCommandPath, aReader); break; - case Clusters::Scenes::Id: - Clusters::Scenes::DispatchServerCommand(apCommandObj, aCommandPath, aReader); - break; case Clusters::SoftwareDiagnostics::Id: Clusters::SoftwareDiagnostics::DispatchServerCommand(apCommandObj, aCommandPath, aReader); break; diff --git a/scripts/tools/zap/tests/outputs/all-clusters-app/app-templates/endpoint_config.h b/scripts/tools/zap/tests/outputs/all-clusters-app/app-templates/endpoint_config.h index befeed9758c537..17faea8ccaa42d 100644 --- a/scripts/tools/zap/tests/outputs/all-clusters-app/app-templates/endpoint_config.h +++ b/scripts/tools/zap/tests/outputs/all-clusters-app/app-templates/endpoint_config.h @@ -758,13 +758,13 @@ { ZAP_SIMPLE_DEFAULT(4), 0x0000FFFD, 2, ZAP_TYPE(INT16U), 0 }, /* ClusterRevision */ \ \ /* Endpoint: 1, Cluster: Scenes (server) */ \ - { ZAP_SIMPLE_DEFAULT(0x00), 0x00000000, 1, ZAP_TYPE(INT8U), 0 }, /* SceneCount */ \ - { ZAP_SIMPLE_DEFAULT(0x00), 0x00000001, 1, ZAP_TYPE(INT8U), 0 }, /* CurrentScene */ \ - { ZAP_SIMPLE_DEFAULT(0x0000), 0x00000002, 2, ZAP_TYPE(GROUP_ID), 0 }, /* CurrentGroup */ \ - { ZAP_SIMPLE_DEFAULT(0x00), 0x00000003, 1, ZAP_TYPE(BOOLEAN), 0 }, /* SceneValid */ \ - { ZAP_EMPTY_DEFAULT(), 0x00000004, 1, ZAP_TYPE(BITMAP8), 0 }, /* NameSupport */ \ - { ZAP_SIMPLE_DEFAULT(0), 0x0000FFFC, 4, ZAP_TYPE(BITMAP32), 0 }, /* FeatureMap */ \ - { ZAP_SIMPLE_DEFAULT(4), 0x0000FFFD, 2, ZAP_TYPE(INT16U), 0 }, /* ClusterRevision */ \ + { ZAP_EMPTY_DEFAULT(), 0x00000000, 1, ZAP_TYPE(INT8U), ZAP_ATTRIBUTE_MASK(EXTERNAL_STORAGE) }, /* SceneCount */ \ + { ZAP_SIMPLE_DEFAULT(0x00), 0x00000001, 1, ZAP_TYPE(INT8U), 0 }, /* CurrentScene */ \ + { ZAP_SIMPLE_DEFAULT(0x0000), 0x00000002, 2, ZAP_TYPE(GROUP_ID), 0 }, /* CurrentGroup */ \ + { ZAP_SIMPLE_DEFAULT(0x00), 0x00000003, 1, ZAP_TYPE(BOOLEAN), 0 }, /* SceneValid */ \ + { ZAP_EMPTY_DEFAULT(), 0x00000004, 1, ZAP_TYPE(BITMAP8), 0 }, /* NameSupport */ \ + { ZAP_SIMPLE_DEFAULT(0), 0x0000FFFC, 4, ZAP_TYPE(BITMAP32), 0 }, /* FeatureMap */ \ + { ZAP_SIMPLE_DEFAULT(4), 0x0000FFFD, 2, ZAP_TYPE(INT16U), 0 }, /* ClusterRevision */ \ \ /* Endpoint: 1, Cluster: On/Off (server) */ \ { ZAP_SIMPLE_DEFAULT(0x00), 0x00000000, 1, ZAP_TYPE(BOOLEAN), ZAP_ATTRIBUTE_MASK(TOKENIZE) }, /* OnOff */ \ @@ -1533,9 +1533,6 @@ (EmberAfGenericClusterFunction) emberAfTimeFormatLocalizationClusterServerInitCallback, \ (EmberAfGenericClusterFunction) MatterTimeFormatLocalizationClusterServerPreAttributeChangedCallback, \ }; \ - const EmberAfGenericClusterFunction chipFuncArrayScenesServer[] = { \ - (EmberAfGenericClusterFunction) emberAfScenesClusterServerInitCallback, \ - }; \ const EmberAfGenericClusterFunction chipFuncArrayOnOffServer[] = { \ (EmberAfGenericClusterFunction) emberAfOnOffClusterServerInitCallback, \ (EmberAfGenericClusterFunction) MatterOnOffClusterServerShutdownCallback, \ @@ -2322,9 +2319,9 @@ .clusterId = 0x00000005, \ .attributes = ZAP_ATTRIBUTE_INDEX(240), \ .attributeCount = 7, \ - .clusterSize = 12, \ - .mask = ZAP_CLUSTER_MASK(SERVER) | ZAP_CLUSTER_MASK(INIT_FUNCTION), \ - .functions = chipFuncArrayScenesServer, \ + .clusterSize = 11, \ + .mask = ZAP_CLUSTER_MASK(SERVER), \ + .functions = NULL, \ .acceptedCommandList = ZAP_GENERATED_COMMANDS_INDEX( 94 ), \ .generatedCommandList = ZAP_GENERATED_COMMANDS_INDEX( 102 ), \ .eventList = nullptr, \ @@ -2950,7 +2947,7 @@ // This is an array of EmberAfEndpointType structures. #define GENERATED_ENDPOINT_TYPES \ { \ - { ZAP_CLUSTER_INDEX(0), 28, 361 }, { ZAP_CLUSTER_INDEX(28), 44, 3459 }, { ZAP_CLUSTER_INDEX(72), 5, 105 }, \ + { ZAP_CLUSTER_INDEX(0), 28, 361 }, { ZAP_CLUSTER_INDEX(28), 44, 3458 }, { ZAP_CLUSTER_INDEX(72), 5, 105 }, \ { ZAP_CLUSTER_INDEX(77), 1, 0 }, \ } @@ -2963,7 +2960,7 @@ static_assert(ATTRIBUTE_LARGEST <= CHIP_CONFIG_MAX_ATTRIBUTE_STORE_ELEMENT_SIZE, #define ATTRIBUTE_SINGLETONS_SIZE (37) // Total size of attribute storage -#define ATTRIBUTE_MAX_SIZE (3925) +#define ATTRIBUTE_MAX_SIZE (3924) // Number of fixed endpoints #define FIXED_ENDPOINT_COUNT (4) diff --git a/src/app/chip_data_model.gni b/src/app/chip_data_model.gni index c2026f61f50f08..1a55d0c524cf99 100644 --- a/src/app/chip_data_model.gni +++ b/src/app/chip_data_model.gni @@ -160,19 +160,13 @@ template("chip_data_model") { "${_app_root}/clusters/identify-server/identify-server.h", "${_app_root}/clusters/level-control/level-control.h", "${_app_root}/clusters/on-off-server/on-off-server.h", - "${_app_root}/clusters/scenes/ExtensionFieldSets.h", - "${_app_root}/clusters/scenes/ExtensionFieldSetsImpl.cpp", - "${_app_root}/clusters/scenes/ExtensionFieldSetsImpl.h", - "${_app_root}/clusters/scenes/SceneTable.h", - "${_app_root}/clusters/scenes/SceneTableImpl.cpp", - "${_app_root}/clusters/scenes/SceneTableImpl.h", - "${_app_root}/clusters/scenes/scenes-tokens.h", - "${_app_root}/clusters/scenes/scenes.h", - - # Disable CM cluster table tests until update is done - # https://github.com/project-chip/connectedhomeip/issues/24425 - # "${_app_root}/util/ClientMonitoringRegistrationTable.cpp", - # "${_app_root}/util/ClientMonitoringRegistrationTable.h", + "${_app_root}/clusters/scenes-server/ExtensionFieldSets.h", + "${_app_root}/clusters/scenes-server/ExtensionFieldSetsImpl.h", + "${_app_root}/clusters/scenes-server/SceneTable.h", + "${_app_root}/clusters/scenes-server/SceneTableImpl.h", + "${_app_root}/clusters/scenes-server/scenes-server.h", + "${_app_root}/util/ClientMonitoringRegistrationTable.cpp", + "${_app_root}/util/ClientMonitoringRegistrationTable.h", "${_app_root}/util/DataModelHandler.cpp", "${_app_root}/util/attribute-size-util.cpp", "${_app_root}/util/attribute-storage.cpp", @@ -258,6 +252,12 @@ template("chip_data_model") { "${_app_root}/clusters/${cluster}/PendingNotificationMap.cpp", "${_app_root}/clusters/${cluster}/PendingNotificationMap.h", ] + } else if (cluster == "scenes-server") { + sources += [ + "${_app_root}/clusters/${cluster}/${cluster}.cpp", + "${_app_root}/clusters/scenes-server/ExtensionFieldSetsImpl.cpp", + "${_app_root}/clusters/scenes-server/SceneTableImpl.cpp", + ] } else { sources += [ "${_app_root}/clusters/${cluster}/${cluster}.cpp" ] } diff --git a/src/app/clusters/groups-server/groups-server.cpp b/src/app/clusters/groups-server/groups-server.cpp index f048991ba112e4..00196ad94c9419 100644 --- a/src/app/clusters/groups-server/groups-server.cpp +++ b/src/app/clusters/groups-server/groups-server.cpp @@ -29,7 +29,7 @@ #include #ifdef EMBER_AF_PLUGIN_SCENES -#include +#include #endif // EMBER_AF_PLUGIN_SCENES using namespace chip; @@ -282,8 +282,8 @@ bool emberAfGroupsClusterRemoveGroupCallback(app::CommandHandler * commandObj, c Groups::Commands::RemoveGroupResponse::Type response; #ifdef EMBER_AF_PLUGIN_SCENES - // If a group is, removed the scenes associated with that group SHOULD be removed. - emberAfScenesClusterRemoveScenesInGroupCallback(commandPath.mEndpointId, commandData.groupID); + // If a group is removed the scenes associated with that group SHOULD be removed. + Scenes::ScenesServer::Instance().GroupWillBeRemoved(fabricIndex, commandPath.mEndpointId, commandData.groupID); #endif response.groupID = commandData.groupID; response.status = GroupRemove(fabricIndex, commandPath.mEndpointId, commandData.groupID); @@ -311,11 +311,11 @@ bool emberAfGroupsClusterRemoveAllGroupsCallback(app::CommandHandler * commandOb { if (commandPath.mEndpointId == mapping.endpoint_id) { - emberAfScenesClusterRemoveScenesInGroupCallback(mapping.endpoint_id, mapping.group_id); + Scenes::ScenesServer::Instance().GroupWillBeRemoved(fabricIndex, mapping.endpoint_id, mapping.group_id); } } iter->Release(); - emberAfScenesClusterRemoveScenesInGroupCallback(commandPath.mEndpointId, ZCL_SCENES_GLOBAL_SCENE_GROUP_ID); + Scenes::ScenesServer::Instance().GroupWillBeRemoved(fabricIndex, commandPath.mEndpointId, ZCL_SCENES_GLOBAL_SCENE_GROUP_ID); } #endif diff --git a/src/app/clusters/level-control/level-control.cpp b/src/app/clusters/level-control/level-control.cpp index 5a732074dd0851..66533df0a88e0e 100644 --- a/src/app/clusters/level-control/level-control.cpp +++ b/src/app/clusters/level-control/level-control.cpp @@ -35,7 +35,7 @@ #include #ifdef EMBER_AF_PLUGIN_SCENES -#include +#include #endif // EMBER_AF_PLUGIN_SCENES #ifdef EMBER_AF_PLUGIN_ON_OFF @@ -283,7 +283,7 @@ void emberAfLevelControlClusterServerTickCallback(EndpointId endpoint) // The level has changed, so the scene is no longer valid. if (emberAfContainsServer(endpoint, Scenes::Id)) { - emberAfScenesClusterMakeInvalidCallback(endpoint); + Scenes::ScenesServer::Instance().MakeSceneInvalid(endpoint); } #endif // EMBER_AF_PLUGIN_SCENES diff --git a/src/app/clusters/on-off-server/on-off-server.cpp b/src/app/clusters/on-off-server/on-off-server.cpp index b201c022326a0d..b13489f7308792 100644 --- a/src/app/clusters/on-off-server/on-off-server.cpp +++ b/src/app/clusters/on-off-server/on-off-server.cpp @@ -26,7 +26,7 @@ #include #ifdef EMBER_AF_PLUGIN_SCENES -#include +#include #endif // EMBER_AF_PLUGIN_SCENES #ifdef EMBER_AF_PLUGIN_LEVEL_CONTROL @@ -264,13 +264,11 @@ EmberAfStatus OnOffServer::setOnOffValue(chip::EndpointId endpoint, chip::Comman } #ifdef EMBER_AF_PLUGIN_SCENES - // the scene has been changed (the value of on/off has changed) so - // the current scene as described in the attribute table is invalid, - // so mark it as invalid (just writes the valid/invalid attribute) - if (emberAfContainsServer(endpoint, Scenes::Id)) - { - emberAfScenesClusterMakeInvalidCallback(endpoint); - } + // the scene has been changed (the value of on/off has changed) so + // the current scene as described in the attribute table is invalid, + // so mark it as invalid (just writes the valid/invalid attribute) + + Scenes::ScenesServer::Instance().MakeSceneInvalid(endpoint); #endif // EMBER_AF_PLUGIN_SCENES // The returned status is based solely on the On/Off cluster. Errors in the @@ -420,10 +418,8 @@ bool OnOffServer::offWithEffectCommand(app::CommandHandler * commandObj, const a { groupId = commandObj->GetExchangeContext()->GetSessionHandle()->AsIncomingGroupSession()->GetGroupId(); } - - emberAfScenesClusterStoreCurrentSceneCallback(fabric, endpoint, groupId, ZCL_SCENES_GLOBAL_SCENE_SCENE_ID); + Scenes::ScenesServer::Instance().StoreCurrentScene(fabric, endpoint, groupId, ZCL_SCENES_GLOBAL_SCENE_SCENE_ID); #endif // EMBER_AF_PLUGIN_SCENES - OnOff::Attributes::GlobalSceneControl::Set(endpoint, false); } @@ -482,7 +478,7 @@ bool OnOffServer::OnWithRecallGlobalSceneCommand(app::CommandHandler * commandOb groupId = commandObj->GetExchangeContext()->GetSessionHandle()->AsIncomingGroupSession()->GetGroupId(); } - emberAfScenesClusterRecallSavedSceneCallback(fabric, endpoint, groupId, ZCL_SCENES_GLOBAL_SCENE_SCENE_ID); + Scenes::ScenesServer::Instance().RecallScene(fabric, endpoint, groupId, ZCL_SCENES_GLOBAL_SCENE_SCENE_ID); #endif // EMBER_AF_PLUGIN_SCENES OnOff::Attributes::GlobalSceneControl::Set(endpoint, true); diff --git a/src/app/clusters/scenes/BUILD.gn b/src/app/clusters/scenes-server/BUILD.gn similarity index 100% rename from src/app/clusters/scenes/BUILD.gn rename to src/app/clusters/scenes-server/BUILD.gn diff --git a/src/app/clusters/scenes/ExtensionFieldSets.h b/src/app/clusters/scenes-server/ExtensionFieldSets.h similarity index 100% rename from src/app/clusters/scenes/ExtensionFieldSets.h rename to src/app/clusters/scenes-server/ExtensionFieldSets.h diff --git a/src/app/clusters/scenes/ExtensionFieldSetsImpl.cpp b/src/app/clusters/scenes-server/ExtensionFieldSetsImpl.cpp similarity index 100% rename from src/app/clusters/scenes/ExtensionFieldSetsImpl.cpp rename to src/app/clusters/scenes-server/ExtensionFieldSetsImpl.cpp diff --git a/src/app/clusters/scenes/ExtensionFieldSetsImpl.h b/src/app/clusters/scenes-server/ExtensionFieldSetsImpl.h similarity index 98% rename from src/app/clusters/scenes/ExtensionFieldSetsImpl.h rename to src/app/clusters/scenes-server/ExtensionFieldSetsImpl.h index 2525546e0cdb7a..381c592941b079 100644 --- a/src/app/clusters/scenes/ExtensionFieldSetsImpl.h +++ b/src/app/clusters/scenes-server/ExtensionFieldSetsImpl.h @@ -17,7 +17,7 @@ #pragma once -#include +#include #include namespace chip { diff --git a/src/app/clusters/scenes/SceneTable.h b/src/app/clusters/scenes-server/SceneTable.h similarity index 87% rename from src/app/clusters/scenes/SceneTable.h rename to src/app/clusters/scenes-server/SceneTable.h index 4352158db1f217..0453161b51e446 100644 --- a/src/app/clusters/scenes/SceneTable.h +++ b/src/app/clusters/scenes-server/SceneTable.h @@ -17,7 +17,7 @@ #pragma once #include -#include +#include #include #include #include @@ -28,19 +28,19 @@ namespace chip { namespace scenes { // Storage index for scenes in nvm -typedef uint8_t SceneIndex; +typedef uint16_t SceneIndex; typedef uint32_t TransitionTimeMs; typedef uint32_t SceneTransitionTime; -constexpr GroupId kGlobalGroupSceneId = 0x0000; -constexpr SceneIndex kUndefinedSceneIndex = 0xff; -constexpr SceneId kUndefinedSceneId = 0xff; -static constexpr uint8_t kMaxScenesPerFabric = CHIP_CONFIG_SCENES_MAX_PER_FABRIC; -static constexpr uint8_t kMaxScenesGlobal = CHIP_CONFIG_SCENES_MAX_NUMBER; +constexpr GroupId kGlobalGroupSceneId = 0x0000; +constexpr SceneIndex kUndefinedSceneIndex = 0xffff; +constexpr SceneId kUndefinedSceneId = 0xff; -static constexpr size_t kIteratorsMax = CHIP_CONFIG_MAX_SCENES_CONCURRENT_ITERATORS; -static constexpr size_t kSceneNameMaxLength = CHIP_CONFIG_SCENES_CLUSTER_MAXIMUM_NAME_LENGTH; +static constexpr size_t kIteratorsMax = CHIP_CONFIG_MAX_SCENES_CONCURRENT_ITERATORS; +static constexpr size_t kSceneNameMaxLength = CHIP_CONFIG_SCENES_CLUSTER_MAXIMUM_NAME_LENGTH; +static constexpr size_t kScenesMaxTransitionTimeS = 6000u; +static constexpr size_t kScenesMaxTransitionTime100ms = kScenesMaxTransitionTimeS * 10; /// @brief SceneHandlers are meant as interface between various clusters and the Scene table. /// When a scene command involving extension field sets is received, the Scene Table will go through @@ -84,7 +84,6 @@ class SceneHandler : public IntrusiveListNodeBase<> /// @param serialisedBytes[out] Buffer to fill from the ExtensionFieldSet in command /// @return CHIP_NO_ERROR if successful, CHIP_ERROR value otherwise /// @note Only gets called after the scene-cluster has previously verified that the endpoint,cluster pair is supported by - /// the handler. It is therefore the implementation's reponsibility to also implement the SupportsCluster method. virtual CHIP_ERROR SerializeAdd(EndpointId endpoint, const app::Clusters::Scenes::Structs::ExtensionFieldSet::DecodableType & extensionFieldSet, @@ -135,30 +134,22 @@ class SceneTable /// @brief struct used to identify a scene in storage by 3 ids, endpoint, group and scene struct SceneStorageId { - // Identifies endpoint to which this scene applies - EndpointId mEndpointId = kInvalidEndpointId; // Identifies group within the scope of the given fabric GroupId mGroupId = kGlobalGroupSceneId; SceneId mSceneId = kUndefinedSceneId; SceneStorageId() = default; - SceneStorageId(EndpointId endpoint, SceneId id, GroupId groupId = kGlobalGroupSceneId) : - mEndpointId(endpoint), mGroupId(groupId), mSceneId(id) - {} + SceneStorageId(SceneId id, GroupId groupId = kGlobalGroupSceneId) : mGroupId(groupId), mSceneId(id) {} void Clear() { - mEndpointId = kInvalidEndpointId; - mGroupId = kGlobalGroupSceneId; - mSceneId = kUndefinedSceneId; + mGroupId = kGlobalGroupSceneId; + mSceneId = kUndefinedSceneId; } - bool IsValid() { return (mEndpointId != kInvalidEndpointId) && (mSceneId != kUndefinedSceneId); } + bool IsValid() { return (mSceneId != kUndefinedSceneId); } - bool operator==(const SceneStorageId & other) - { - return (mEndpointId == other.mEndpointId && mGroupId == other.mGroupId && mSceneId == other.mSceneId); - } + bool operator==(const SceneStorageId & other) { return (mGroupId == other.mGroupId && mSceneId == other.mSceneId); } }; /// @brief struct used to store data held in a scene @@ -270,14 +261,15 @@ class SceneTable virtual void Finish() = 0; // Global scene count - virtual CHIP_ERROR GetGlobalSceneCount(uint8_t & scene_count) = 0; + virtual CHIP_ERROR GetEndpointSceneCount(uint8_t & scene_count) = 0; + virtual CHIP_ERROR GetFabricSceneCount(FabricIndex fabric_index, uint8_t & scene_count) = 0; // Data - virtual CHIP_ERROR GetRemainingCapacity(FabricIndex fabric_index, uint8_t & capacity) = 0; - virtual CHIP_ERROR SetSceneTableEntry(FabricIndex fabric_index, const SceneTableEntry & entry) = 0; - virtual CHIP_ERROR GetSceneTableEntry(FabricIndex fabric_index, SceneStorageId scene_id, SceneTableEntry & entry) = 0; - virtual CHIP_ERROR RemoveSceneTableEntry(FabricIndex fabric_index, SceneStorageId scene_id) = 0; - virtual CHIP_ERROR RemoveSceneTableEntryAtPosition(FabricIndex fabric_index, SceneIndex scene_idx) = 0; + virtual CHIP_ERROR GetRemainingCapacity(FabricIndex fabric_index, uint8_t & capacity) = 0; + virtual CHIP_ERROR SetSceneTableEntry(FabricIndex fabric_index, const SceneTableEntry & entry) = 0; + virtual CHIP_ERROR GetSceneTableEntry(FabricIndex fabric_index, SceneStorageId scene_id, SceneTableEntry & entry) = 0; + virtual CHIP_ERROR RemoveSceneTableEntry(FabricIndex fabric_index, SceneStorageId scene_id) = 0; + virtual CHIP_ERROR RemoveSceneTableEntryAtPosition(EndpointId endpoint, FabricIndex fabric_index, SceneIndex scene_idx) = 0; // Groups virtual CHIP_ERROR GetAllSceneIdsInGroup(FabricIndex fabric_index, GroupId group_id, Span & scene_list) = 0; diff --git a/src/app/clusters/scenes/SceneTableImpl.cpp b/src/app/clusters/scenes-server/SceneTableImpl.cpp similarity index 75% rename from src/app/clusters/scenes/SceneTableImpl.cpp rename to src/app/clusters/scenes-server/SceneTableImpl.cpp index a34cd9179ddcc7..f0b5131f033776 100644 --- a/src/app/clusters/scenes/SceneTableImpl.cpp +++ b/src/app/clusters/scenes-server/SceneTableImpl.cpp @@ -15,8 +15,7 @@ * limitations under the License. */ -#include -#include +#include #include #include @@ -33,10 +32,9 @@ namespace scenes { /// kTransitionTime: Tag for the transition time of the scene in miliseconds enum class TagScene : uint8_t { - kGlobalSceneCount = 1, + kEndpointSceneCount = 1, kSceneCount, kStorageIDArray, - kEndpointID, kGroupID, kSceneID, kName, @@ -52,18 +50,20 @@ using SceneData = DefaultSceneTableImpl::SceneData; // byte value, 1 byte end struct. 8 Bytes leaves space for potential increase in count_value size. static constexpr size_t kPersistentBufferSceneCountBytes = 8; -struct GlobalSceneCount : public PersistentData +struct EndpointSceneCount : public PersistentData { - uint8_t count_value = 0; + EndpointId endpoint_id = kInvalidEndpointId; + uint8_t count_value = 0; - GlobalSceneCount(uint8_t count = 0) : count_value(count) {} - ~GlobalSceneCount() {} + EndpointSceneCount(EndpointId endpoint, uint8_t count = 0) : endpoint_id(endpoint), count_value(count) {} + ~EndpointSceneCount() {} void Clear() override { count_value = 0; } CHIP_ERROR UpdateKey(StorageKeyName & key) override { - key = DefaultStorageKeyAllocator::GlobalSceneCountKey(); + VerifyOrReturnError(kInvalidEndpointId != endpoint_id, CHIP_ERROR_INVALID_ARGUMENT); + key = DefaultStorageKeyAllocator::EndpointSceneCountKey(endpoint_id); return CHIP_NO_ERROR; } @@ -71,7 +71,7 @@ struct GlobalSceneCount : public PersistentData { + EndpointId endpoint_id = kInvalidEndpointId; FabricIndex fabric_index = kUndefinedFabricIndex; SceneIndex index = 0; bool first = true; - SceneTableData() : SceneTableEntry() {} - SceneTableData(FabricIndex fabric) : fabric_index(fabric) {} - SceneTableData(FabricIndex fabric, SceneIndex idx) : fabric_index(fabric), index(idx) {} - SceneTableData(FabricIndex fabric, SceneStorageId storageId) : SceneTableEntry(storageId), fabric_index(fabric) {} - SceneTableData(FabricIndex fabric, SceneStorageId storageId, SceneData data) : - SceneTableEntry(storageId, data), fabric_index(fabric) + SceneTableData(EndpointId endpoint, FabricIndex fabric, SceneIndex idx = 0) : + endpoint_id(endpoint), fabric_index(fabric), index(idx) + {} + SceneTableData(EndpointId endpoint, FabricIndex fabric, SceneStorageId storageId) : + SceneTableEntry(storageId), endpoint_id(endpoint), fabric_index(fabric) + {} + SceneTableData(EndpointId endpoint, FabricIndex fabric, SceneStorageId storageId, SceneData data) : + SceneTableEntry(storageId, data), endpoint_id(endpoint), fabric_index(fabric) {} CHIP_ERROR UpdateKey(StorageKeyName & key) override { VerifyOrReturnError(kUndefinedFabricIndex != fabric_index, CHIP_ERROR_INVALID_FABRIC_INDEX); - key = DefaultStorageKeyAllocator::FabricSceneKey(fabric_index, index); + VerifyOrReturnError(kInvalidEndpointId != endpoint_id, CHIP_ERROR_INVALID_ARGUMENT); + key = DefaultStorageKeyAllocator::FabricSceneKey(fabric_index, endpoint_id, index); return CHIP_NO_ERROR; } @@ -135,7 +139,6 @@ struct SceneTableData : public SceneTableEntry, PersistentData { + EndpointId endpoint_id; FabricIndex fabric_index; uint8_t scene_count = 0; - uint8_t max_scenes_per_fabric; - uint8_t max_scenes_global; + uint16_t max_scenes_per_fabric; + uint16_t max_scenes_per_endpoint; SceneStorageId scene_map[kMaxScenesPerFabric]; - FabricSceneData(FabricIndex fabric = kUndefinedFabricIndex, uint8_t maxScenesPerFabric = kMaxScenesPerFabric, - uint8_t maxScenesGlobal = kMaxScenesGlobal) : - fabric_index(fabric), - max_scenes_per_fabric(maxScenesPerFabric), max_scenes_global(maxScenesGlobal) + FabricSceneData(EndpointId endpoint = kInvalidEndpointId, FabricIndex fabric = kUndefinedFabricIndex, + uint16_t maxScenesPerFabric = kMaxScenesPerFabric, uint16_t maxScenesPerEndpoint = kMaxScenesPerEndpoint) : + endpoint_id(endpoint), + fabric_index(fabric), max_scenes_per_fabric(maxScenesPerFabric), max_scenes_per_endpoint(maxScenesPerEndpoint) {} CHIP_ERROR UpdateKey(StorageKeyName & key) override { VerifyOrReturnError(kUndefinedFabricIndex != fabric_index, CHIP_ERROR_INVALID_FABRIC_INDEX); - key = DefaultStorageKeyAllocator::FabricSceneDataKey(fabric_index); + VerifyOrReturnError(kInvalidEndpointId != endpoint_id, CHIP_ERROR_INVALID_ARGUMENT); + key = DefaultStorageKeyAllocator::FabricSceneDataKey(fabric_index, endpoint_id); return CHIP_NO_ERROR; } void Clear() override { scene_count = 0; - for (uint8_t i = 0; i < max_scenes_per_fabric; i++) + for (uint16_t i = 0; i < max_scenes_per_fabric; i++) { scene_map[i].Clear(); } @@ -241,11 +244,10 @@ struct FabricSceneData : public PersistentData writer.StartContainer(TLV::ContextTag(TagScene::kStorageIDArray), TLV::kTLVType_Array, sceneMapContainer)); // Storing the scene map - for (uint8_t i = 0; i < max_scenes_per_fabric; i++) + for (uint16_t i = 0; i < max_scenes_per_fabric; i++) { TLV::TLVType sceneIdContainer; ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, sceneIdContainer)); - ReturnErrorOnFailure(writer.Put(TLV::ContextTag(TagScene::kEndpointID), (scene_map[i].mEndpointId))); ReturnErrorOnFailure(writer.Put(TLV::ContextTag(TagScene::kGroupID), (scene_map[i].mGroupId))); ReturnErrorOnFailure(writer.Put(TLV::ContextTag(TagScene::kSceneID), (scene_map[i].mSceneId))); ReturnErrorOnFailure(writer.EndContainer(sceneIdContainer)); @@ -277,12 +279,12 @@ struct FabricSceneData : public PersistentData ReturnErrorOnFailure(reader.EnterContainer(fabricSceneContainer)); ReturnErrorOnFailure(reader.Next(TLV::ContextTag(TagScene::kSceneCount))); ReturnErrorOnFailure(reader.Get(scene_count)); - scene_count = min(scene_count, max_scenes_per_fabric); + scene_count = min(scene_count, static_cast(max_scenes_per_fabric)); ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Array, TLV::ContextTag(TagScene::kStorageIDArray))); TLV::TLVType sceneMapContainer; ReturnErrorOnFailure(reader.EnterContainer(sceneMapContainer)); - uint8_t i = 0; + uint16_t i = 0; CHIP_ERROR err; deleted_scenes_count = 0; @@ -292,8 +294,6 @@ struct FabricSceneData : public PersistentData if (i < max_scenes_per_fabric) { ReturnErrorOnFailure(reader.EnterContainer(sceneIdContainer)); - ReturnErrorOnFailure(reader.Next(TLV::ContextTag(TagScene::kEndpointID))); - ReturnErrorOnFailure(reader.Get(scene_map[i].mEndpointId)); ReturnErrorOnFailure(reader.Next(TLV::ContextTag(TagScene::kGroupID))); ReturnErrorOnFailure(reader.Get(scene_map[i].mGroupId)); ReturnErrorOnFailure(reader.Next(TLV::ContextTag(TagScene::kSceneID))); @@ -302,10 +302,8 @@ struct FabricSceneData : public PersistentData } else { - SceneTableData scene(fabric_index, i); + SceneTableData scene(endpoint_id, fabric_index, i); ReturnErrorOnFailure(reader.EnterContainer(sceneIdContainer)); - ReturnErrorOnFailure(reader.Next(TLV::ContextTag(TagScene::kEndpointID))); - ReturnErrorOnFailure(reader.Get(scene.mStorageId.mEndpointId)); ReturnErrorOnFailure(reader.Next(TLV::ContextTag(TagScene::kGroupID))); ReturnErrorOnFailure(reader.Get(scene.mStorageId.mGroupId)); ReturnErrorOnFailure(reader.Next(TLV::ContextTag(TagScene::kSceneID))); @@ -323,8 +321,8 @@ struct FabricSceneData : public PersistentData return reader.ExitContainer(fabricSceneContainer); } - /// @brief Finds the index where to insert current scene by going through the whole table and looking if the scene is already in - /// there. If the target is not in the table, sets idx to the first empty space + /// @brief Finds the index where to insert current scene by going through the endpoint's table and looking if the scene is + /// already in there. If the target is not in the table, sets idx to the first empty space /// @param target_scene Storage Id of scene to store /// @param idx Index where target or space is found /// @return CHIP_NO_ERROR if managed to find the target scene, CHIP_ERROR_NOT_FOUND if not found and space left @@ -332,7 +330,7 @@ struct FabricSceneData : public PersistentData CHIP_ERROR Find(SceneStorageId target_scene, SceneIndex & idx) { SceneIndex firstFreeIdx = kUndefinedSceneIndex; // storage index if scene not found - uint8_t index = 0; + uint16_t index = 0; while (index < max_scenes_per_fabric) { @@ -360,7 +358,7 @@ struct FabricSceneData : public PersistentData CHIP_ERROR SaveScene(PersistentStorageDelegate * storage, const SceneTableEntry & entry) { CHIP_ERROR err = CHIP_NO_ERROR; - SceneTableData scene(fabric_index, entry.mStorageId, entry.mStorageData); + SceneTableData scene(endpoint_id, fabric_index, entry.mStorageId, entry.mStorageData); // Look for empty storage space err = this->Find(entry.mStorageId, scene.index); @@ -373,11 +371,11 @@ struct FabricSceneData : public PersistentData if (CHIP_ERROR_NOT_FOUND == err) // If not found, scene.index should be the first free index { // Update the global scene count - GlobalSceneCount global_scene_count; - ReturnErrorOnFailure(global_scene_count.Load(storage)); - VerifyOrReturnError(global_scene_count.count_value < max_scenes_global, CHIP_ERROR_NO_MEMORY); - global_scene_count.count_value++; - ReturnErrorOnFailure(global_scene_count.Save(storage)); + EndpointSceneCount endpoint_scene_count(endpoint_id); + ReturnErrorOnFailure(endpoint_scene_count.Load(storage)); + VerifyOrReturnError(endpoint_scene_count.count_value < max_scenes_per_endpoint, CHIP_ERROR_NO_MEMORY); + endpoint_scene_count.count_value++; + ReturnErrorOnFailure(endpoint_scene_count.Save(storage)); scene_count++; scene_map[scene.index] = scene.mStorageId; @@ -385,8 +383,8 @@ struct FabricSceneData : public PersistentData err = this->Save(storage); if (CHIP_NO_ERROR != err) { - global_scene_count.count_value--; - ReturnErrorOnFailure(global_scene_count.Save(storage)); + endpoint_scene_count.count_value--; + ReturnErrorOnFailure(endpoint_scene_count.Save(storage)); return err; } @@ -395,8 +393,8 @@ struct FabricSceneData : public PersistentData // on failure to save the scene, undoes the changes to Fabric Scene Data if (CHIP_NO_ERROR != err) { - global_scene_count.count_value--; - ReturnErrorOnFailure(global_scene_count.Save(storage)); + endpoint_scene_count.count_value--; + ReturnErrorOnFailure(endpoint_scene_count.Save(storage)); scene_count--; scene_map[scene.index].Clear(); @@ -417,7 +415,7 @@ struct FabricSceneData : public PersistentData CHIP_ERROR RemoveScene(PersistentStorageDelegate * storage, const SceneStorageId & scene_id) { CHIP_ERROR err = CHIP_NO_ERROR; - SceneTableData scene(fabric_index, scene_id); + SceneTableData scene(endpoint_id, fabric_index, scene_id); // Empty Scene Fabric Data returns CHIP_NO_ERROR on remove if (scene_count > 0) @@ -426,10 +424,10 @@ struct FabricSceneData : public PersistentData VerifyOrReturnValue(this->Find(scene_id, scene.index) == CHIP_NO_ERROR, CHIP_NO_ERROR); // Update the global scene count - GlobalSceneCount global_scene_count; - ReturnErrorOnFailure(global_scene_count.Load(storage)); - global_scene_count.count_value--; - ReturnErrorOnFailure(global_scene_count.Save(storage)); + EndpointSceneCount endpoint_scene_count(endpoint_id); + ReturnErrorOnFailure(endpoint_scene_count.Load(storage)); + endpoint_scene_count.count_value--; + ReturnErrorOnFailure(endpoint_scene_count.Save(storage)); scene_count--; scene_map[scene.index].Clear(); @@ -438,8 +436,8 @@ struct FabricSceneData : public PersistentData // On failure to update the scene map, undo the global count modification if (CHIP_NO_ERROR != err) { - global_scene_count.count_value++; - ReturnErrorOnFailure(global_scene_count.Save(storage)); + endpoint_scene_count.count_value++; + ReturnErrorOnFailure(endpoint_scene_count.Save(storage)); return err; } @@ -448,8 +446,8 @@ struct FabricSceneData : public PersistentData // On failure to delete scene, undo the change to the Fabric Scene Data and the global scene count if (CHIP_NO_ERROR != err) { - global_scene_count.count_value++; - ReturnErrorOnFailure(global_scene_count.Save(storage)); + endpoint_scene_count.count_value++; + ReturnErrorOnFailure(endpoint_scene_count.Save(storage)); scene_count++; scene_map[scene.index] = scene.mStorageId; @@ -487,11 +485,11 @@ struct FabricSceneData : public PersistentData err = Deserialize(reader, storage, deleted_scenes_count); // If Deserialize sets the "deleted_scenes" variable, the table in flash memory held too many scenes (can happen - // if max_scenes_per_fabric was reduced during an OTA) and was adjusted during deserailizing . The fabric data must then + // if max_scenes_per_fabric was reduced during an OTA) and was adjusted during deserializing . The fabric data must then // be updated if (deleted_scenes_count) { - GlobalSceneCount global_count; + EndpointSceneCount global_count(endpoint_id); ReturnErrorOnFailure(global_count.Load(storage)); global_count.count_value = static_cast(global_count.count_value - deleted_scenes_count); ReturnErrorOnFailure(global_count.Save(storage)); @@ -510,7 +508,7 @@ CHIP_ERROR DefaultSceneTableImpl::Init(PersistentStorageDelegate * storage) } // Verified the initialized parameter respect the maximum allowed values for scene capacity - VerifyOrReturnError(mMaxScenesPerFabric <= kMaxScenesPerFabric && mMaxScenesGlobal <= kMaxScenesGlobal, + VerifyOrReturnError(mMaxScenesPerFabric <= kMaxScenesPerFabric && mMaxScenesPerEndpoint <= kMaxScenesPerEndpoint, CHIP_ERROR_INVALID_INTEGER_VALUE); mStorage = storage; return CHIP_NO_ERROR; @@ -521,44 +519,57 @@ void DefaultSceneTableImpl::Finish() UnregisterAllHandlers(); mSceneEntryIterators.ReleaseAll(); } +CHIP_ERROR DefaultSceneTableImpl::GetFabricSceneCount(FabricIndex fabric_index, uint8_t & scene_count) +{ + VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL); + + FabricSceneData fabric(mEndpointId, fabric_index); + CHIP_ERROR err = fabric.Load(mStorage); + VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_NOT_FOUND == err, err); + + scene_count = (CHIP_ERROR_NOT_FOUND == err) ? 0 : fabric.scene_count; -CHIP_ERROR DefaultSceneTableImpl::GetGlobalSceneCount(uint8_t & scene_count) + return CHIP_NO_ERROR; +} + +CHIP_ERROR DefaultSceneTableImpl::GetEndpointSceneCount(uint8_t & scene_count) { VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL); - GlobalSceneCount global_count; + EndpointSceneCount endpoint_scene_count(mEndpointId); - ReturnErrorOnFailure(global_count.Load(mStorage)); - scene_count = global_count.count_value; + ReturnErrorOnFailure(endpoint_scene_count.Load(mStorage)); + scene_count = endpoint_scene_count.count_value; return CHIP_NO_ERROR; } -CHIP_ERROR DefaultSceneTableImpl::SetGlobalSceneCount(const uint8_t & scene_count) + +CHIP_ERROR DefaultSceneTableImpl::SetEndpointSceneCount(const uint8_t & scene_count) { VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL); - GlobalSceneCount global_count(scene_count); - return global_count.Save(mStorage); + EndpointSceneCount endpoint_scene_count(mEndpointId, scene_count); + return endpoint_scene_count.Save(mStorage); } CHIP_ERROR DefaultSceneTableImpl::GetRemainingCapacity(FabricIndex fabric_index, uint8_t & capacity) { VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL); - uint8_t global_scene_count = 0; - ReturnErrorOnFailure(GetGlobalSceneCount(global_scene_count)); + uint8_t endpoint_scene_count = 0; + ReturnErrorOnFailure(GetEndpointSceneCount(endpoint_scene_count)); // If the global scene count is higher than the maximal Global scene capacity, this returns a capacity of 0 until enough scenes // have been deleted to bring the global number of scenes under the global maximum. - if (global_scene_count > mMaxScenesGlobal) + if (endpoint_scene_count > mMaxScenesPerEndpoint) { capacity = 0; return CHIP_NO_ERROR; } - uint8_t remaining_capacity_global = static_cast(mMaxScenesGlobal - global_scene_count); - uint8_t remaining_capacity_fabric = mMaxScenesPerFabric; + uint8_t remaining_capacity_global = static_cast(mMaxScenesPerEndpoint - endpoint_scene_count); + uint8_t remaining_capacity_fabric = static_cast(mMaxScenesPerFabric); - FabricSceneData fabric(fabric_index); + FabricSceneData fabric(mEndpointId, fabric_index); // Load fabric data (defaults to zero) CHIP_ERROR err = fabric.Load(mStorage); @@ -578,21 +589,22 @@ CHIP_ERROR DefaultSceneTableImpl::SetSceneTableEntry(FabricIndex fabric_index, c { VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL); - FabricSceneData fabric(fabric_index, mMaxScenesPerFabric, mMaxScenesGlobal); + FabricSceneData fabric(mEndpointId, fabric_index, mMaxScenesPerFabric, mMaxScenesPerEndpoint); // Load fabric data (defaults to zero) CHIP_ERROR err = fabric.Load(mStorage); VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_NOT_FOUND == err, err); - return fabric.SaveScene(mStorage, entry); + err = fabric.SaveScene(mStorage, entry); + return err; } CHIP_ERROR DefaultSceneTableImpl::GetSceneTableEntry(FabricIndex fabric_index, SceneStorageId scene_id, SceneTableEntry & entry) { VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL); - FabricSceneData fabric(fabric_index, mMaxScenesPerFabric, mMaxScenesGlobal); - SceneTableData scene(fabric_index); + FabricSceneData fabric(mEndpointId, fabric_index, mMaxScenesPerFabric, mMaxScenesPerEndpoint); + SceneTableData scene(mEndpointId, fabric_index); ReturnErrorOnFailure(fabric.Load(mStorage)); VerifyOrReturnError(fabric.Find(scene_id, scene.index) == CHIP_NO_ERROR, CHIP_ERROR_NOT_FOUND); @@ -616,8 +628,7 @@ CHIP_ERROR DefaultSceneTableImpl::GetSceneTableEntry(FabricIndex fabric_index, S CHIP_ERROR DefaultSceneTableImpl::RemoveSceneTableEntry(FabricIndex fabric_index, SceneStorageId scene_id) { VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL); - - FabricSceneData fabric(fabric_index, mMaxScenesPerFabric, mMaxScenesGlobal); + FabricSceneData fabric(mEndpointId, fabric_index, mMaxScenesPerFabric, mMaxScenesPerEndpoint); ReturnErrorOnFailure(fabric.Load(mStorage)); @@ -629,13 +640,14 @@ CHIP_ERROR DefaultSceneTableImpl::RemoveSceneTableEntry(FabricIndex fabric_index /// @param fabric_index Fabric in which the scene belongs /// @param scened_idx Position in the Scene Table /// @return CHIP_NO_ERROR if removal was successful, errors if failed to remove the scene or to update the fabric after removing it -CHIP_ERROR DefaultSceneTableImpl::RemoveSceneTableEntryAtPosition(FabricIndex fabric_index, SceneIndex scene_idx) +CHIP_ERROR DefaultSceneTableImpl::RemoveSceneTableEntryAtPosition(EndpointId endpoint, FabricIndex fabric_index, + SceneIndex scene_idx) { VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL); CHIP_ERROR err = CHIP_NO_ERROR; - FabricSceneData fabric(fabric_index, mMaxScenesPerFabric, mMaxScenesGlobal); - SceneTableData scene(fabric_index, scene_idx); + FabricSceneData fabric(endpoint, fabric_index, mMaxScenesPerFabric, mMaxScenesPerEndpoint); + SceneTableData scene(endpoint, fabric_index, scene_idx); ReturnErrorOnFailure(fabric.Load(mStorage)); err = scene.Load(mStorage); @@ -647,8 +659,8 @@ CHIP_ERROR DefaultSceneTableImpl::RemoveSceneTableEntryAtPosition(FabricIndex fa CHIP_ERROR DefaultSceneTableImpl::GetAllSceneIdsInGroup(FabricIndex fabric_index, GroupId group_id, Span & scene_list) { - FabricSceneData fabric(fabric_index, mMaxScenesPerFabric, mMaxScenesGlobal); - SceneTableData scene(fabric_index); + FabricSceneData fabric(mEndpointId, fabric_index, mMaxScenesPerFabric, mMaxScenesPerEndpoint); + SceneTableData scene(mEndpointId, fabric_index); auto * iterator = this->IterateSceneEntries(fabric_index); VerifyOrReturnError(nullptr != iterator, CHIP_ERROR_INTERNAL); @@ -677,12 +689,12 @@ CHIP_ERROR DefaultSceneTableImpl::DeleteAllScenesInGroup(FabricIndex fabric_inde { VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL); - FabricSceneData fabric(fabric_index, mMaxScenesPerFabric, mMaxScenesGlobal); - SceneTableData scene(fabric_index); + FabricSceneData fabric(mEndpointId, fabric_index, mMaxScenesPerFabric, mMaxScenesPerEndpoint); + SceneTableData scene(mEndpointId, fabric_index); ReturnErrorOnFailure(fabric.Load(mStorage)); - for (uint8_t i = 0; i < mMaxScenesPerFabric; i++) + for (uint16_t i = 0; i < mMaxScenesPerFabric; i++) { if (fabric.scene_map[i].mGroupId == group_id) { @@ -728,7 +740,7 @@ CHIP_ERROR DefaultSceneTableImpl::SceneSaveEFS(SceneTableEntry & scene) uint8_t clusterCount = 0; clusterId cArray[kMaxClustersPerScene]; Span cSpan(cArray); - clusterCount = GetClustersFromEndpoint(scene.mStorageId.mEndpointId, cArray, kMaxClustersPerScene); + clusterCount = GetClustersFromEndpoint(cArray, kMaxClustersPerScene); cSpan.reduce_size(clusterCount); for (clusterId cluster : cSpan) { @@ -738,9 +750,9 @@ CHIP_ERROR DefaultSceneTableImpl::SceneSaveEFS(SceneTableEntry & scene) for (auto & handler : mHandlerList) { - if (handler.SupportsCluster(scene.mStorageId.mEndpointId, cluster)) + if (handler.SupportsCluster(mEndpointId, cluster)) { - ReturnErrorOnFailure(handler.SerializeSave(scene.mStorageId.mEndpointId, EFS.mID, EFSSpan)); + ReturnErrorOnFailure(handler.SerializeSave(mEndpointId, EFS.mID, EFSSpan)); EFS.mUsedBytes = static_cast(EFSSpan.size()); ReturnErrorOnFailure(scene.mStorageData.mExtensionFieldSets.InsertFieldSet(EFS)); break; @@ -775,9 +787,9 @@ CHIP_ERROR DefaultSceneTableImpl::SceneApplyEFS(const SceneTableEntry & scene) { for (auto & handler : mHandlerList) { - if (handler.SupportsCluster(scene.mStorageId.mEndpointId, cluster)) + if (handler.SupportsCluster(mEndpointId, cluster)) { - ReturnErrorOnFailure(handler.ApplyScene(scene.mStorageId.mEndpointId, cluster, EFSSpan, time)); + ReturnErrorOnFailure(handler.ApplyScene(mEndpointId, cluster, EFSSpan, time)); break; } } @@ -792,41 +804,63 @@ CHIP_ERROR DefaultSceneTableImpl::RemoveFabric(FabricIndex fabric_index) { VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INTERNAL); - FabricSceneData fabric(fabric_index); - SceneIndex idx = 0; - CHIP_ERROR err = fabric.Load(mStorage); - VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_NOT_FOUND == err, err); - - while (idx < mMaxScenesPerFabric) + for (auto endpoint : app::EnabledEndpointsWithServerCluster(chip::app::Clusters::Scenes::Id)) { - err = RemoveSceneTableEntryAtPosition(fabric_index, idx); - VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND == err, err); - idx++; + FabricSceneData fabric(endpoint, fabric_index); + SceneIndex idx = 0; + CHIP_ERROR err = fabric.Load(mStorage); + VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_NOT_FOUND == err, err); + if (CHIP_ERROR_NOT_FOUND == err) + { + continue; + } + + while (idx < mMaxScenesPerFabric) + { + err = RemoveSceneTableEntryAtPosition(endpoint, fabric_index, idx); + VerifyOrReturnError(CHIP_NO_ERROR == err || CHIP_ERROR_NOT_FOUND == err, err); + idx++; + } + + // Remove fabric scenes on endpoint + ReturnErrorOnFailure(fabric.Delete(mStorage)); } - // Remove fabric - return fabric.Delete(mStorage); + return CHIP_NO_ERROR; } /// @brief wrapper function around emberAfGetClustersFromEndpoint to allow testing, shimmed in test configuration because /// emberAfGetClusterFromEndpoint relies on , which relies on zap generated files -uint8_t DefaultSceneTableImpl::GetClustersFromEndpoint(EndpointId endpoint, ClusterId * clusterList, uint8_t listLen) +uint8_t DefaultSceneTableImpl::GetClustersFromEndpoint(ClusterId * clusterList, uint8_t listLen) { - return emberAfGetClustersFromEndpoint(endpoint, clusterList, listLen, true); + return emberAfGetClustersFromEndpoint(mEndpointId, clusterList, listLen, true); } -DefaultSceneTableImpl::SceneEntryIterator * DefaultSceneTableImpl::IterateSceneEntries(FabricIndex fabric_index) +void DefaultSceneTableImpl::SetEndpoint(EndpointId endpoint) +{ + mEndpointId = endpoint; +} + +void DefaultSceneTableImpl::SetTableSize(uint16_t endpointSceneTableSize) +{ + // Verify the endpoint passed size respects the limits of the device configuration + mMaxScenesPerEndpoint = (kMaxScenesPerEndpoint < endpointSceneTableSize) ? kMaxScenesPerEndpoint : endpointSceneTableSize; + mMaxScenesPerFabric = static_cast(endpointSceneTableSize / 2); +} + +DefaultSceneTableImpl::SceneEntryIterator * DefaultSceneTableImpl::IterateSceneEntries(FabricIndex fabric) { VerifyOrReturnError(IsInitialized(), nullptr); - return mSceneEntryIterators.CreateObject(*this, fabric_index, mMaxScenesPerFabric, mMaxScenesGlobal); + return mSceneEntryIterators.CreateObject(*this, fabric, mEndpointId, mMaxScenesPerFabric, mMaxScenesPerEndpoint); } -DefaultSceneTableImpl::SceneEntryIteratorImpl::SceneEntryIteratorImpl(DefaultSceneTableImpl & provider, FabricIndex fabric_index, - uint8_t maxScenesPerFabric, uint8_t maxScenesGlobal) : +DefaultSceneTableImpl::SceneEntryIteratorImpl::SceneEntryIteratorImpl(DefaultSceneTableImpl & provider, FabricIndex fabricIdx, + EndpointId endpoint, uint16_t maxScenesPerFabric, + uint16_t maxScenesEndpoint) : mProvider(provider), - mFabric(fabric_index), mMaxScenesPerFabric(maxScenesPerFabric), mMaxScenesGlobal(maxScenesGlobal) + mFabric(fabricIdx), mEndpoint(endpoint), mMaxScenesPerFabric(maxScenesPerFabric), mMaxScenesPerEndpoint(maxScenesEndpoint) { - FabricSceneData fabric(fabric_index, mMaxScenesPerFabric, mMaxScenesGlobal); + FabricSceneData fabric(mEndpoint, fabricIdx, mMaxScenesPerFabric, mMaxScenesPerEndpoint); ReturnOnFailure(fabric.Load(provider.mStorage)); mTotalScenes = fabric.scene_count; mSceneIndex = 0; @@ -839,8 +873,8 @@ size_t DefaultSceneTableImpl::SceneEntryIteratorImpl::Count() bool DefaultSceneTableImpl::SceneEntryIteratorImpl::Next(SceneTableEntry & output) { - FabricSceneData fabric(mFabric); - SceneTableData scene(mFabric); + FabricSceneData fabric(mEndpoint, mFabric); + SceneTableData scene(mEndpoint, mFabric); VerifyOrReturnError(fabric.Load(mProvider.mStorage) == CHIP_NO_ERROR, false); @@ -869,5 +903,24 @@ void DefaultSceneTableImpl::SceneEntryIteratorImpl::Release() mProvider.mSceneEntryIterators.ReleaseObject(this); } +namespace { + +static DefaultSceneTableImpl gSceneTableImpl; + +} // namespace + +/// @brief Instance getter for the default global scene table implementation +/// @note This API should always be called prior to using the scene Table and the return pointer should never be cached. As per +/// issue: https://github.com/project-chip/connectedhomeip/issues/26878, this API is currently not thread +/// safe and calls to it should be made thread safe in the event of using multiple endpoints at once. +/// @return Default global scene table implementation +DefaultSceneTableImpl * GetSceneTableImpl(EndpointId endpoint, uint16_t endpointTableSize) +{ + gSceneTableImpl.SetEndpoint(endpoint); + gSceneTableImpl.SetTableSize(endpointTableSize); + + return &gSceneTableImpl; +} + } // namespace scenes } // namespace chip diff --git a/src/app/clusters/scenes/SceneTableImpl.h b/src/app/clusters/scenes-server/SceneTableImpl.h similarity index 79% rename from src/app/clusters/scenes/SceneTableImpl.h rename to src/app/clusters/scenes-server/SceneTableImpl.h index d601e607dac39d..7298f60a310e79 100644 --- a/src/app/clusters/scenes/SceneTableImpl.h +++ b/src/app/clusters/scenes-server/SceneTableImpl.h @@ -16,8 +16,9 @@ */ #pragma once -#include -#include +#include +#include +#include #include #include #include @@ -26,6 +27,9 @@ namespace chip { namespace scenes { +static constexpr uint16_t kMaxScenesPerFabric = CHIP_CONFIG_SCENES_MAX_PER_FABRIC; +static constexpr uint16_t kMaxScenesPerEndpoint = CHIP_CONFIG_MAX_SCENES_PER_ENDPOINT; + using clusterId = chip::ClusterId; /// @brief Default implementation of handler, handle EFS from add scene and view scene commands for any cluster @@ -153,15 +157,16 @@ class DefaultSceneTableImpl : public SceneTable CHIP_ERROR Init(PersistentStorageDelegate * storage) override; void Finish() override; - // Global scene count - CHIP_ERROR GetGlobalSceneCount(uint8_t & scene_count) override; + // Scene count + CHIP_ERROR GetEndpointSceneCount(uint8_t & scene_count) override; + CHIP_ERROR GetFabricSceneCount(FabricIndex fabric_index, uint8_t & scene_count) override; // Data CHIP_ERROR GetRemainingCapacity(FabricIndex fabric_index, uint8_t & capacity) override; CHIP_ERROR SetSceneTableEntry(FabricIndex fabric_index, const SceneTableEntry & entry) override; CHIP_ERROR GetSceneTableEntry(FabricIndex fabric_index, SceneStorageId scene_id, SceneTableEntry & entry) override; CHIP_ERROR RemoveSceneTableEntry(FabricIndex fabric_index, SceneStorageId scene_id) override; - CHIP_ERROR RemoveSceneTableEntryAtPosition(FabricIndex fabric_index, SceneIndex scene_idx) override; + CHIP_ERROR RemoveSceneTableEntryAtPosition(EndpointId endpoint, FabricIndex fabric_index, SceneIndex scene_idx) override; // Groups CHIP_ERROR GetAllSceneIdsInGroup(FabricIndex fabric_index, GroupId group_id, Span & scene_list) override; @@ -173,7 +178,6 @@ class DefaultSceneTableImpl : public SceneTable void UnregisterAllHandlers() override; // Extension field sets operation - CHIP_ERROR SceneSaveEFS(SceneTableEntry & scene) override; CHIP_ERROR SceneApplyEFS(const SceneTableEntry & scene) override; @@ -183,46 +187,58 @@ class DefaultSceneTableImpl : public SceneTable // Iterators SceneEntryIterator * IterateSceneEntries(FabricIndex fabric_index) override; + void SetEndpoint(EndpointId endpoint); + void SetTableSize(uint16_t endpointSceneTableSize); + bool IsInitialized() { return (mStorage != nullptr); } + protected: // This constructor is meant for test purposes, it allows to change the defined max for scenes per fabric and global, which // allows to simulate OTA where this value was changed - DefaultSceneTableImpl(uint8_t maxScenesPerFabric = scenes::kMaxScenesPerFabric, - uint8_t maxScenesGlobal = scenes::kMaxScenesGlobal) : - mMaxScenesPerFabric(maxScenesPerFabric), - mMaxScenesGlobal(maxScenesGlobal) + DefaultSceneTableImpl(uint16_t maxScenesPerFabric, uint16_t maxScenesPerEndpoint) : + mMaxScenesPerFabric(maxScenesPerFabric), mMaxScenesPerEndpoint(maxScenesPerEndpoint) {} // Global scene count - CHIP_ERROR SetGlobalSceneCount(const uint8_t & scene_count); + CHIP_ERROR SetEndpointSceneCount(const uint8_t & scene_count); // wrapper function around emberAfGetClustersFromEndpoint to allow override when testing - virtual uint8_t GetClustersFromEndpoint(EndpointId endpoint, ClusterId * clusterList, uint8_t listLen); + virtual uint8_t GetClustersFromEndpoint(ClusterId * clusterList, uint8_t listLen); class SceneEntryIteratorImpl : public SceneEntryIterator { public: - SceneEntryIteratorImpl(DefaultSceneTableImpl & provider, FabricIndex fabric_index, uint8_t maxScenesPerFabric, - uint8_t maxScenesGlobal); + SceneEntryIteratorImpl(DefaultSceneTableImpl & provider, FabricIndex fabricIdx, EndpointId endpoint, + uint16_t maxScenesPerFabric, uint16_t maxScenesPerEndpoint); size_t Count() override; bool Next(SceneTableEntry & output) override; void Release() override; protected: DefaultSceneTableImpl & mProvider; - FabricIndex mFabric = kUndefinedFabricIndex; + FabricIndex mFabric = kUndefinedFabricIndex; + EndpointId mEndpoint = kInvalidEndpointId; SceneIndex mNextSceneIdx; SceneIndex mSceneIndex = 0; uint8_t mTotalScenes = 0; - uint8_t mMaxScenesPerFabric; - uint8_t mMaxScenesGlobal; + uint16_t mMaxScenesPerFabric; + uint16_t mMaxScenesPerEndpoint; }; - bool IsInitialized() { return (mStorage != nullptr); } - const uint8_t mMaxScenesPerFabric = kMaxScenesPerFabric; - const uint8_t mMaxScenesGlobal = kMaxScenesGlobal; + uint16_t mMaxScenesPerFabric = kMaxScenesPerFabric; + uint16_t mMaxScenesPerEndpoint = kMaxScenesPerEndpoint; + EndpointId mEndpointId = kInvalidEndpointId; chip::PersistentStorageDelegate * mStorage = nullptr; ObjectPool mSceneEntryIterators; }; // class DefaultSceneTableImpl +/// @brief Gets a pointer to the instance of Scene Table Impl, providing EndpointId and Table Size for said endpoint +/// @param endpoint Endpoint ID, optional only if getting the Table to initialize it, any storage action will require an endpoint +/// different than kInvalidEndpoint ID and WILL fail if an endpoint wasn't provided here. +/// @param endpointTableSize Size of the scene table for a specific endpoint. Must be lower than restriction set in CHIPConfig to +/// match device restrictions, if it is higher than permitted by CHIPConfig, will be capped at kMaxScenesPerEndpoint +/// @return Pointer to the instance of the Scene Table +DefaultSceneTableImpl * GetSceneTableImpl(EndpointId endpoint = kInvalidEndpointId, + uint16_t endpointTableSize = kMaxScenesPerEndpoint); + } // namespace scenes } // namespace chip diff --git a/src/app/clusters/scenes-server/scenes-server.cpp b/src/app/clusters/scenes-server/scenes-server.cpp new file mode 100644 index 00000000000000..f55e06feb611a7 --- /dev/null +++ b/src/app/clusters/scenes-server/scenes-server.cpp @@ -0,0 +1,866 @@ +/* + * + * Copyright (c) 2021-2023 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 "scenes-server.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using SceneTableEntry = chip::scenes::DefaultSceneTableImpl::SceneTableEntry; +using SceneStorageId = chip::scenes::DefaultSceneTableImpl::SceneStorageId; +using SceneData = chip::scenes::DefaultSceneTableImpl::SceneData; +using HandlerContext = chip::app::CommandHandlerInterface::HandlerContext; +using ExtensionFieldSet = chip::scenes::ExtensionFieldSet; +using GroupDataProvider = chip::Credentials::GroupDataProvider; +using SceneTable = chip::scenes::SceneTable; +using AuthMode = chip::Access::AuthMode; +using ScenesServer = chip::app::Clusters::Scenes::ScenesServer; + +namespace chip { +namespace app { +namespace Clusters { +namespace Scenes { + +/// @brief Generate and add a response to a command handler context if err parameter is not CHIP_NO_ERROR +/// @tparam ResponseType Type of response, depends on the command +/// @param ctx Command Handler context where to add reponse +/// @param resp Response to add in ctx +/// @param status Status to verify +/// @return CHIP_ERROR +template +CHIP_ERROR AddResponseOnError(CommandHandlerInterface::HandlerContext & ctx, ResponseType & resp, CHIP_ERROR err) +{ + if (CHIP_NO_ERROR != err) + { + resp.status = to_underlying(StatusIB(err).mStatus); + ctx.mCommandHandler.AddResponse(ctx.mRequestPath, resp); + } + return err; +} + +/// @brief Generate and add a response to a command handler context depending on an EmberAfStatus +/// @tparam ResponseType Type of response, depends on the command +/// @param ctx Command Handler context where to add reponse +/// @param resp Response to add in ctx +/// @param status Status to verify +/// @return EmberAfStatus -> CHIP_ERROR +template +CHIP_ERROR AddResponseOnError(CommandHandlerInterface::HandlerContext & ctx, ResponseType & resp, EmberAfStatus status) +{ + return AddResponseOnError(ctx, resp, StatusIB(ToInteractionModelStatus(status)).ToChipError()); +} + +template +CHIP_ERROR UpdateLastConfiguredBy(HandlerContext & ctx, ResponseType resp) +{ + Access::SubjectDescriptor descriptor = ctx.mCommandHandler.GetSubjectDescriptor(); + if (AuthMode::kCase == descriptor.authMode) + { + ReturnErrorOnFailure( + AddResponseOnError(ctx, resp, Attributes::LastConfiguredBy::Set(ctx.mRequestPath.mEndpointId, descriptor.subject))); + } + else + { + ReturnErrorOnFailure(AddResponseOnError(ctx, resp, Attributes::LastConfiguredBy::SetNull(ctx.mRequestPath.mEndpointId))); + } + + return CHIP_NO_ERROR; +} + +ScenesServer ScenesServer::mInstance; + +ScenesServer & ScenesServer::Instance() +{ + return mInstance; +} +void ReportAttributeOnAllEndpoints(AttributeId attribute) {} + +CHIP_ERROR ScenesServer::Init() +{ + // Prevents re-initializing + VerifyOrReturnError(!mIsInitialized, CHIP_ERROR_INCORRECT_STATE); + + ReturnErrorOnFailure(chip::app::InteractionModelEngine::GetInstance()->RegisterCommandHandler(this)); + VerifyOrReturnError(registerAttributeAccessOverride(this), CHIP_ERROR_INCORRECT_STATE); + mGroupProvider = Credentials::GetGroupDataProvider(); + + scenes::SceneTable * sceneTable = scenes::GetSceneTableImpl(); + ReturnErrorOnFailure(sceneTable->Init(&chip::Server::GetInstance().GetPersistentStorage())); + + for (auto endpoint : EnabledEndpointsWithServerCluster(Id)) + { + EmberAfStatus status = Attributes::FeatureMap::Set(endpoint, to_underlying(Feature::kSceneNames)); + if (EMBER_ZCL_STATUS_SUCCESS != status) + { + ChipLogDetail(Zcl, "ERR: setting feature map on Endpoint %hu Status: %x", endpoint, status); + } + // The bit of 7 the NameSupport attribute indicates whether or not scene names are supported + // + // According to spec, bit 7 (Scene Names) MUST match feature bit 0 (Scene Names) + status = Attributes::NameSupport::Set(endpoint, 0x80); + if (EMBER_ZCL_STATUS_SUCCESS != status) + { + ChipLogDetail(Zcl, "ERR: setting NameSupport on Endpoint %hu Status: %x", endpoint, status); + } + status = Attributes::LastConfiguredBy::SetNull(endpoint); + if (EMBER_ZCL_STATUS_SUCCESS != status) + { + ChipLogDetail(Zcl, "ERR: setting LastConfiguredBy on Endpoint %hu Status: %x", endpoint, status); + } + } + + mIsInitialized = true; + return CHIP_NO_ERROR; +} + +void ScenesServer::Shutdown() +{ + chip::app::InteractionModelEngine::GetInstance()->UnregisterCommandHandler(this); + + scenes::SceneTable * sceneTable = scenes::GetSceneTableImpl(); + sceneTable->Finish(); + mGroupProvider = nullptr; + mIsInitialized = false; +} + +template +void AddSceneParse(CommandHandlerInterface::HandlerContext & ctx, const CommandData & req, GroupDataProvider * groupProvider) +{ + ResponseType response; + uint16_t endpointTableSize = 0; + + ReturnOnFailure( + AddResponseOnError(ctx, response, Attributes::SceneTableSize::Get(ctx.mRequestPath.mEndpointId, &endpointTableSize))); + + // Get Scene Table Instance + scenes::SceneTable * sceneTable = + scenes::GetSceneTableImpl(ctx.mRequestPath.mEndpointId, endpointTableSize); + + // Response data + response.groupID = req.groupID; + response.sceneID = req.sceneID; + + // Verify the attributes are respecting constraints + if (req.transitionTime > scenes::kScenesMaxTransitionTimeS || req.sceneName.size() > scenes::kSceneNameMaxLength) + { + response.status = to_underlying(Protocols::InteractionModel::Status::InvalidCommand); + ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); + return; + } + + // Verify Endpoint in group + VerifyOrReturn(nullptr != groupProvider); + if (0 != req.groupID && + !groupProvider->HasEndpoint(ctx.mCommandHandler.GetAccessingFabricIndex(), req.groupID, ctx.mRequestPath.mEndpointId)) + { + response.status = to_underlying(Protocols::InteractionModel::Status::InvalidCommand); + ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); + return; + } + + uint32_t transitionTimeMs = 0; + if (Commands::AddScene::Id == ctx.mRequestPath.mCommandId) + { + transitionTimeMs = static_cast(req.transitionTime) * 1000u; + } + else if (Commands::EnhancedAddScene::Id == ctx.mRequestPath.mCommandId) + { + transitionTimeMs = static_cast(req.transitionTime) * 100u; + } + + auto fieldSetIter = req.extensionFieldSets.begin(); + + uint8_t EFSCount = 0; + SceneData storageData(req.sceneName, transitionTimeMs); + + // Goes through all EFS in command + while (fieldSetIter.Next() && EFSCount < scenes::kMaxClustersPerScene) + { + scenes::ExtensionFieldSet tempEFS; + tempEFS.mID = fieldSetIter.GetValue().clusterID; + + MutableByteSpan buff_span(tempEFS.mBytesBuffer); + + // Check if a handler is registered for the EFS's cluster + for (auto & handler : sceneTable->mHandlerList) + { + if (handler.SupportsCluster(ctx.mRequestPath.mEndpointId, tempEFS.mID)) + { + ReturnOnFailure(AddResponseOnError( + ctx, response, handler.SerializeAdd(ctx.mRequestPath.mEndpointId, fieldSetIter.GetValue(), buff_span))); + break; + } + } + + static_assert(sizeof(tempEFS.mBytesBuffer) <= UINT8_MAX, "Serialized EFS number of bytes must fit in a uint8"); + tempEFS.mUsedBytes = static_cast(buff_span.size()); + + if (!tempEFS.IsEmpty()) + { + storageData.mExtensionFieldSets.InsertFieldSet(tempEFS); + } + } + ReturnOnFailure(AddResponseOnError(ctx, response, fieldSetIter.GetStatus())); + + // Create scene from data and ID + SceneTableEntry scene(SceneStorageId(req.sceneID, req.groupID), storageData); + + // Get Capacity + VerifyOrReturn(nullptr != sceneTable); + uint8_t capacity = 0; + ReturnOnFailure(AddResponseOnError(ctx, response, + sceneTable->GetRemainingCapacity(ctx.mCommandHandler.GetAccessingFabricIndex(), capacity))); + + if (0 == capacity) + { + response.status = to_underlying(Protocols::InteractionModel::Status::ResourceExhausted); + ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); + return; + } + + // Insert in table + ReturnOnFailure( + AddResponseOnError(ctx, response, sceneTable->SetSceneTableEntry(ctx.mCommandHandler.GetAccessingFabricIndex(), scene))); + + // Update Attributes + MatterReportingAttributeChangeCallback(ctx.mRequestPath.mEndpointId, Id, Attributes::SceneCount::Id); + MatterReportingAttributeChangeCallback(ctx.mRequestPath.mEndpointId, Id, Attributes::RemainingCapacity::Id); + + ReturnOnFailure(UpdateLastConfiguredBy(ctx, response)); + + // Write response + response.status = to_underlying(Protocols::InteractionModel::Status::Success); + ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); +} + +template +void ViewSceneParse(HandlerContext & ctx, const CommandData & req, GroupDataProvider * groupProvider) +{ + ResponseType response; + + uint16_t endpointTableSize = 0; + + ReturnOnFailure( + AddResponseOnError(ctx, response, Attributes::SceneTableSize::Get(ctx.mRequestPath.mEndpointId, &endpointTableSize))); + + // Get Scene Table Instance + scenes::SceneTable * sceneTable = + scenes::GetSceneTableImpl(ctx.mRequestPath.mEndpointId, endpointTableSize); + + // Response data + response.groupID = req.groupID; + response.sceneID = req.sceneID; + + // Verify Endpoint in group + VerifyOrReturn(nullptr != groupProvider); + if (0 != req.groupID && + !groupProvider->HasEndpoint(ctx.mCommandHandler.GetAccessingFabricIndex(), req.groupID, ctx.mRequestPath.mEndpointId)) + { + response.status = to_underlying(Protocols::InteractionModel::Status::InvalidCommand); + ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); + return; + } + + SceneTableEntry scene; + + // Gets the scene from the table + ReturnOnFailure(AddResponseOnError(ctx, response, + sceneTable->GetSceneTableEntry(ctx.mCommandHandler.GetAccessingFabricIndex(), + SceneStorageId(req.sceneID, req.groupID), scene))); + + // Response Extension Field Sets buffer + Structs::ExtensionFieldSet::Type responseEFSBuffer[scenes::kMaxClustersPerScene]; + uint8_t deserializedEFSCount = 0; + + // Adds extension field sets to the scene + for (uint8_t i = 0; i < scene.mStorageData.mExtensionFieldSets.GetFieldSetCount(); i++) + { + // gets data from the field in the scene + ExtensionFieldSet tempField; + scene.mStorageData.mExtensionFieldSets.GetFieldSetAtPosition(tempField, i); + ByteSpan efsSpan(tempField.mBytesBuffer, tempField.mUsedBytes); + + // This should only find one handle per cluster + for (auto & handler : sceneTable->mHandlerList) + { + if (handler.SupportsCluster(ctx.mRequestPath.mEndpointId, tempField.mID)) + { + ReturnOnFailure(AddResponseOnError( + ctx, response, + handler.Deserialize(ctx.mRequestPath.mEndpointId, tempField.mID, efsSpan, responseEFSBuffer[i]))); + deserializedEFSCount++; + break; + } + } + } + + response.status = to_underlying(Protocols::InteractionModel::Status::Success); + + // Verifies how to convert transition time + if (Commands::ViewScene::Id == ctx.mRequestPath.mCommandId) + { + response.transitionTime.SetValue(static_cast(scene.mStorageData.mSceneTransitionTimeMs / 1000)); + } + else if (Commands::EnhancedViewScene::Id == ctx.mRequestPath.mCommandId) + { + response.transitionTime.SetValue(static_cast(scene.mStorageData.mSceneTransitionTimeMs / 100)); + } + + response.sceneName.SetValue(CharSpan(scene.mStorageData.mName, scene.mStorageData.mNameLength)); + Span responseEFSSpan(responseEFSBuffer, deserializedEFSCount); + response.extensionFieldSets.SetValue(responseEFSSpan); + + ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); +} + +CHIP_ERROR StoreSceneParse(const FabricIndex & fabricIdx, const EndpointId & endpointID, const GroupId & groupID, + const SceneId & sceneID, GroupDataProvider * groupProvider) +{ + uint16_t endpointTableSize = 0; + ReturnErrorOnFailure( + StatusIB(ToInteractionModelStatus(Attributes::SceneTableSize::Get(endpointID, &endpointTableSize))).ToChipError()); + + // Get Scene Table Instance + scenes::SceneTable * sceneTable = scenes::GetSceneTableImpl(endpointID, endpointTableSize); + + // Verify Endpoint in group + VerifyOrReturnError(nullptr != groupProvider, CHIP_ERROR_INTERNAL); + if (0 != groupID && !groupProvider->HasEndpoint(fabricIdx, groupID, endpointID)) + { + return CHIP_IM_GLOBAL_STATUS(InvalidCommand); + } + + // Scene Table interface data + SceneTableEntry scene(SceneStorageId(sceneID, groupID)); + + VerifyOrReturnError(nullptr != sceneTable, CHIP_ERROR_INTERNAL); + CHIP_ERROR err = sceneTable->GetSceneTableEntry(fabricIdx, scene.mStorageId, scene); + if (CHIP_NO_ERROR != err && CHIP_ERROR_NOT_FOUND != err) + { + return err; + } + + if (CHIP_ERROR_NOT_FOUND == err) + { + scene.mStorageData.SetName(CharSpan()); + scene.mStorageData.mSceneTransitionTimeMs = 0; + } + else + { + scene.mStorageData.mExtensionFieldSets.Clear(); + } + + // Gets the EFS + ReturnErrorOnFailure(sceneTable->SceneSaveEFS(scene)); + // Insert in Scene Table + ReturnErrorOnFailure(sceneTable->SetSceneTableEntry(fabricIdx, scene)); + + // Update size attributes + MatterReportingAttributeChangeCallback(endpointID, Id, Attributes::SceneCount::Id); + MatterReportingAttributeChangeCallback(endpointID, Id, Attributes::RemainingCapacity::Id); + + ReturnErrorOnFailure(StatusIB(ToInteractionModelStatus(Attributes::CurrentScene::Set(endpointID, sceneID))).ToChipError()); + ReturnErrorOnFailure(StatusIB(ToInteractionModelStatus(Attributes::CurrentGroup::Set(endpointID, groupID))).ToChipError()); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR RecallSceneParse(const FabricIndex & fabricIdx, const EndpointId & endpointID, const GroupId & groupID, + const SceneId & sceneID, const Optional> & transitionTime, + GroupDataProvider * groupProvider) +{ + uint16_t endpointTableSize = 0; + ReturnErrorOnFailure( + StatusIB(ToInteractionModelStatus(Attributes::SceneTableSize::Get(endpointID, &endpointTableSize))).ToChipError()); + + // Get Scene Table Instance + scenes::SceneTable * sceneTable = scenes::GetSceneTableImpl(endpointID, endpointTableSize); + + // Verify Endpoint in group + VerifyOrReturnError(nullptr != groupProvider, CHIP_ERROR_INTERNAL); + if (0 != groupID && !groupProvider->HasEndpoint(fabricIdx, groupID, endpointID)) + { + return CHIP_IM_GLOBAL_STATUS(InvalidCommand); + } + + // Scene Table interface data + SceneTableEntry scene(SceneStorageId(sceneID, groupID)); + + VerifyOrReturnError(nullptr != sceneTable, CHIP_ERROR_INTERNAL); + ReturnErrorOnFailure(sceneTable->GetSceneTableEntry(fabricIdx, scene.mStorageId, scene)); + + // Check for optional + if (transitionTime.HasValue()) + { + // Check for nullable + if (!transitionTime.Value().IsNull()) + { + scene.mStorageData.mSceneTransitionTimeMs = static_cast(transitionTime.Value().Value() * 100); + } + } + + ReturnErrorOnFailure(sceneTable->SceneApplyEFS(scene)); + + ReturnErrorOnFailure(StatusIB(ToInteractionModelStatus(Attributes::CurrentScene::Set(endpointID, sceneID))).ToChipError()); + + ReturnErrorOnFailure(StatusIB(ToInteractionModelStatus(Attributes::CurrentGroup::Set(endpointID, groupID))).ToChipError()); + + return CHIP_NO_ERROR; +} + +// CommandHanlerInterface +void ScenesServer::InvokeCommand(HandlerContext & ctxt) +{ + switch (ctxt.mRequestPath.mCommandId) + { + case Commands::AddScene::Id: + HandleCommand( + ctxt, [this](HandlerContext & ctx, const auto & req) { HandleAddScene(ctx, req); }); + return; + case Commands::ViewScene::Id: + HandleCommand( + ctxt, [this](HandlerContext & ctx, const auto & req) { HandleViewScene(ctx, req); }); + return; + case Commands::RemoveScene::Id: + HandleCommand( + ctxt, [this](HandlerContext & ctx, const auto & req) { HandleRemoveScene(ctx, req); }); + return; + case Commands::RemoveAllScenes::Id: + HandleCommand( + ctxt, [this](HandlerContext & ctx, const auto & req) { HandleRemoveAllScenes(ctx, req); }); + return; + case Commands::StoreScene::Id: + HandleCommand( + ctxt, [this](HandlerContext & ctx, const auto & req) { HandleStoreScene(ctx, req); }); + return; + case Commands::RecallScene::Id: + HandleCommand( + ctxt, [this](HandlerContext & ctx, const auto & req) { HandleRecallScene(ctx, req); }); + return; + case Commands::GetSceneMembership::Id: + HandleCommand( + ctxt, [this](HandlerContext & ctx, const auto & req) { HandleGetSceneMembership(ctx, req); }); + return; + case Commands::EnhancedAddScene::Id: + HandleCommand( + ctxt, [this](HandlerContext & ctx, const auto & req) { HandleEnhancedAddScene(ctx, req); }); + return; + case Commands::EnhancedViewScene::Id: + HandleCommand( + ctxt, [this](HandlerContext & ctx, const auto & req) { HandleEnhancedViewScene(ctx, req); }); + return; + case Commands::CopyScene::Id: + HandleCommand( + ctxt, [this](HandlerContext & ctx, const auto & req) { HandleCopyScene(ctx, req); }); + return; + } +} + +// AttributeAccessInterface +CHIP_ERROR ScenesServer::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) +{ + uint8_t value = 0; + uint16_t endpointTableSize = 0; + ReturnErrorOnFailure( + StatusIB(ToInteractionModelStatus(Attributes::SceneTableSize::Get(aPath.mEndpointId, &endpointTableSize))).ToChipError()); + + // Get Scene Table Instance + scenes::SceneTable * sceneTable; + + switch (aPath.mAttributeId) + { + case Attributes::SceneCount::Id: + sceneTable = scenes::GetSceneTableImpl(aPath.mEndpointId, endpointTableSize); + ReturnErrorOnFailure(sceneTable->GetEndpointSceneCount(value)); + return aEncoder.Encode(value); + case Attributes::RemainingCapacity::Id: + sceneTable = scenes::GetSceneTableImpl(aPath.mEndpointId, endpointTableSize); + ReturnErrorOnFailure(sceneTable->GetRemainingCapacity(aEncoder.AccessingFabricIndex(), value)); + return aEncoder.Encode(value); + default: + return CHIP_NO_ERROR; + } +} + +void ScenesServer::GroupWillBeRemoved(FabricIndex aFabricIx, EndpointId aEndpointId, GroupId aGroupId) +{ + // Get Scene Table Instance + scenes::SceneTable * sceneTable = scenes::GetSceneTableImpl(aEndpointId); + VerifyOrReturn(nullptr != sceneTable); + + MakeSceneInvalid(aEndpointId); + + VerifyOrReturn(nullptr != mGroupProvider); + if (0 != aGroupId && !mGroupProvider->HasEndpoint(aFabricIx, aGroupId, aEndpointId)) + { + return; + } + + sceneTable->DeleteAllScenesInGroup(aFabricIx, aGroupId); +} + +void ScenesServer::MakeSceneInvalid(EndpointId aEndpointId) +{ + Attributes::SceneValid::Set(aEndpointId, false); +} + +void ScenesServer::StoreCurrentScene(FabricIndex aFabricIx, EndpointId aEndpointId, GroupId aGroupId, SceneId aSceneId) +{ + if (CHIP_NO_ERROR == StoreSceneParse(aFabricIx, aEndpointId, aGroupId, aSceneId, mGroupProvider)) + { + Attributes::SceneValid::Set(aEndpointId, true); + } +} +void ScenesServer::RecallScene(FabricIndex aFabricIx, EndpointId aEndpointId, GroupId aGroupId, SceneId aSceneId) +{ + VerifyOrReturn(EMBER_ZCL_STATUS_SUCCESS == Attributes::SceneValid::Set(aEndpointId, false)); + + Optional> transitionTime; + + if (CHIP_NO_ERROR == RecallSceneParse(aFabricIx, aEndpointId, aGroupId, aSceneId, transitionTime, mGroupProvider)) + { + Attributes::SceneValid::Set(aEndpointId, true); + } +} + +void ScenesServer::HandleAddScene(HandlerContext & ctx, const Commands::AddScene::DecodableType & req) +{ + AddSceneParse(ctx, req, mGroupProvider); +} + +void ScenesServer::HandleViewScene(HandlerContext & ctx, const Commands::ViewScene::DecodableType & req) +{ + ViewSceneParse(ctx, req, mGroupProvider); +} + +void ScenesServer::HandleRemoveScene(HandlerContext & ctx, const Commands::RemoveScene::DecodableType & req) +{ + Commands::RemoveSceneResponse::Type response; + + uint16_t endpointTableSize = 0; + ReturnOnFailure( + AddResponseOnError(ctx, response, Attributes::SceneTableSize::Get(ctx.mRequestPath.mEndpointId, &endpointTableSize))); + + // Get Scene Table Instance + scenes::SceneTable * sceneTable = + scenes::GetSceneTableImpl(ctx.mRequestPath.mEndpointId, endpointTableSize); + + // Response data + response.groupID = req.groupID; + response.sceneID = req.sceneID; + + // Scene Table interface data + SceneTableEntry scene(SceneStorageId(req.sceneID, req.groupID)); + + // Verify Endpoint in group + VerifyOrReturn(nullptr != mGroupProvider); + if (0 != req.groupID && + !mGroupProvider->HasEndpoint(ctx.mCommandHandler.GetAccessingFabricIndex(), req.groupID, ctx.mRequestPath.mEndpointId)) + { + response.status = to_underlying(Protocols::InteractionModel::Status::InvalidCommand); + ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); + return; + } + + // Gets the scene from the table + ReturnOnFailure(AddResponseOnError( + ctx, response, sceneTable->GetSceneTableEntry(ctx.mCommandHandler.GetAccessingFabricIndex(), scene.mStorageId, scene))); + + // Remove the scene from the scene table + ReturnOnFailure(AddResponseOnError( + ctx, response, sceneTable->RemoveSceneTableEntry(ctx.mCommandHandler.GetAccessingFabricIndex(), scene.mStorageId))); + + // Update Attributes + MatterReportingAttributeChangeCallback(ctx.mRequestPath.mEndpointId, Id, Attributes::SceneCount::Id); + MatterReportingAttributeChangeCallback(ctx.mRequestPath.mEndpointId, Id, Attributes::RemainingCapacity::Id); + + ReturnOnFailure(UpdateLastConfiguredBy(ctx, response)); + + // Write response + response.status = to_underlying(Protocols::InteractionModel::Status::Success); + ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); +} + +void ScenesServer::HandleRemoveAllScenes(HandlerContext & ctx, const Commands::RemoveAllScenes::DecodableType & req) +{ + Commands::RemoveAllScenesResponse::Type response; + + uint16_t endpointTableSize = 0; + ReturnOnFailure( + AddResponseOnError(ctx, response, Attributes::SceneTableSize::Get(ctx.mRequestPath.mEndpointId, &endpointTableSize))); + + // Get Scene Table Instance + scenes::SceneTable * sceneTable = + scenes::GetSceneTableImpl(ctx.mRequestPath.mEndpointId, endpointTableSize); + + // Response data + response.groupID = req.groupID; + + // Verify Endpoint in group + VerifyOrReturn(nullptr != mGroupProvider); + if (0 != req.groupID && + !mGroupProvider->HasEndpoint(ctx.mCommandHandler.GetAccessingFabricIndex(), req.groupID, ctx.mRequestPath.mEndpointId)) + { + response.status = to_underlying(Protocols::InteractionModel::Status::InvalidCommand); + ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); + return; + } + + ReturnOnFailure(AddResponseOnError( + ctx, response, sceneTable->DeleteAllScenesInGroup(ctx.mCommandHandler.GetAccessingFabricIndex(), req.groupID))); + + // Update Attributes + MatterReportingAttributeChangeCallback(ctx.mRequestPath.mEndpointId, Id, Attributes::SceneCount::Id); + MatterReportingAttributeChangeCallback(ctx.mRequestPath.mEndpointId, Id, Attributes::RemainingCapacity::Id); + + ReturnOnFailure(UpdateLastConfiguredBy(ctx, response)); + + // Write response + response.status = to_underlying(Protocols::InteractionModel::Status::Success); + ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); +} + +void ScenesServer::HandleStoreScene(HandlerContext & ctx, const Commands::StoreScene::DecodableType & req) +{ + Commands::StoreSceneResponse::Type response; + + // Scene Valid is false when this command begins + ReturnOnFailure(AddResponseOnError(ctx, response, Attributes::SceneValid::Set(ctx.mRequestPath.mEndpointId, false))); + + // Response data + response.groupID = req.groupID; + response.sceneID = req.sceneID; + + CHIP_ERROR err = StoreSceneParse(ctx.mCommandHandler.GetAccessingFabricIndex(), ctx.mRequestPath.mEndpointId, req.groupID, + req.sceneID, mGroupProvider); + + if (CHIP_NO_ERROR == err) + { + ReturnOnFailure(AddResponseOnError(ctx, response, Attributes::SceneValid::Set(ctx.mRequestPath.mEndpointId, true))); + + ReturnOnFailure(UpdateLastConfiguredBy(ctx, response)); + } + + response.status = to_underlying(StatusIB(err).mStatus); + ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); +} + +void ScenesServer::HandleRecallScene(HandlerContext & ctx, const Commands::RecallScene::DecodableType & req) +{ + // Scene Valid is false when this command begins + EmberAfStatus status = Attributes::SceneValid::Set(ctx.mRequestPath.mEndpointId, false); + if (EMBER_ZCL_STATUS_SUCCESS != status) + { + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, ToInteractionModelStatus(status)); + return; + } + + CHIP_ERROR err = RecallSceneParse(ctx.mCommandHandler.GetAccessingFabricIndex(), ctx.mRequestPath.mEndpointId, req.groupID, + req.sceneID, req.transitionTime, mGroupProvider); + + if (CHIP_NO_ERROR == err) + { + status = Attributes::SceneValid::Set(ctx.mRequestPath.mEndpointId, true); + if (EMBER_ZCL_STATUS_SUCCESS != status) + { + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, ToInteractionModelStatus(status)); + return; + } + } + + ctx.mCommandHandler.AddStatus(ctx.mRequestPath, StatusIB(err).mStatus); +} + +void ScenesServer::HandleGetSceneMembership(HandlerContext & ctx, const Commands::GetSceneMembership::DecodableType & req) +{ + Commands::GetSceneMembershipResponse::Type response; + + uint16_t endpointTableSize = 0; + ReturnOnFailure( + AddResponseOnError(ctx, response, Attributes::SceneTableSize::Get(ctx.mRequestPath.mEndpointId, &endpointTableSize))); + + // Get Scene Table Instance + scenes::SceneTable * sceneTable = + scenes::GetSceneTableImpl(ctx.mRequestPath.mEndpointId, endpointTableSize); + + // Response data + response.groupID = req.groupID; + + // Scene Table interface data + SceneId scenesInGroup[scenes::kMaxScenesPerFabric]; + Span sceneList = Span(scenesInGroup); + SceneTableEntry scene; + + // Verify Endpoint in group + VerifyOrReturn(nullptr != mGroupProvider); + if (0 != req.groupID && + !mGroupProvider->HasEndpoint(ctx.mCommandHandler.GetAccessingFabricIndex(), req.groupID, ctx.mRequestPath.mEndpointId)) + { + response.status = to_underlying(Protocols::InteractionModel::Status::InvalidCommand); + ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); + return; + } + + uint8_t capacity = 0; + // Get Capacity + ReturnOnFailure(AddResponseOnError(ctx, response, + sceneTable->GetRemainingCapacity(ctx.mCommandHandler.GetAccessingFabricIndex(), capacity))); + response.capacity.SetNonNull(capacity); + + // populate scene list + ReturnOnFailure(AddResponseOnError( + ctx, response, sceneTable->GetAllSceneIdsInGroup(ctx.mCommandHandler.GetAccessingFabricIndex(), req.groupID, sceneList))); + + response.sceneList.SetValue(sceneList); + + // Write response + response.status = to_underlying(Protocols::InteractionModel::Status::Success); + ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); +} + +void ScenesServer::HandleEnhancedAddScene(HandlerContext & ctx, const Commands::EnhancedAddScene::DecodableType & req) +{ + AddSceneParse(ctx, req, mGroupProvider); +} + +void ScenesServer::HandleEnhancedViewScene(HandlerContext & ctx, const Commands::EnhancedViewScene::DecodableType & req) +{ + ViewSceneParse(ctx, req, mGroupProvider); +} +void ScenesServer::HandleCopyScene(HandlerContext & ctx, const Commands::CopyScene::DecodableType & req) +{ + Commands::CopySceneResponse::Type response; + + uint16_t endpointTableSize = 0; + ReturnOnFailure( + AddResponseOnError(ctx, response, Attributes::SceneTableSize::Get(ctx.mRequestPath.mEndpointId, &endpointTableSize))); + + // Get Scene Table Instance + scenes::SceneTable * sceneTable = + scenes::GetSceneTableImpl(ctx.mRequestPath.mEndpointId, endpointTableSize); + + // Response data + response.groupIdentifierFrom = req.groupIdentifierFrom; + response.sceneIdentifierFrom = req.sceneIdentifierFrom; + + // Verify Endpoint in group + VerifyOrReturn(nullptr != mGroupProvider); + if ((0 != req.groupIdentifierFrom && + !mGroupProvider->HasEndpoint(ctx.mCommandHandler.GetAccessingFabricIndex(), req.groupIdentifierFrom, + ctx.mRequestPath.mEndpointId)) || + (0 != req.groupIdentifierTo && + !mGroupProvider->HasEndpoint(ctx.mCommandHandler.GetAccessingFabricIndex(), req.groupIdentifierTo, + ctx.mRequestPath.mEndpointId))) + { + response.status = to_underlying(Protocols::InteractionModel::Status::InvalidCommand); + ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); + return; + } + + uint8_t capacity = 0; + // Get Capacity + ReturnOnFailure(AddResponseOnError(ctx, response, + sceneTable->GetRemainingCapacity(ctx.mCommandHandler.GetAccessingFabricIndex(), capacity))); + + // Checks if we copy a single scene or all of them + if (req.mode.GetField(app::Clusters::Scenes::ScenesCopyMode::kCopyAllScenes)) + { + // Scene Table interface data + SceneId scenesInGroup[scenes::kMaxScenesPerFabric]; + Span sceneList = Span(scenesInGroup); + + // populate scene list + ReturnOnFailure(AddResponseOnError( + ctx, response, + sceneTable->GetAllSceneIdsInGroup(ctx.mCommandHandler.GetAccessingFabricIndex(), req.groupIdentifierFrom, sceneList))); + + if (0 == capacity) + { + response.status = to_underlying(Protocols::InteractionModel::Status::ResourceExhausted); + ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); + return; + } + + for (auto & sceneId : sceneList) + { + SceneTableEntry scene(SceneStorageId(sceneId, req.groupIdentifierFrom)); + // Insert in table + ReturnOnFailure(AddResponseOnError( + ctx, response, + sceneTable->GetSceneTableEntry(ctx.mCommandHandler.GetAccessingFabricIndex(), scene.mStorageId, scene))); + + scene.mStorageId = SceneStorageId(sceneId, req.groupIdentifierTo); + + ReturnOnFailure(AddResponseOnError( + ctx, response, sceneTable->SetSceneTableEntry(ctx.mCommandHandler.GetAccessingFabricIndex(), scene))); + } + + // Update Attributes + MatterReportingAttributeChangeCallback(ctx.mRequestPath.mEndpointId, Id, Attributes::SceneCount::Id); + MatterReportingAttributeChangeCallback(ctx.mRequestPath.mEndpointId, Id, Attributes::RemainingCapacity::Id); + + ReturnOnFailure(UpdateLastConfiguredBy(ctx, response)); + + response.status = to_underlying(Protocols::InteractionModel::Status::Success); + ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); + return; + } + + SceneTableEntry scene(SceneStorageId(req.sceneIdentifierFrom, req.groupIdentifierFrom)); + ReturnOnFailure(AddResponseOnError( + ctx, response, sceneTable->GetSceneTableEntry(ctx.mCommandHandler.GetAccessingFabricIndex(), scene.mStorageId, scene))); + + scene.mStorageId = SceneStorageId(req.sceneIdentifierTo, req.groupIdentifierTo); + + ReturnOnFailure( + AddResponseOnError(ctx, response, sceneTable->SetSceneTableEntry(ctx.mCommandHandler.GetAccessingFabricIndex(), scene))); + + // Update Attributes + MatterReportingAttributeChangeCallback(ctx.mRequestPath.mEndpointId, Id, Attributes::SceneCount::Id); + MatterReportingAttributeChangeCallback(ctx.mRequestPath.mEndpointId, Id, Attributes::RemainingCapacity::Id); + + ReturnOnFailure(UpdateLastConfiguredBy(ctx, response)); + + response.status = to_underlying(Protocols::InteractionModel::Status::Success); + ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); +} + +} // namespace Scenes +} // namespace Clusters +} // namespace app +} // namespace chip + +void MatterScenesPluginServerInitCallback() +{ + CHIP_ERROR err = ScenesServer::Instance().Init(); + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "ScenesServer::Instance().Init() error: %" CHIP_ERROR_FORMAT, err.Format()); + } +} diff --git a/src/app/clusters/scenes-server/scenes-server.h b/src/app/clusters/scenes-server/scenes-server.h new file mode 100644 index 00000000000000..ed14cc8cfb1eb0 --- /dev/null +++ b/src/app/clusters/scenes-server/scenes-server.h @@ -0,0 +1,82 @@ +/** + * + * Copyright (c) 2023 Project CHIP Authors + * + * 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. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace chip { +namespace app { +namespace Clusters { +namespace Scenes { + +class ScenesServer : public CommandHandlerInterface, public AttributeAccessInterface +{ +public: + static ScenesServer & Instance(); + + CHIP_ERROR Init(); + void Shutdown(); + + // CommandHanlerInterface + void InvokeCommand(HandlerContext & ctx) override; + + // AttributeAccessInterface + CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override; + + // Callbacks + void GroupWillBeRemoved(FabricIndex aFabricIx, EndpointId aEndpointId, GroupId aGroupId); + void MakeSceneInvalid(EndpointId aEndpointId); + void StoreCurrentScene(FabricIndex aFabricIx, EndpointId aEndpointId, GroupId aGroupId, SceneId aSceneId); + void RecallScene(FabricIndex aFabricIx, EndpointId aEndpointId, GroupId aGroupId, SceneId aSceneId); + +private: + ScenesServer() : CommandHandlerInterface(Optional(), Id), AttributeAccessInterface(Optional(), Id) {} + ~ScenesServer() {} + + bool mIsInitialized = false; + + // Command handlers + void HandleAddScene(HandlerContext & ctx, const Commands::AddScene::DecodableType & req); + void HandleViewScene(HandlerContext & ctx, const Commands::ViewScene::DecodableType & req); + void HandleRemoveScene(HandlerContext & ctx, const Commands::RemoveScene::DecodableType & req); + void HandleRemoveAllScenes(HandlerContext & ctx, const Commands::RemoveAllScenes::DecodableType & req); + void HandleStoreScene(HandlerContext & ctx, const Commands::StoreScene::DecodableType & req); + void HandleRecallScene(HandlerContext & ctx, const Commands::RecallScene::DecodableType & req); + void HandleGetSceneMembership(HandlerContext & ctx, const Commands::GetSceneMembership::DecodableType & req); + void HandleEnhancedAddScene(HandlerContext & ctx, const Commands::EnhancedAddScene::DecodableType & req); + void HandleEnhancedViewScene(HandlerContext & ctx, const Commands::EnhancedViewScene::DecodableType & req); + void HandleCopyScene(HandlerContext & ctx, const Commands::CopyScene::DecodableType & req); + + // Group Data Provider + Credentials::GroupDataProvider * mGroupProvider = nullptr; + + // Instance + static ScenesServer mInstance; +}; + +} // namespace Scenes +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/src/app/clusters/scenes/scenes-tokens.h b/src/app/clusters/scenes/scenes-tokens.h deleted file mode 100644 index a644efcb317d25..00000000000000 --- a/src/app/clusters/scenes/scenes-tokens.h +++ /dev/null @@ -1,41 +0,0 @@ -/** - * - * Copyright (c) 2020 Project CHIP Authors - * - * 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. - */ - -#pragma once - -#include - -#ifdef EMBER_AF_PLUGIN_SCENES_USE_TOKENS - -#define CREATOR_SCENES_NUM_ENTRIES (0x8723) -#define NVM3KEY_SCENES_NUM_ENTRIES (NVM3KEY_DOMAIN_ZIGBEE | 0x8723) - -#define CREATOR_SCENES_TABLE (0x8724) -// This key is used for an indexed token and the subsequent 0x7F keys are also reserved -#define NVM3KEY_SCENES_TABLE (NVM3KEY_DOMAIN_ZIGBEE | 0x4080) - -#ifdef DEFINETYPES -// Include or define any typedef for tokens here -#endif // DEFINETYPES -#ifdef DEFINETOKENS -// Define the actual token storage information here - -DEFINE_BASIC_TOKEN(SCENES_NUM_ENTRIES, uint8_t, 0x00) -DEFINE_INDEXED_TOKEN(SCENES_TABLE, EmberAfSceneTableEntry, MATTER_SCENES_TABLE_SIZE, { EMBER_AF_SCENE_TABLE_UNUSED_ENDPOINT_ID }) -#endif // DEFINETOKENS - -#endif // EMBER_AF_PLUGIN_SCENES_USE_TOKENS diff --git a/src/app/clusters/scenes/scenes.cpp b/src/app/clusters/scenes/scenes.cpp deleted file mode 100644 index c2a60b3f30b49f..00000000000000 --- a/src/app/clusters/scenes/scenes.cpp +++ /dev/null @@ -1,1335 +0,0 @@ -/** - * - * Copyright (c) 2020 Project CHIP Authors - * - * 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 "scenes.h" -#include "app/util/common.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef EMBER_AF_PLUGIN_GROUPS_SERVER -#include -#endif - -#ifdef EMBER_AF_PLUGIN_ZLL_SCENES_SERVER -#include "../zll-scenes-server/zll-scenes-server.h" -#endif - -using namespace chip; -using namespace chip::app::Clusters; -using namespace chip::app::Clusters::Scenes; -using namespace chip::app::Clusters::Scenes::Commands; - -uint8_t emberAfPluginScenesServerEntriesInUse = 0; -#if !defined(EMBER_AF_PLUGIN_SCENES_USE_TOKENS) || defined(EZSP_HOST) -EmberAfSceneTableEntry emberAfPluginScenesServerSceneTable[MATTER_SCENES_TABLE_SIZE]; -#endif - -static bool logReadError(EmberAfStatus status, const char * attributeName) -{ - // Don't log errors for the "this cluster is not even supported" cases. - if (status != EMBER_ZCL_STATUS_SUCCESS && status != EMBER_ZCL_STATUS_UNSUPPORTED_ENDPOINT && - status != EMBER_ZCL_STATUS_UNSUPPORTED_CLUSTER) - { - emberAfScenesClusterPrintln("ERR: %sing %s 0x%x", "read", attributeName, status); - } - return status == EMBER_ZCL_STATUS_SUCCESS; -} - -static EmberAfStatus logWriteError(EmberAfStatus status, const char * attributeName) -{ - if (status != EMBER_ZCL_STATUS_SUCCESS) - { - emberAfScenesClusterPrintln("ERR: %sing %s 0x%x", "writ", attributeName, status); - } - return status; -} - -bool isEndpointInGroup(chip::FabricIndex fabricIndex, EndpointId endpoint, GroupId groupId) -{ -#ifdef EMBER_AF_PLUGIN_GROUPS_SERVER - return (groupId == ZCL_SCENES_GLOBAL_SCENE_GROUP_ID || - emberAfGroupsClusterEndpointInGroupCallback(fabricIndex, endpoint, groupId)); -#else - return (groupId == ZCL_SCENES_GLOBAL_SCENE_GROUP_ID); -#endif // EMBER_AF_PLUGIN_GROUPS_SERVER -} - -void emberAfScenesClusterServerInitCallback(EndpointId endpoint) -{ -#if defined(MATTER_CLUSTER_SCENE_NAME_SUPPORT) && MATTER_CLUSTER_SCENE_NAME_SUPPORT - { - // The high bit of Name Support indicates whether scene names are supported. - uint8_t nameSupport = EMBER_BIT(7); - logWriteError(Attributes::NameSupport::Set(endpoint, nameSupport), "NameSupport"); - } -#endif -#if !defined(EMBER_AF_PLUGIN_SCENES_USE_TOKENS) || defined(EZSP_HOST) - { - uint8_t i; - for (i = 0; i < MATTER_SCENES_TABLE_SIZE; i++) - { - EmberAfSceneTableEntry entry; - emberAfPluginScenesServerRetrieveSceneEntry(entry, i); - entry.endpoint = EMBER_AF_SCENE_TABLE_UNUSED_ENDPOINT_ID; - emberAfPluginScenesServerSaveSceneEntry(entry, i); - } - emberAfPluginScenesServerSetNumSceneEntriesInUse(0); - } -#endif - emberAfScenesSetSceneCountAttribute(endpoint, emberAfPluginScenesServerNumSceneEntriesInUse()); -} - -EmberAfStatus emberAfScenesSetSceneCountAttribute(EndpointId endpoint, uint8_t newCount) -{ - return logWriteError(Attributes::SceneCount::Set(endpoint, newCount), "SceneCount"); -} - -EmberAfStatus emberAfScenesMakeValid(EndpointId endpoint, uint8_t sceneId, GroupId groupId) -{ - EmberAfStatus status; - bool valid = true; - - // scene ID - status = logWriteError(Attributes::CurrentScene::Set(endpoint, sceneId), "CurrentScene"); - if (status != EMBER_ZCL_STATUS_SUCCESS) - { - return status; - } - - // group ID - status = logWriteError(Attributes::CurrentGroup::Set(endpoint, groupId), "CurrentGroup"); - if (status != EMBER_ZCL_STATUS_SUCCESS) - { - return status; - } - - status = logWriteError(Attributes::SceneValid::Set(endpoint, valid), "SceneValid"); - return status; -} - -EmberAfStatus emberAfScenesClusterMakeInvalidCallback(EndpointId endpoint) -{ - bool valid = false; - return logWriteError(Attributes::SceneValid::Set(endpoint, valid), "SceneValid"); -} - -void emAfPluginScenesServerPrintInfo() -{ - uint8_t i; - EmberAfSceneTableEntry entry; - emberAfCorePrintln("using 0x%x out of 0x%x table slots", emberAfPluginScenesServerNumSceneEntriesInUse(), - MATTER_SCENES_TABLE_SIZE); - for (i = 0; i < MATTER_SCENES_TABLE_SIZE; i++) - { - emberAfPluginScenesServerRetrieveSceneEntry(entry, i); - emberAfCorePrint("%x: ", i); - if (entry.endpoint != EMBER_AF_SCENE_TABLE_UNUSED_ENDPOINT_ID) - { - emberAfCorePrint("ep %x grp %2x scene %x tt %d", entry.endpoint, entry.groupId, entry.sceneId, entry.transitionTime); - emberAfCorePrint(".%d", entry.transitionTime100ms); -#if defined(MATTER_CLUSTER_SCENE_NAME_SUPPORT) && MATTER_CLUSTER_SCENE_NAME_SUPPORT - emberAfCorePrint(" name(%x)\"", emberAfStringLength(entry.name)); - emberAfCorePrintString(entry.name); - emberAfCorePrint("\""); -#endif -#ifdef ZCL_USING_ON_OFF_CLUSTER_SERVER - emberAfCorePrint(" on/off %x", entry.onOffValue); -#endif -#ifdef ZCL_USING_LEVEL_CONTROL_CLUSTER_SERVER - if (entry.currentLevelValue.IsNull()) - { - emberAfCorePrint(" lvl null"); - } - else - { - emberAfCorePrint(" lvl %x", entry.currentLevelValue.Value()); - } -#endif -#ifdef ZCL_USING_THERMOSTAT_CLUSTER_SERVER - emberAfCorePrint(" therm %2x %2x %x", entry.occupiedCoolingSetpointValue, entry.occupiedHeatingSetpointValue, - entry.systemModeValue); -#endif -#ifdef ZCL_USING_COLOR_CONTROL_CLUSTER_SERVER - emberAfCorePrint(" color %2x %2x", entry.currentXValue, entry.currentYValue); - emberAfCorePrint(" %2x %x %x %x %2x %2x", entry.enhancedCurrentHueValue, entry.currentSaturationValue, - entry.colorLoopActiveValue, entry.colorLoopDirectionValue, entry.colorLoopTimeValue, - entry.colorTemperatureMiredsValue); - emberAfCoreFlush(); -#endif // ZCL_USING_COLOR_CONTROL_CLUSTER_SERVER -#ifdef ZCL_USING_DOOR_LOCK_CLUSTER_SERVER - if (entry.lockStateValue.IsNull()) - { - emberAfCorePrint(" door null"); - } - else - { - emberAfCorePrint(" door %x", to_underlying(entry.lockStateValue.Value())); - } -#endif -#ifdef ZCL_USING_WINDOW_COVERING_CLUSTER_SERVER - if (!entry.currentPositionLiftPercentageValue.IsNull() && !entry.currentPositionTiltPercentageValue.IsNull()) - { - emberAfCorePrint(" Window current percentage Lift %3u, Tilt %3u", entry.currentPositionLiftPercentageValue.Value(), - entry.currentPositionTiltPercentageValue.Value()); - } - else if (!entry.currentPositionLiftPercentageValue.IsNull()) - { - emberAfCorePrint(" Window current percentage Lift %3u", entry.currentPositionLiftPercentageValue.Value()); - } - else if (!entry.currentPositionTiltPercentageValue.IsNull()) - { - emberAfCorePrint(" Window current percentage Tilt %3u", entry.currentPositionTiltPercentageValue.Value()); - } - - if (!entry.targetPositionLiftPercent100thsValue.IsNull() && !entry.targetPositionTiltPercent100thsValue.IsNull()) - { - emberAfCorePrint(" Window target percent100ths Lift %5u, Tilt %5u", - entry.targetPositionLiftPercent100thsValue.Value(), - entry.targetPositionTiltPercent100thsValue.Value()); - } - else if (!entry.targetPositionLiftPercent100thsValue.IsNull()) - { - emberAfCorePrint(" Window target percent100ths Lift %5u", entry.targetPositionLiftPercent100thsValue.Value()); - } - else if (!entry.targetPositionTiltPercent100thsValue.IsNull()) - { - emberAfCorePrint(" Window target percent100ths Tilt %5u", entry.targetPositionTiltPercent100thsValue.Value()); - } -#endif - } - emberAfCorePrintln("%s", ""); - } -} - -bool emberAfScenesClusterAddSceneCallback(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, - const Commands::AddScene::DecodableType & commandData) -{ - auto & groupId = commandData.groupID; - auto & sceneId = commandData.sceneID; - auto & transitionTime = commandData.transitionTime; - auto & sceneName = commandData.sceneName; - auto & extensionFieldSets = commandData.extensionFieldSets; - - return emberAfPluginScenesServerParseAddScene(commandObj, commandPath, groupId, sceneId, transitionTime, sceneName, - extensionFieldSets); -} - -bool emberAfScenesClusterViewSceneCallback(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, - const Commands::ViewScene::DecodableType & commandData) -{ - auto & groupId = commandData.groupID; - auto & sceneId = commandData.sceneID; - - return emberAfPluginScenesServerParseViewScene(commandObj, commandPath, groupId, sceneId); -} - -bool emberAfScenesClusterRemoveSceneCallback(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, - const Commands::RemoveScene::DecodableType & commandData) -{ - auto fabricIndex = commandObj->GetAccessingFabricIndex(); - auto & groupId = commandData.groupID; - auto & sceneId = commandData.sceneID; - - EmberAfStatus status = EMBER_ZCL_STATUS_NOT_FOUND; - CHIP_ERROR err = CHIP_NO_ERROR; - - emberAfScenesClusterPrintln("RX: RemoveScene 0x%2x, 0x%x", groupId, sceneId); - - if (!isEndpointInGroup(fabricIndex, commandPath.mEndpointId, groupId)) - { - status = EMBER_ZCL_STATUS_INVALID_COMMAND; - } - else - { - uint8_t i; - for (i = 0; i < MATTER_SCENES_TABLE_SIZE; i++) - { - EmberAfSceneTableEntry entry; - emberAfPluginScenesServerRetrieveSceneEntry(entry, i); - if (entry.endpoint == commandPath.mEndpointId && entry.groupId == groupId && entry.sceneId == sceneId) - { - entry.endpoint = EMBER_AF_SCENE_TABLE_UNUSED_ENDPOINT_ID; - emberAfPluginScenesServerSaveSceneEntry(entry, i); - emberAfPluginScenesServerDecrNumSceneEntriesInUse(); - emberAfScenesSetSceneCountAttribute(commandPath.mEndpointId, emberAfPluginScenesServerNumSceneEntriesInUse()); - status = EMBER_ZCL_STATUS_SUCCESS; - break; - } - } - } - - app::ConcreteCommandPath path = { commandPath.mEndpointId, Scenes::Id, RemoveSceneResponse::Id }; - TLV::TLVWriter * writer = nullptr; - SuccessOrExit(err = commandObj->PrepareCommand(path)); - VerifyOrExit((writer = commandObj->GetCommandDataIBTLVWriter()) != nullptr, err = CHIP_ERROR_INCORRECT_STATE); - SuccessOrExit(err = writer->Put(TLV::ContextTag(0), status)); - SuccessOrExit(err = writer->Put(TLV::ContextTag(1), groupId)); - SuccessOrExit(err = writer->Put(TLV::ContextTag(2), sceneId)); - SuccessOrExit(err = commandObj->FinishCommand()); - -exit: - if (err != CHIP_NO_ERROR) - { - ChipLogError(Zcl, "Failed to encode response command."); - } - return true; -} - -bool emberAfScenesClusterRemoveAllScenesCallback(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, - const Commands::RemoveAllScenes::DecodableType & commandData) -{ - auto fabricIndex = commandObj->GetAccessingFabricIndex(); - auto & groupId = commandData.groupID; - - EmberAfStatus status = EMBER_ZCL_STATUS_INVALID_COMMAND; - CHIP_ERROR err = CHIP_NO_ERROR; - - emberAfScenesClusterPrintln("RX: RemoveAllScenes 0x%2x", groupId); - - if (isEndpointInGroup(fabricIndex, commandPath.mEndpointId, groupId)) - { - uint8_t i; - status = EMBER_ZCL_STATUS_SUCCESS; - for (i = 0; i < MATTER_SCENES_TABLE_SIZE; i++) - { - EmberAfSceneTableEntry entry; - emberAfPluginScenesServerRetrieveSceneEntry(entry, i); - if (entry.endpoint == commandPath.mEndpointId && entry.groupId == groupId) - { - entry.endpoint = EMBER_AF_SCENE_TABLE_UNUSED_ENDPOINT_ID; - emberAfPluginScenesServerSaveSceneEntry(entry, i); - emberAfPluginScenesServerDecrNumSceneEntriesInUse(); - } - } - emberAfScenesSetSceneCountAttribute(commandPath.mEndpointId, emberAfPluginScenesServerNumSceneEntriesInUse()); - } - - app::ConcreteCommandPath path = { commandPath.mEndpointId, Scenes::Id, RemoveAllScenesResponse::Id }; - TLV::TLVWriter * writer = nullptr; - SuccessOrExit(err = commandObj->PrepareCommand(path)); - VerifyOrExit((writer = commandObj->GetCommandDataIBTLVWriter()) != nullptr, err = CHIP_ERROR_INCORRECT_STATE); - SuccessOrExit(err = writer->Put(TLV::ContextTag(0), status)); - SuccessOrExit(err = writer->Put(TLV::ContextTag(1), groupId)); - SuccessOrExit(err = commandObj->FinishCommand()); -exit: - if (err != CHIP_NO_ERROR) - { - ChipLogError(Zcl, "Failed to encode response command."); - } - return true; -} - -bool emberAfScenesClusterStoreSceneCallback(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, - const Commands::StoreScene::DecodableType & commandData) -{ - auto fabricIndex = commandObj->GetAccessingFabricIndex(); - auto & groupId = commandData.groupID; - auto & sceneId = commandData.sceneID; - - EmberAfStatus status; - CHIP_ERROR err = CHIP_NO_ERROR; - emberAfScenesClusterPrintln("RX: StoreScene 0x%2x, 0x%x", groupId, sceneId); - status = emberAfScenesClusterStoreCurrentSceneCallback(fabricIndex, commandPath.mEndpointId, groupId, sceneId); - - app::ConcreteCommandPath path = { commandPath.mEndpointId, Scenes::Id, StoreSceneResponse::Id }; - TLV::TLVWriter * writer = nullptr; - SuccessOrExit(err = commandObj->PrepareCommand(path)); - VerifyOrExit((writer = commandObj->GetCommandDataIBTLVWriter()) != nullptr, err = CHIP_ERROR_INCORRECT_STATE); - SuccessOrExit(err = writer->Put(TLV::ContextTag(0), status)); - SuccessOrExit(err = writer->Put(TLV::ContextTag(1), groupId)); - SuccessOrExit(err = writer->Put(TLV::ContextTag(2), sceneId)); - SuccessOrExit(err = commandObj->FinishCommand()); -exit: - if (err != CHIP_NO_ERROR) - { - ChipLogError(Zcl, "Failed to encode response command."); - } - return true; -} - -bool emberAfScenesClusterRecallSceneCallback(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, - const Commands::RecallScene::DecodableType & commandData) -{ - auto fabricIndex = commandObj->GetAccessingFabricIndex(); - auto & groupId = commandData.groupID; - auto & sceneId = commandData.sceneID; - - // NOTE: TransitionTime field in the RecallScene command is currently - // ignored. Per Zigbee Alliance ZCL 7 (07-5123-07): - // - // "The transition time determines how long the tranition takes from the - // old cluster state to the new cluster state. It is recommended that, where - // possible (e.g., it is not possible for attributes with Boolean type), - // a gradual transition SHOULD take place from the old to the new state - // over this time. However, the exact transition is manufacturer dependent." - // - // The manufacturer-dependent implementation here is to immediately set - // all attributes to their scene-specified values, without regard to the - // value of TransitionTime. - - EmberAfStatus status; - emberAfScenesClusterPrintln("RX: RecallScene 0x%2x, 0x%x", groupId, sceneId); - status = emberAfScenesClusterRecallSavedSceneCallback(fabricIndex, commandPath.mEndpointId, groupId, sceneId); -#ifdef EMBER_AF_PLUGIN_ZLL_SCENES_SERVER - if (status == EMBER_ZCL_STATUS_SUCCESS) - { - emberAfPluginZllScenesServerRecallSceneZllExtensions(commandPath.mEndpointId); - } -#endif - CHIP_ERROR sendErr = commandObj->AddStatus(commandPath, app::ToInteractionModelStatus(status)); - if (CHIP_NO_ERROR != sendErr) - { - emberAfScenesClusterPrintln("Scenes: failed to send %s: %" CHIP_ERROR_FORMAT, "status_response", sendErr.Format()); - } - return true; -} - -bool emberAfScenesClusterGetSceneMembershipCallback(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, - const Commands::GetSceneMembership::DecodableType & commandData) -{ - auto fabricIndex = commandObj->GetAccessingFabricIndex(); - auto & groupId = commandData.groupID; - - CHIP_ERROR err = CHIP_NO_ERROR; - EmberAfStatus status = EMBER_ZCL_STATUS_SUCCESS; - uint8_t sceneCount = 0; - uint8_t sceneList[MATTER_SCENES_TABLE_SIZE]; - - emberAfScenesClusterPrintln("RX: GetSceneMembership 0x%2x", groupId); - - if (!isEndpointInGroup(fabricIndex, commandPath.mEndpointId, groupId)) - { - status = EMBER_ZCL_STATUS_INVALID_COMMAND; - } - - if (status == EMBER_ZCL_STATUS_SUCCESS) - { - uint8_t i; - for (i = 0; i < MATTER_SCENES_TABLE_SIZE; i++) - { - EmberAfSceneTableEntry entry; - emberAfPluginScenesServerRetrieveSceneEntry(entry, i); - if (entry.endpoint == commandPath.mEndpointId && entry.groupId == groupId) - { - sceneList[sceneCount] = entry.sceneId; - sceneCount++; - } - } - emberAfPutInt8uInResp(sceneCount); - for (i = 0; i < sceneCount; i++) - { - emberAfPutInt8uInResp(sceneList[i]); - } - } - - { - app::ConcreteCommandPath path = { commandPath.mEndpointId, Scenes::Id, GetSceneMembershipResponse::Id }; - TLV::TLVWriter * writer = nullptr; - SuccessOrExit(err = commandObj->PrepareCommand(path)); - VerifyOrExit((writer = commandObj->GetCommandDataIBTLVWriter()) != nullptr, err = CHIP_ERROR_INCORRECT_STATE); - SuccessOrExit(err = writer->Put(TLV::ContextTag(0), status)); - SuccessOrExit( - err = writer->Put(TLV::ContextTag(1), - static_cast(MATTER_SCENES_TABLE_SIZE - emberAfPluginScenesServerNumSceneEntriesInUse()))); - SuccessOrExit(err = writer->Put(TLV::ContextTag(2), groupId)); - SuccessOrExit(err = writer->Put(TLV::ContextTag(3), sceneCount)); - SuccessOrExit(err = writer->Put(TLV::ContextTag(4), ByteSpan(sceneList, sceneCount))); - SuccessOrExit(err = commandObj->FinishCommand()); - } - -exit: - if (err != CHIP_NO_ERROR) - { - ChipLogError(Zcl, "Failed to encode response command."); - } - return true; -} - -EmberAfStatus emberAfScenesClusterStoreCurrentSceneCallback(chip::FabricIndex fabricIndex, EndpointId endpoint, GroupId groupId, - uint8_t sceneId) -{ - EmberAfSceneTableEntry entry; - uint8_t i, index = EMBER_AF_SCENE_TABLE_NULL_INDEX; - - if (!isEndpointInGroup(fabricIndex, endpoint, groupId)) - { - return EMBER_ZCL_STATUS_INVALID_COMMAND; - } - - for (i = 0; i < MATTER_SCENES_TABLE_SIZE; i++) - { - emberAfPluginScenesServerRetrieveSceneEntry(entry, i); - if (entry.endpoint == endpoint && entry.groupId == groupId && entry.sceneId == sceneId) - { - index = i; - break; - } - if (index == EMBER_AF_SCENE_TABLE_NULL_INDEX && entry.endpoint == EMBER_AF_SCENE_TABLE_UNUSED_ENDPOINT_ID) - { - index = i; - } - } - - // If the target index is still zero, the table is full. - if (index == EMBER_AF_SCENE_TABLE_NULL_INDEX) - { - return EMBER_ZCL_STATUS_RESOURCE_EXHAUSTED; - } - - emberAfPluginScenesServerRetrieveSceneEntry(entry, index); - - // When creating a new entry or refreshing an existing one, the extension - // fields are updated with the current state of other clusters on the device. -#ifdef ZCL_USING_ON_OFF_CLUSTER_SERVER - entry.hasOnOffValue = logReadError(OnOff::Attributes::OnOff::Get(endpoint, &entry.onOffValue), "OnOff"); -#endif -#ifdef ZCL_USING_LEVEL_CONTROL_CLUSTER_SERVER - entry.hasCurrentLevelValue = - logReadError(LevelControl::Attributes::CurrentLevel::Get(endpoint, entry.currentLevelValue), "CurrentLevel"); -#endif -#ifdef ZCL_USING_THERMOSTAT_CLUSTER_SERVER - { - using namespace Thermostat::Attributes; - entry.hasOccupiedCoolingSetpointValue = - logReadError(OccupiedCoolingSetpoint::Get(endpoint, &entry.occupiedCoolingSetpointValue), "OccupiedCoolingSetpoint"); - entry.hasOccupiedHeatingSetpointValue = - logReadError(OccupiedHeatingSetpoint::Get(endpoint, &entry.occupiedHeatingSetpointValue), "OccupiedHeatingSetpoint"); - entry.hasSystemModeValue = logReadError(SystemMode::Get(endpoint, &entry.systemModeValue), "SystemMode"); - } -#endif -#ifdef ZCL_USING_COLOR_CONTROL_CLUSTER_SERVER - { - using namespace ColorControl::Attributes; - entry.hasCurrentXValue = logReadError(CurrentX::Get(endpoint, &entry.currentXValue), "CurrentX"); - entry.hasCurrentYValue = logReadError(CurrentY::Get(endpoint, &entry.currentYValue), "CurrentY"); - entry.hasEnhancedCurrentHueValue = - logReadError(EnhancedCurrentHue::Get(endpoint, &entry.enhancedCurrentHueValue), "EnhancedCurrentHue"); - entry.hasCurrentSaturationValue = - logReadError(CurrentSaturation::Get(endpoint, &entry.currentSaturationValue), "CurrentSaturation"); - entry.hasColorLoopActiveValue = - logReadError(ColorLoopActive::Get(endpoint, &entry.colorLoopActiveValue), "ColorLoopActive"); - entry.hasColorLoopDirectionValue = - logReadError(ColorLoopDirection::Get(endpoint, &entry.colorLoopDirectionValue), "ColorLoopDirection"); - entry.hasColorLoopTimeValue = logReadError(ColorLoopTime::Get(endpoint, &entry.colorLoopTimeValue), "ColorLoopTime"); - entry.hasColorTemperatureMiredsValue = - logReadError(ColorTemperatureMireds::Get(endpoint, &entry.colorTemperatureMiredsValue), "ColorTemperatureMireds"); - } -#endif // ZCL_USING_COLOR_CONTROL_CLUSTER_SERVER -#ifdef ZCL_USING_DOOR_LOCK_CLUSTER_SERVER - entry.hasLockStateValue = logReadError(DoorLock::Attributes::LockState::Get(endpoint, entry.lockStateValue), "LockState"); -#endif -#ifdef ZCL_USING_WINDOW_COVERING_CLUSTER_SERVER - { - using namespace WindowCovering::Attributes; - entry.hasCurrentPositionLiftPercentageValue = - logReadError(CurrentPositionLiftPercentage::Get(endpoint, entry.currentPositionLiftPercentageValue), - "CurrentPositionLiftPercentage"); - entry.hasCurrentPositionTiltPercentageValue = - logReadError(CurrentPositionTiltPercentage::Get(endpoint, entry.currentPositionTiltPercentageValue), - "CurrentPositionTiltPercentage"); - entry.hasTargetPositionLiftPercent100thsValue = - logReadError(TargetPositionLiftPercent100ths::Get(endpoint, entry.targetPositionLiftPercent100thsValue), - "TragetPositionLiftPercent100ths"); - entry.hasTargetPositionTiltPercent100thsValue = - logReadError(TargetPositionTiltPercent100ths::Get(endpoint, entry.targetPositionTiltPercent100thsValue), - "TragetPositionTiltPercent100ths"); - } -#endif - - // When creating a new entry, the name is set to the null string (i.e., the - // length is set to zero) and the transition time is set to zero. The scene - // count must be increased and written to the attribute table when adding a - // new scene. Otherwise, these fields and the count are left alone. - if (i != index) - { - entry.endpoint = endpoint; - entry.groupId = groupId; - entry.sceneId = sceneId; -#if defined(MATTER_CLUSTER_SCENE_NAME_SUPPORT) && MATTER_CLUSTER_SCENE_NAME_SUPPORT - entry.name[0] = 0; -#endif - entry.transitionTime = 0; - entry.transitionTime100ms = 0; - emberAfPluginScenesServerIncrNumSceneEntriesInUse(); - emberAfScenesSetSceneCountAttribute(endpoint, emberAfPluginScenesServerNumSceneEntriesInUse()); - } - - // Save the scene entry and mark is as valid by storing its scene and group - // ids in the attribute table and setting valid to true. - emberAfPluginScenesServerSaveSceneEntry(entry, index); - emberAfScenesMakeValid(endpoint, sceneId, groupId); - return EMBER_ZCL_STATUS_SUCCESS; -} - -EmberAfStatus emberAfScenesClusterRecallSavedSceneCallback(chip::FabricIndex fabricIndex, EndpointId endpoint, GroupId groupId, - uint8_t sceneId) -{ - if (!isEndpointInGroup(fabricIndex, endpoint, groupId)) - { - return EMBER_ZCL_STATUS_INVALID_COMMAND; - } - - uint8_t i; - for (i = 0; i < MATTER_SCENES_TABLE_SIZE; i++) - { - EmberAfSceneTableEntry entry; - emberAfPluginScenesServerRetrieveSceneEntry(entry, i); - if (entry.endpoint == endpoint && entry.groupId == groupId && entry.sceneId == sceneId) - { -#ifdef ZCL_USING_ON_OFF_CLUSTER_SERVER - if (entry.hasOnOffValue) - { - logWriteError(OnOff::Attributes::OnOff::Set(endpoint, entry.onOffValue), "OnOff"); - } -#endif -#ifdef ZCL_USING_LEVEL_CONTROL_CLUSTER_SERVER - if (entry.hasCurrentLevelValue) - { - logWriteError(LevelControl::Attributes::CurrentLevel::Set(endpoint, entry.currentLevelValue), "CurrentLevel"); - } -#endif -#ifdef ZCL_USING_THERMOSTAT_CLUSTER_SERVER - { - using namespace Thermostat::Attributes; - if (entry.hasOccupiedCoolingSetpointValue) - { - logWriteError(OccupiedCoolingSetpoint::Set(endpoint, entry.occupiedCoolingSetpointValue), - "OccupiedCoolingSetpoint"); - } - if (entry.hasOccupiedHeatingSetpointValue) - { - logWriteError(OccupiedHeatingSetpoint::Set(endpoint, entry.occupiedHeatingSetpointValue), - "OccupiedHeatingSetpoint"); - } - if (entry.hasSystemModeValue) - { - logWriteError(SystemMode::Set(endpoint, entry.systemModeValue), "SystemMode"); - } - } -#endif -#ifdef ZCL_USING_COLOR_CONTROL_CLUSTER_SERVER - { - using namespace ColorControl::Attributes; - if (entry.hasCurrentXValue) - { - logWriteError(CurrentX::Set(endpoint, entry.currentXValue), "CurrentX"); - } - if (entry.hasCurrentYValue) - { - logWriteError(CurrentY::Set(endpoint, entry.currentXValue), "CurrentY"); - } - - if (entry.hasEnhancedCurrentHueValue) - { - logWriteError(EnhancedCurrentHue::Set(endpoint, entry.enhancedCurrentHueValue), "EnhancedCurrentHue"); - } - if (entry.hasCurrentSaturationValue) - { - logWriteError(CurrentSaturation::Set(endpoint, entry.currentSaturationValue), "CurrentSaturation"); - } - if (entry.hasColorLoopActiveValue) - { - logWriteError(ColorLoopActive::Set(endpoint, entry.colorLoopActiveValue), "ColorLoopActive"); - } - if (entry.hasColorLoopDirectionValue) - { - logWriteError(ColorLoopDirection::Set(endpoint, entry.colorLoopDirectionValue), "ColorLoopDirection"); - } - if (entry.hasColorLoopTimeValue) - { - logWriteError(ColorLoopTime::Set(endpoint, entry.colorLoopTimeValue), "ColorLoopTime"); - } - if (entry.hasColorTemperatureMiredsValue) - { - logWriteError(ColorTemperatureMireds::Set(endpoint, entry.colorTemperatureMiredsValue), - "ColorTemperatureMireds"); - } - } -#endif // ZCL_USING_COLOR_CONTROL_CLUSTER_SERVER -#ifdef ZCL_USING_DOOR_LOCK_CLUSTER_SERVER - if (entry.hasLockStateValue) - { - logWriteError(DoorLock::Attributes::LockState::Set(endpoint, entry.lockStateValue), "LockState"); - } -#endif -#ifdef ZCL_USING_WINDOW_COVERING_CLUSTER_SERVER - { - using namespace WindowCovering::Attributes; - if (entry.hasCurrentPositionLiftPercentageValue) - { - logWriteError(CurrentPositionLiftPercentage::Set(endpoint, entry.currentPositionLiftPercentageValue), - "CurrentPositionLiftPercentage"); - } - if (entry.hasCurrentPositionTiltPercentageValue) - { - logWriteError(CurrentPositionTiltPercentage::Set(endpoint, entry.currentPositionTiltPercentageValue), - "CurrentPositionTiltPercentage"); - } - if (entry.hasTargetPositionLiftPercent100thsValue) - { - logWriteError(TargetPositionLiftPercent100ths::Set(endpoint, entry.targetPositionLiftPercent100thsValue), - "TargetPositionLiftPercent100ths"); - } - if (entry.hasTargetPositionTiltPercent100thsValue) - { - logWriteError(TargetPositionTiltPercent100ths::Set(endpoint, entry.targetPositionTiltPercent100thsValue), - "TargetPositionTiltPercent100ths"); - } - } -#endif - emberAfScenesMakeValid(endpoint, sceneId, groupId); - return EMBER_ZCL_STATUS_SUCCESS; - } - } - - return EMBER_ZCL_STATUS_NOT_FOUND; -} - -template -struct NullableUnderlyingType -{ -}; - -template -struct NullableUnderlyingType> -{ - using Type = T; -}; - -bool emberAfPluginScenesServerParseAddScene( - app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, GroupId groupId, uint8_t sceneId, - uint16_t transitionTime, const CharSpan & sceneName, - const app::DataModel::DecodableList & extensionFieldSets) -{ - CHIP_ERROR err = CHIP_NO_ERROR; - EmberAfSceneTableEntry entry; - EmberAfStatus status; - bool enhanced = (commandPath.mCommandId == EnhancedAddScene::Id); - auto fabricIndex = commandObj->GetAccessingFabricIndex(); - EndpointId endpoint = commandPath.mEndpointId; - uint8_t i, index = EMBER_AF_SCENE_TABLE_NULL_INDEX; - - emberAfScenesClusterPrintln("RX: %pAddScene 0x%2x, 0x%x, 0x%2x, \"%.*s\"", (enhanced ? "Enhanced" : ""), groupId, sceneId, - transitionTime, static_cast(sceneName.size()), sceneName.data()); - - auto fieldSetIter = extensionFieldSets.begin(); - - // Add Scene commands can only reference groups to which we belong. - if (!isEndpointInGroup(fabricIndex, endpoint, groupId)) - { - status = EMBER_ZCL_STATUS_INVALID_COMMAND; - goto kickout; - } - - for (i = 0; i < MATTER_SCENES_TABLE_SIZE; i++) - { - emberAfPluginScenesServerRetrieveSceneEntry(entry, i); - if (entry.endpoint == endpoint && entry.groupId == groupId && entry.sceneId == sceneId) - { - index = i; - break; - } - if (index == EMBER_AF_SCENE_TABLE_NULL_INDEX && entry.endpoint == EMBER_AF_SCENE_TABLE_UNUSED_ENDPOINT_ID) - { - index = i; - } - } - - // If the target index is still zero, the table is full. - if (index == EMBER_AF_SCENE_TABLE_NULL_INDEX) - { - status = EMBER_ZCL_STATUS_RESOURCE_EXHAUSTED; - goto kickout; - } - - emberAfPluginScenesServerRetrieveSceneEntry(entry, index); - - // The transition time is specified in seconds in the regular version of the - // command and tenths of a second in the enhanced version. - if (enhanced) - { - entry.transitionTime = transitionTime / 10; - entry.transitionTime100ms = (uint8_t)(transitionTime - entry.transitionTime * 10); - } - else - { - entry.transitionTime = transitionTime; - entry.transitionTime100ms = 0; - } - -#if defined(MATTER_CLUSTER_SCENE_NAME_SUPPORT) && MATTER_CLUSTER_SCENE_NAME_SUPPORT - emberAfCopyString(entry.name, Uint8::from_const_char(sceneName.data()), ZCL_SCENES_CLUSTER_MAXIMUM_NAME_LENGTH); -#endif - - // When adding a new scene, wipe out all of the extensions before parsing the - // extension field sets data. - if (i != index) - { -#ifdef ZCL_USING_ON_OFF_CLUSTER_SERVER - entry.hasOnOffValue = false; -#endif -#ifdef ZCL_USING_LEVEL_CONTROL_CLUSTER_SERVER - entry.hasCurrentLevelValue = false; -#endif -#ifdef ZCL_USING_THERMOSTAT_CLUSTER_SERVER - entry.hasOccupiedCoolingSetpointValue = false; - entry.hasOccupiedHeatingSetpointValue = false; - entry.hasSystemModeValue = false; -#endif -#ifdef ZCL_USING_COLOR_CONTROL_CLUSTER_SERVER - entry.hasCurrentXValue = false; - entry.hasCurrentYValue = false; - entry.hasEnhancedCurrentHueValue = false; - entry.hasCurrentSaturationValue = false; - entry.hasColorLoopActiveValue = false; - entry.hasColorLoopDirectionValue = false; - entry.hasColorLoopTimeValue = false; - entry.hasColorTemperatureMiredsValue = false; -#endif // ZCL_USING_COLOR_CONTROL_CLUSTER_SERVER -#ifdef ZCL_USING_DOOR_LOCK_CLUSTER_SERVER - entry.hasLockStateValue = false; -#endif -#ifdef ZCL_USING_WINDOW_COVERING_CLUSTER_SERVER - entry.hasCurrentPositionLiftPercentageValue = false; - entry.hasCurrentPositionTiltPercentageValue = false; - entry.hasTargetPositionLiftPercent100thsValue = false; - entry.hasTargetPositionTiltPercent100thsValue = false; -#endif - } - - while (fieldSetIter.Next()) - { - auto & fieldSet = fieldSetIter.GetValue(); - - ClusterId clusterId = fieldSet.clusterID; - - // TODO: We need to encode scene field sets in TLV. - // https://github.com/project-chip/connectedhomeip/issues/10334 - switch (clusterId) - { -#if 0 -#ifdef ZCL_USING_ON_OFF_CLUSTER_SERVER - case OnOff::Id: - // We only know of one extension for the On/Off cluster and it is just one - // byte, which means we can skip some logic for this cluster. If other - // extensions are added in this cluster, more logic will be needed here. - entry.hasOnOffValue = true; - entry.onOffValue = emberAfGetInt8u(extensionFieldSets, extensionFieldSetsIndex, extensionFieldSetsLen); - break; -#endif -#ifdef ZCL_USING_LEVEL_CONTROL_CLUSTER_SERVER - case LevelControl::Id: { - // We only know of one extension for the Level Control cluster and it is - // just one byte, which means we can skip some logic for this cluster. If - // other extensions are added in this cluster, more logic will be needed - // here. - entry.hasCurrentLevelValue = true; - using Traits = NumericAttributeTraits::Type>; - // TODO: This is not right; we should be getting TLV values here or something! - Traits::StorageType storedValue = emberAfGetInt8u(extensionFieldSets, extensionFieldSetsIndex, extensionFieldSetsLen); - if (Traits::IsNullValue(storedValue)) { - entry.currentLevelValue.SetNull(); - } else { - entry.currentLevelValue.SetValue(storedValue); - } - break; - } -#endif -#ifdef ZCL_USING_THERMOSTAT_CLUSTER_SERVER - case Thermostat::Id: - if (length < 2) - { - break; - } - entry.hasOccupiedCoolingSetpointValue = true; - entry.occupiedCoolingSetpointValue = - (int16_t) emberAfGetInt16u(extensionFieldSets, extensionFieldSetsIndex, extensionFieldSetsLen); - extensionFieldSetsIndex = static_cast(extensionFieldSetsIndex + 2); - length = static_cast(length - 2); - if (length < 2) - { - break; - } - entry.hasOccupiedHeatingSetpointValue = true; - entry.occupiedHeatingSetpointValue = - (int16_t) emberAfGetInt16u(extensionFieldSets, extensionFieldSetsIndex, extensionFieldSetsLen); - extensionFieldSetsIndex = static_cast(extensionFieldSetsIndex + 2); - length = static_cast(length - 2); - if (length < 1) - { - break; - } - entry.hasSystemModeValue = true; - entry.systemModeValue = emberAfGetInt8u(extensionFieldSets, extensionFieldSetsIndex, extensionFieldSetsLen); - // If additional Thermostat extensions are added, adjust the index and - // length variables here. - break; -#endif -#ifdef ZCL_USING_COLOR_CONTROL_CLUSTER_SERVER - case ColorControl::Id: - if (length < 2) - { - break; - } - entry.hasCurrentXValue = true; - entry.currentXValue = emberAfGetInt16u(extensionFieldSets, extensionFieldSetsIndex, extensionFieldSetsLen); - extensionFieldSetsIndex = static_cast(extensionFieldSetsIndex + 2); - length = static_cast(length - 2); - if (length < 2) - { - break; - } - entry.hasCurrentYValue = true; - entry.currentYValue = emberAfGetInt16u(extensionFieldSets, extensionFieldSetsIndex, extensionFieldSetsLen); - if (enhanced) - { - extensionFieldSetsIndex = static_cast(extensionFieldSetsIndex + 2); - length = static_cast(length - 2); - ; - if (length < 2) - { - break; - } - entry.hasEnhancedCurrentHueValue = true; - entry.enhancedCurrentHueValue = - emberAfGetInt16u(extensionFieldSets, extensionFieldSetsIndex, extensionFieldSetsLen); - extensionFieldSetsIndex = static_cast(extensionFieldSetsIndex + 2); - length = static_cast(length - 2); - if (length < 1) - { - break; - } - entry.hasCurrentSaturationValue = true; - entry.currentSaturationValue = emberAfGetInt8u(extensionFieldSets, extensionFieldSetsIndex, extensionFieldSetsLen); - extensionFieldSetsIndex++; - length--; - if (length < 1) - { - break; - } - entry.hasColorLoopActiveValue = true; - entry.colorLoopActiveValue = emberAfGetInt8u(extensionFieldSets, extensionFieldSetsIndex, extensionFieldSetsLen); - extensionFieldSetsIndex++; - length--; - if (length < 1) - { - break; - } - entry.hasColorLoopDirectionValue = true; - entry.colorLoopDirectionValue = emberAfGetInt8u(extensionFieldSets, extensionFieldSetsIndex, extensionFieldSetsLen); - extensionFieldSetsIndex++; - length--; - if (length < 2) - { - break; - } - entry.hasColorLoopTimeValue = true; - entry.colorLoopTimeValue = emberAfGetInt16u(extensionFieldSets, extensionFieldSetsIndex, extensionFieldSetsLen); - extensionFieldSetsIndex = static_cast(extensionFieldSetsIndex + 2); - length = static_cast(length - 2); - if (length < 2) - { - break; - } - entry.hasColorTemperatureMiredsValue = true; - entry.colorTemperatureMiredsValue = - emberAfGetInt16u(extensionFieldSets, extensionFieldSetsIndex, extensionFieldSetsLen); - } - // If additional Color Control extensions are added, adjust the index and - // length variables here. - break; -#endif // ZCL_USING_COLOR_CONTROL_CLUSTER_SERVER -#ifdef ZCL_USING_DOOR_LOCK_CLUSTER_SERVER - case DoorLock::Id: { - // We only know of one extension for the Door Lock cluster and it is just - // one byte, which means we can skip some logic for this cluster. If - // other extensions are added in this cluster, more logic will be needed - // here. - entry.hasLockStateValue = true; - using Traits = NumericAttributeTraits::Type>; - // TODO: This is not right; we should be getting TLV values here or something! - Traits::StorageType storedValue = emberAfGetInt8u(extensionFieldSets, extensionFieldSetsIndex, extensionFieldSetsLen); - if (Traits::IsNullValue(storedValue)) { - entry.lockStateValue.SetNull(); - } else { - entry.lockStateValue.SetValue(storedValue); - } - break; - } -#endif -#ifdef ZCL_USING_WINDOW_COVERING_CLUSTER_SERVER - case WindowCovering::Id: - // If we're here, we know we have at least one byte, so we can skip the - // length check for the first field. - { - entry.hasCurrentPositionLiftPercentageValue = true; - using Traits = NumericAttributeTraits::Type>; - // TODO: This is not right; we should be getting TLV values here or something! - Traits::StorageType storedValue = - emberAfGetInt8u(extensionFieldSets, extensionFieldSetsIndex, extensionFieldSetsLen); - if (Traits::IsNullValue(storedValue)){ - entry.currentPositionLiftPercentageValue.SetNull(); - } else { - entry.currentPositionLiftPercentageValue.SetValue(storedValue); - } - } - - extensionFieldSetsIndex++; - length--; - if (length < 1) - { - break; - } - - { - entry.hasCurrentPositionTiltPercentageValue = true; - using Traits = NumericAttributeTraits::Type>; - // TODO: This is not right; we should be getting TLV values here or something! - Traits::StorageType storedValue = - emberAfGetInt8u(extensionFieldSets, extensionFieldSetsIndex, extensionFieldSetsLen); - if (Traits::IsNullValue(storedValue)){ - entry.currentPositionTiltPercentageValue.SetNull(); - } else { - entry.currentPositionTiltPercentageValue.SetValue(storedValue); - } - } - - extensionFieldSetsIndex++; - length--; - if (length < 2) - { - break; - } - - { - entry.hasTargetPositionLiftPercent100thsValue = true; - using Traits = NumericAttributeTraits::Type>; - // TODO: This is not right; we should be getting TLV values here or something! - Traits::StorageType storedValue = - emberAfGetInt8u(extensionFieldSets, extensionFieldSetsIndex, extensionFieldSetsLen); - if (Traits::IsNullValue(storedValue)){ - entry.targetPositionLiftPercent100thsValue.SetNull(); - } else { - entry.targetPositionLiftPercent100thsValue.SetValue(storedValue); - } - } - - extensionFieldSetsIndex = static_cast(extensionFieldSetsIndex + 2); - length = static_cast(length - 2); - if (length < 2) - { - break; - } - - { - entry.hasTargetPositionTiltPercent100thsValue = true; - using Traits = NumericAttributeTraits::Type>; - // TODO: This is not right; we should be getting TLV values here or something! - Traits::StorageType storedValue = - emberAfGetInt8u(extensionFieldSets, extensionFieldSetsIndex, extensionFieldSetsLen); - if (Traits::IsNullValue(storedValue)){ - entry.targetPositionTiltPercent100thsValue.SetNull(); - } else { - entry.targetPositionTiltPercent100thsValue.SetValue(storedValue); - } - } - // If additional Window Covering extensions are added, adjust the index - // and length variables here. - break; -#endif -#endif // if 0 disabling all the code. - default: - break; - } - } - - if (fieldSetIter.GetStatus() != CHIP_NO_ERROR) - { - status = EMBER_ZCL_STATUS_MALFORMED_COMMAND; - goto kickout; - } - - // If we got this far, we either added a new entry or updated an existing one. - // If we added, store the basic data and increment the scene count. In either - // case, save the entry. - if (i != index) - { - entry.endpoint = endpoint; - entry.groupId = groupId; - entry.sceneId = sceneId; - emberAfPluginScenesServerIncrNumSceneEntriesInUse(); - emberAfScenesSetSceneCountAttribute(endpoint, emberAfPluginScenesServerNumSceneEntriesInUse()); - } - emberAfPluginScenesServerSaveSceneEntry(entry, index); - status = EMBER_ZCL_STATUS_SUCCESS; - -kickout: - - app::ConcreteCommandPath path = { commandPath.mEndpointId, Scenes::Id, AddSceneResponse::Id }; - if (enhanced) - { - path = { commandPath.mEndpointId, Scenes::Id, EnhancedAddSceneResponse::Id }; - } - TLV::TLVWriter * writer = nullptr; - SuccessOrExit(err = commandObj->PrepareCommand(path)); - VerifyOrExit((writer = commandObj->GetCommandDataIBTLVWriter()) != nullptr, err = CHIP_ERROR_INCORRECT_STATE); - SuccessOrExit(err = writer->Put(TLV::ContextTag(0), status)); - SuccessOrExit(err = writer->Put(TLV::ContextTag(1), groupId)); - SuccessOrExit(err = writer->Put(TLV::ContextTag(2), sceneId)); - SuccessOrExit(err = commandObj->FinishCommand()); - -exit: - if (err != CHIP_NO_ERROR) - { - ChipLogError(Zcl, "Failed to encode response command."); - } - return true; -} - -bool emberAfPluginScenesServerParseViewScene(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, - GroupId groupId, uint8_t sceneId) -{ - CHIP_ERROR err = CHIP_NO_ERROR; - EmberAfSceneTableEntry entry = {}; - EmberAfStatus status = EMBER_ZCL_STATUS_NOT_FOUND; - bool enhanced = (commandPath.mCommandId == EnhancedViewScene::Id); - FabricIndex fabricIndex = commandObj->GetAccessingFabricIndex(); - EndpointId endpoint = commandPath.mEndpointId; - - emberAfScenesClusterPrintln("RX: %pViewScene 0x%2x, 0x%x", (enhanced ? "Enhanced" : ""), groupId, sceneId); - - // View Scene commands can only reference groups which we belong to. - if (!isEndpointInGroup(fabricIndex, endpoint, groupId)) - { - status = EMBER_ZCL_STATUS_INVALID_COMMAND; - } - else - { - uint8_t i; - for (i = 0; i < MATTER_SCENES_TABLE_SIZE; i++) - { - emberAfPluginScenesServerRetrieveSceneEntry(entry, i); - if (entry.endpoint == endpoint && entry.groupId == groupId && entry.sceneId == sceneId) - { - status = EMBER_ZCL_STATUS_SUCCESS; - break; - } - } - } - - // The status, group id, and scene id are always included in the response, but - // the transition time, name, and extension fields are only included if the - // scene was found. - app::ConcreteCommandPath path = { commandPath.mEndpointId, Scenes::Id, ViewSceneResponse::Id }; - if (enhanced) - { - path = { commandPath.mEndpointId, Scenes::Id, EnhancedViewSceneResponse::Id }; - } - TLV::TLVWriter * writer = nullptr; - SuccessOrExit(err = commandObj->PrepareCommand(path)); - VerifyOrExit((writer = commandObj->GetCommandDataIBTLVWriter()) != nullptr, err = CHIP_ERROR_INCORRECT_STATE); - SuccessOrExit(err = writer->Put(TLV::ContextTag(0), status)); - SuccessOrExit(err = writer->Put(TLV::ContextTag(1), groupId)); - SuccessOrExit(err = writer->Put(TLV::ContextTag(2), sceneId)); - SuccessOrExit(err = writer->Put(TLV::ContextTag(3), - static_cast(enhanced ? entry.transitionTime * 10 + entry.transitionTime100ms - : entry.transitionTime))); -#if defined(MATTER_CLUSTER_SCENE_NAME_SUPPORT) && MATTER_CLUSTER_SCENE_NAME_SUPPORT - SuccessOrExit(err = writer->Put(TLV::ContextTag(4), entry.name)); -#else - SuccessOrExit(err = writer->PutString(TLV::ContextTag(4), "")); -#endif - // #6620: Need the build the array for response. - SuccessOrExit(err = writer->Put(TLV::ContextTag(5), ByteSpan(nullptr, 0))); - SuccessOrExit(err = commandObj->FinishCommand()); - - /* - if (status == EMBER_ZCL_STATUS_SUCCESS) - { - // The transition time is returned in seconds in the regular version of the - // command and tenths of a second in the enhanced version. - emberAfPutInt16uInResp( - static_cast(enhanced ? entry.transitionTime * 10 + entry.transitionTime100ms : entry.transitionTime)); - #if defined(MATTER_CLUSTER_SCENE_NAME_SUPPORT) && MATTER_CLUSTER_SCENE_NAME_SUPPORT - emberAfPutStringInResp(entry.name); - #else - emberAfPutInt8uInResp(0); // name length - #endif - #ifdef ZCL_USING_ON_OFF_CLUSTER_SERVER - if (entry.hasOnOffValue) - { - emberAfPutInt16uInResp(OnOff::Id); - emberAfPutInt8uInResp(1); // length - emberAfPutInt8uInResp(entry.onOffValue); - } - #endif - #ifdef ZCL_USING_LEVEL_CONTROL_CLUSTER_SERVER - if (entry.hasCurrentLevelValue) - { - emberAfPutInt16uInResp(LevelControl::Id); - emberAfPutInt8uInResp(1); // length - emberAfPutInt8uInResp(entry.currentLevelValue); - } - #endif - #ifdef ZCL_USING_THERMOSTAT_CLUSTER_SERVER - if (entry.hasOccupiedCoolingSetpointValue) - { - uint8_t * length; - emberAfPutInt16uInResp(Thermostat::Id); - length = &appResponseData[appResponseLength]; - emberAfPutInt8uInResp(0); // temporary length - emberAfPutInt16sInResp(entry.occupiedCoolingSetpointValue); - *length += 2; - if (entry.hasOccupiedHeatingSetpointValue) - { - emberAfPutInt16sInResp(entry.occupiedHeatingSetpointValue); - *length += 2; - if (entry.hasSystemModeValue) - { - emberAfPutInt8uInResp(entry.systemModeValue); - (*length)++; - } - } - } - #endif - #ifdef ZCL_USING_COLOR_CONTROL_CLUSTER_SERVER - if (entry.hasCurrentXValue) - { - uint8_t * length; - emberAfPutInt16uInResp(ColorControl::Id); - length = &appResponseData[appResponseLength]; - emberAfPutInt8uInResp(0); // temporary length - emberAfPutInt16uInResp(entry.currentXValue); - *length = static_cast(*length + 2); - if (entry.hasCurrentYValue) - { - emberAfPutInt16uInResp(entry.currentYValue); - *length = static_cast(*length + 2); - if (enhanced) - { - if (entry.hasEnhancedCurrentHueValue) - { - emberAfPutInt16uInResp(entry.enhancedCurrentHueValue); - *length = static_cast(*length + 2); - if (entry.hasCurrentSaturationValue) - { - emberAfPutInt8uInResp(entry.currentSaturationValue); - (*length)++; - if (entry.hasColorLoopActiveValue) - { - emberAfPutInt8uInResp(entry.colorLoopActiveValue); - (*length)++; - if (entry.hasColorLoopDirectionValue) - { - emberAfPutInt8uInResp(entry.colorLoopDirectionValue); - (*length)++; - if (entry.hasColorLoopTimeValue) - { - emberAfPutInt16uInResp(entry.colorLoopTimeValue); - *length = static_cast(*length + 2); - if (entry.hasColorTemperatureMiredsValue) - { - emberAfPutInt16uInResp(entry.colorTemperatureMiredsValue); - *length = static_cast(*length + 2); - } - } - } - } - } - } - } - } - } - #endif // ZCL_USING_COLOR_CONTROL_CLUSTER_SERVER - #ifdef ZCL_USING_DOOR_LOCK_CLUSTER_SERVER - if (entry.hasLockStateValue) - { - emberAfPutInt16uInResp(DoorLock::Id); - emberAfPutInt8uInResp(1); // length - emberAfPutInt8uInResp(entry.lockStateValue); - } - #endif - #ifdef ZCL_USING_WINDOW_COVERING_CLUSTER_SERVER - if (entry.hasCurrentPositionLiftPercentageValue) - { - uint8_t * length; - emberAfPutInt16uInResp(WindowCovering::Id); - length = &appResponseData[appResponseLength]; - emberAfPutInt8uInResp(0); // temporary length - emberAfPutInt8uInResp(entry.currentPositionLiftPercentageValue); - (*length)++; - if (entry.hasCurrentPositionTiltPercentageValue) - { - emberAfPutInt8uInResp(entry.currentPositionTiltPercentageValue); - (*length)++; - if (entry.hasTargetPositionLiftPercent100thsValue) - { - emberAfPutInt16uInResp(entry.targetPositionLiftPercent100thsValue); - *length = static_cast(*length + 2); - if (entry.hasTargetPositionTiltPercent100thsValue) - { - emberAfPutInt16uInResp(entry.targetPositionTiltPercent100thsValue); - *length = static_cast(*length + 2); - } - } - } - } - #endif - } - - sendStatus = emberAfSendResponse(); - if (EMBER_SUCCESS != sendStatus) - { - emberAfScenesClusterPrintln("Scenes: failed to send %s response: 0x%x", "view_scene", sendStatus); - } - */ - -exit: - if (err != CHIP_NO_ERROR) - { - ChipLogError(Zcl, "Failed to encode response command."); - } - return true; -} - -void emberAfScenesClusterRemoveScenesInGroupCallback(EndpointId endpoint, GroupId groupId) -{ - uint8_t i; - for (i = 0; i < MATTER_SCENES_TABLE_SIZE; i++) - { - EmberAfSceneTableEntry entry; - emberAfPluginScenesServerRetrieveSceneEntry(entry, i); - if (entry.endpoint == endpoint && entry.groupId == groupId) - { - entry.groupId = ZCL_SCENES_GLOBAL_SCENE_GROUP_ID; - entry.endpoint = EMBER_AF_SCENE_TABLE_UNUSED_ENDPOINT_ID; - emberAfPluginScenesServerSaveSceneEntry(entry, i); - emberAfPluginScenesServerDecrNumSceneEntriesInUse(); - emberAfScenesSetSceneCountAttribute(endpoint, emberAfPluginScenesServerNumSceneEntriesInUse()); - } - } -} - -void MatterScenesPluginServerInitCallback() {} diff --git a/src/app/clusters/scenes/scenes.h b/src/app/clusters/scenes/scenes.h deleted file mode 100644 index 524c63eae0ae4a..00000000000000 --- a/src/app/clusters/scenes/scenes.h +++ /dev/null @@ -1,182 +0,0 @@ -/** - * - * Copyright (c) 2020 Project CHIP Authors - * - * 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. - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -/** - * @brief A structure used to store scene table entries in RAM or in storage, - * depending on a plugin setting. If endpoint field is - * ::EMBER_AF_SCENE_TABLE_UNUSED_ENDPOINT_ID, the entry is unused. - */ -typedef struct -{ - chip::EndpointId endpoint; // 0x00 when this record is not in use - chip::GroupId groupId; // 0x0000 if not associated with a group - uint8_t sceneId; -#if defined(MATTER_CLUSTER_SCENE_NAME_SUPPORT) && MATTER_CLUSTER_SCENE_NAME_SUPPORT - uint8_t name[ZCL_SCENES_CLUSTER_MAXIMUM_NAME_LENGTH + 1]; -#endif - uint16_t transitionTime; // in seconds - uint8_t transitionTime100ms; // in tenths of a seconds -#ifdef ZCL_USING_ON_OFF_CLUSTER_SERVER - bool hasOnOffValue; - bool onOffValue; -#endif -#ifdef ZCL_USING_LEVEL_CONTROL_CLUSTER_SERVER - bool hasCurrentLevelValue; - chip::app::DataModel::Nullable currentLevelValue; -#endif -#ifdef ZCL_USING_THERMOSTAT_CLUSTER_SERVER - bool hasOccupiedCoolingSetpointValue; - int16_t occupiedCoolingSetpointValue; - bool hasOccupiedHeatingSetpointValue; - int16_t occupiedHeatingSetpointValue; - bool hasSystemModeValue; - uint8_t systemModeValue; -#endif -#ifdef ZCL_USING_COLOR_CONTROL_CLUSTER_SERVER - bool hasCurrentXValue; - uint16_t currentXValue; - bool hasCurrentYValue; - uint16_t currentYValue; - bool hasEnhancedCurrentHueValue; - uint16_t enhancedCurrentHueValue; - bool hasCurrentSaturationValue; - uint8_t currentSaturationValue; - bool hasColorLoopActiveValue; - uint8_t colorLoopActiveValue; - bool hasColorLoopDirectionValue; - uint8_t colorLoopDirectionValue; - bool hasColorLoopTimeValue; - uint16_t colorLoopTimeValue; - bool hasColorTemperatureMiredsValue; - uint16_t colorTemperatureMiredsValue; -#endif // ZCL_USING_COLOR_CONTROL_CLUSTER_SERVER -#ifdef ZCL_USING_DOOR_LOCK_CLUSTER_SERVER - bool hasLockStateValue; - chip::app::DataModel::Nullable lockStateValue; -#endif -#ifdef ZCL_USING_WINDOW_COVERING_CLUSTER_SERVER - bool hasCurrentPositionLiftPercentageValue; - chip::app::DataModel::Nullable currentPositionLiftPercentageValue; - bool hasCurrentPositionTiltPercentageValue; - chip::app::DataModel::Nullable currentPositionTiltPercentageValue; - bool hasTargetPositionLiftPercent100thsValue; - chip::app::DataModel::Nullable targetPositionLiftPercent100thsValue; - bool hasTargetPositionTiltPercent100thsValue; - chip::app::DataModel::Nullable targetPositionTiltPercent100thsValue; -#endif -} EmberAfSceneTableEntry; - -EmberAfStatus emberAfScenesSetSceneCountAttribute(chip::EndpointId endpoint, uint8_t newCount); -EmberAfStatus emberAfScenesMakeValid(chip::EndpointId endpoint, uint8_t sceneId, chip::GroupId groupId); - -// DEPRECATED. -#define emberAfScenesMakeInvalid emberAfScenesClusterMakeInvalidCallback - -void emAfPluginScenesServerPrintInfo(void); - -extern uint8_t emberAfPluginScenesServerEntriesInUse; -#if defined(EMBER_AF_PLUGIN_SCENES_USE_TOKENS) && !defined(EZSP_HOST) -// TODO: Make the storage bits actually work -#define emberAfPluginScenesServerRetrieveSceneEntry(entry, i) -#define emberAfPluginScenesServerSaveSceneEntry(entry, i) -#define emberAfPluginScenesServerNumSceneEntriesInUse() \ - (halCommonGetToken(&emberAfPluginScenesServerEntriesInUse, TOKEN_SCENES_NUM_ENTRIES), emberAfPluginScenesServerEntriesInUse) -#define emberAfPluginScenesServerSetNumSceneEntriesInUse(x) \ - (emberAfPluginScenesServerEntriesInUse = (x), \ - halCommonSetToken(TOKEN_SCENES_NUM_ENTRIES, &emberAfPluginScenesServerEntriesInUse)) -#define emberAfPluginScenesServerIncrNumSceneEntriesInUse() \ - ((halCommonGetToken(&emberAfPluginScenesServerEntriesInUse, TOKEN_SCENES_NUM_ENTRIES), \ - ++emberAfPluginScenesServerEntriesInUse), \ - halCommonSetToken(TOKEN_SCENES_NUM_ENTRIES, &emberAfPluginScenesServerEntriesInUse)) -#define emberAfPluginScenesServerDecrNumSceneEntriesInUse() \ - ((halCommonGetToken(&emberAfPluginScenesServerEntriesInUse, TOKEN_SCENES_NUM_ENTRIES), \ - --emberAfPluginScenesServerEntriesInUse), \ - halCommonSetToken(TOKEN_SCENES_NUM_ENTRIES, &emberAfPluginScenesServerEntriesInUse)) -#else -// Use normal RAM storage -extern EmberAfSceneTableEntry emberAfPluginScenesServerSceneTable[]; -#define emberAfPluginScenesServerRetrieveSceneEntry(entry, i) (entry = emberAfPluginScenesServerSceneTable[i]) -#define emberAfPluginScenesServerSaveSceneEntry(entry, i) (emberAfPluginScenesServerSceneTable[i] = entry) -#define emberAfPluginScenesServerNumSceneEntriesInUse() (emberAfPluginScenesServerEntriesInUse) -#define emberAfPluginScenesServerSetNumSceneEntriesInUse(x) (emberAfPluginScenesServerEntriesInUse = (x)) -#define emberAfPluginScenesServerIncrNumSceneEntriesInUse() (++emberAfPluginScenesServerEntriesInUse) -#define emberAfPluginScenesServerDecrNumSceneEntriesInUse() (--emberAfPluginScenesServerEntriesInUse) -#endif // Use tokens - -bool emberAfPluginScenesServerParseAddScene( - chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, chip::GroupId groupId, - uint8_t sceneId, uint16_t transitionTime, const chip::CharSpan & sceneName, - const chip::app::DataModel::DecodableList & - extensionFieldSets); -bool emberAfPluginScenesServerParseViewScene(chip::app::CommandHandler * commandObj, - const chip::app::ConcreteCommandPath & commandPath, chip::GroupId groupId, - uint8_t sceneId); - -/** @brief Scenes Cluster Recall Saved Scene - * - * This function is called by the framework when the application should recall a - * saved scene. - * - * @param endpoint The endpoint. Ver.: always - * @param groupId The group identifier. Ver.: always - * @param sceneId The scene identifier. Ver.: always - */ -EmberAfStatus emberAfScenesClusterRecallSavedSceneCallback(chip::FabricIndex fabricIndex, chip::EndpointId endpoint, - chip::GroupId groupId, uint8_t sceneId); - -/** @brief Scenes Cluster Store Current Scene - * - * This function is called by the framework when the application should store - * the current scene. If an entry already exists in the scene table with the - * same scene and group ids, the application should update the entry with the - * current scene. Otherwise, a new entry should be adde to the scene table, if - * possible. - * - * @param endpoint The endpoint. Ver.: always - * @param groupId The group identifier. Ver.: always - * @param sceneId The scene identifier. Ver.: always - */ -EmberAfStatus emberAfScenesClusterStoreCurrentSceneCallback(chip::FabricIndex fabricIndex, chip::EndpointId endpoint, - chip::GroupId groupId, uint8_t sceneId); - -/** @brief Scenes Cluster Remove Scenes In Group - * - * This function removes the scenes from a specified group. - * - * @param endpoint Endpoint Ver.: always - * @param groupId Group ID Ver.: always - */ -void emberAfScenesClusterRemoveScenesInGroupCallback(chip::EndpointId endpoint, chip::GroupId groupId); - -/** @brief Scenes Cluster Make Invalid - * - * This function is called to invalidate the valid attribute in the Scenes - * cluster. - * - * @param endpoint Ver.: always - */ -EmberAfStatus emberAfScenesClusterMakeInvalidCallback(chip::EndpointId endpoint); diff --git a/src/app/clusters/window-covering-server/window-covering-server.cpp b/src/app/clusters/window-covering-server/window-covering-server.cpp index d01f2fac668746..32515eeff0e8b5 100644 --- a/src/app/clusters/window-covering-server/window-covering-server.cpp +++ b/src/app/clusters/window-covering-server/window-covering-server.cpp @@ -31,7 +31,7 @@ #include #ifdef EMBER_AF_PLUGIN_SCENES -#include +#include #endif // EMBER_AF_PLUGIN_SCENES using namespace chip; diff --git a/src/app/common/templates/config-data.yaml b/src/app/common/templates/config-data.yaml index d07c3e35246bfd..dada0c43b3deca 100644 --- a/src/app/common/templates/config-data.yaml +++ b/src/app/common/templates/config-data.yaml @@ -23,6 +23,7 @@ CommandHandlerInterfaceOnlyClusters: # CommandHandlerInterface and hence do not need generated command dispatch. # This uses asUpperCamelCase versions of the cluster name. - NetworkCommissioning + - Scenes # We need a more configurable way of deciding which clusters have which init functions.... # See https://github.com/project-chip/connectedhomeip/issues/4369 @@ -36,7 +37,6 @@ ClustersWithInitFunctions: - Occupancy Sensing - On/Off - Pump Configuration and Control - - Scenes - Time Format Localization - Thermostat - Mode Select diff --git a/src/app/tests/BUILD.gn b/src/app/tests/BUILD.gn index 49657c9d3798c8..1f7ad407339ca8 100644 --- a/src/app/tests/BUILD.gn +++ b/src/app/tests/BUILD.gn @@ -81,13 +81,12 @@ source_set("ota-requestor-test-srcs") { source_set("scenes-table-test-srcs") { sources = [ - "${chip_root}/src/app/clusters/scenes/ExtensionFieldSets.h", - "${chip_root}/src/app/clusters/scenes/ExtensionFieldSetsImpl.cpp", - "${chip_root}/src/app/clusters/scenes/ExtensionFieldSetsImpl.h", - "${chip_root}/src/app/clusters/scenes/SceneTable.h", - "${chip_root}/src/app/clusters/scenes/SceneTableImpl.cpp", - "${chip_root}/src/app/clusters/scenes/SceneTableImpl.h", - "${chip_root}/src/app/clusters/scenes/scenes-tokens.h", + "${chip_root}/src/app/clusters/scenes-server/ExtensionFieldSets.h", + "${chip_root}/src/app/clusters/scenes-server/ExtensionFieldSetsImpl.cpp", + "${chip_root}/src/app/clusters/scenes-server/ExtensionFieldSetsImpl.h", + "${chip_root}/src/app/clusters/scenes-server/SceneTable.h", + "${chip_root}/src/app/clusters/scenes-server/SceneTableImpl.cpp", + "${chip_root}/src/app/clusters/scenes-server/SceneTableImpl.h", ] public_deps = [ diff --git a/src/app/tests/TestExtensionFieldSets.cpp b/src/app/tests/TestExtensionFieldSets.cpp index 3f9400fe109e03..d26df5c84b9e7f 100644 --- a/src/app/tests/TestExtensionFieldSets.cpp +++ b/src/app/tests/TestExtensionFieldSets.cpp @@ -16,7 +16,7 @@ * limitations under the License. */ -#include +#include #include #include #include diff --git a/src/app/tests/TestSceneTable.cpp b/src/app/tests/TestSceneTable.cpp index 82f11edef04fa6..25808686828d16 100644 --- a/src/app/tests/TestSceneTable.cpp +++ b/src/app/tests/TestSceneTable.cpp @@ -16,7 +16,8 @@ * limitations under the License. */ -#include +#include +#include #include #include #include @@ -42,9 +43,9 @@ constexpr chip::ClusterId kLevelControlClusterId = 0x0008; constexpr chip::ClusterId kColorControlClusterId = 0x0300; // Test Endpoint ID -constexpr chip::EndpointId kTestEndpoint1 = 0x0001; -constexpr chip::EndpointId kTestEndpoint2 = 0x0099; -constexpr chip::EndpointId kTestEndpoint3 = 0x0010; +constexpr chip::EndpointId kTestEndpoint1 = chip::Test::kMockEndpoint1; +constexpr chip::EndpointId kTestEndpoint2 = chip::Test::kMockEndpoint2; +constexpr chip::EndpointId kTestEndpoint3 = chip::Test::kMockEndpoint3; // Test Attribute ID constexpr uint32_t kOnOffAttId = 0x0000; @@ -69,7 +70,11 @@ constexpr chip::GroupId kGroup4 = 0x00; constexpr chip::SceneId kScene1 = 0xAA; constexpr chip::SceneId kScene2 = 0x45; constexpr chip::SceneId kScene3 = 0x77; -constexpr chip::SceneId kScene4 = 0xEE; +constexpr chip::SceneId kScene4 = 0xED; +constexpr chip::SceneId kScene5 = 0xDE; +constexpr chip::SceneId kScene6 = 0xAB; +constexpr chip::SceneId kScene7 = 0xBB; +constexpr chip::SceneId kScene8 = 0x22; // Test fabrics, adding more requires to modify the "ResetSceneTable" function constexpr chip::FabricIndex kFabric1 = 1; @@ -77,15 +82,15 @@ constexpr chip::FabricIndex kFabric2 = 7; constexpr chip::FabricIndex kFabric3 = 77; // Scene storage ID -static const SceneStorageId sceneId1(kTestEndpoint1, kScene1, kGroup1); -static const SceneStorageId sceneId2(kTestEndpoint1, kScene4, kGroup1); -static const SceneStorageId sceneId3(kTestEndpoint2, kScene2, kGroup1); -static const SceneStorageId sceneId4(kTestEndpoint2, kScene4, kGroup1); -static const SceneStorageId sceneId5(kTestEndpoint1, kScene3, kGroup2); -static const SceneStorageId sceneId6(kTestEndpoint1, kScene4, kGroup2); -static const SceneStorageId sceneId7(kTestEndpoint1, kScene1, kGroup3); -static const SceneStorageId sceneId8(kTestEndpoint3, kScene1, kGroup4); -static const SceneStorageId sceneId9(kTestEndpoint2, kScene1, kGroup4); +static const SceneStorageId sceneId1(kScene1, kGroup1); +static const SceneStorageId sceneId2(kScene2, kGroup1); +static const SceneStorageId sceneId3(kScene3, kGroup1); +static const SceneStorageId sceneId4(kScene4, kGroup1); +static const SceneStorageId sceneId5(kScene5, kGroup2); +static const SceneStorageId sceneId6(kScene6, kGroup2); +static const SceneStorageId sceneId7(kScene7, kGroup3); +static const SceneStorageId sceneId8(kScene8, kGroup4); +static const SceneStorageId sceneId9(kScene1, kGroup4); CharSpan empty; @@ -154,7 +159,7 @@ class TestSceneHandler : public scenes::DefaultSceneHandlerImpl clusterBuffer.reduce_size(2); } } - else if (endpoint == kTestEndpoint2) + else if (endpoint == kTestEndpoint1) { if (clusterBuffer.size() >= 2) { @@ -163,7 +168,7 @@ class TestSceneHandler : public scenes::DefaultSceneHandlerImpl clusterBuffer.reduce_size(2); } } - else if (endpoint == kTestEndpoint3) + else if (endpoint == kTestEndpoint1) { if (clusterBuffer.size() >= 3) { @@ -186,7 +191,7 @@ class TestSceneHandler : public scenes::DefaultSceneHandlerImpl } } - if (endpoint == kTestEndpoint2) + if (endpoint == kTestEndpoint1) { if (cluster == kOnOffClusterId || cluster == kColorControlClusterId) { @@ -194,7 +199,7 @@ class TestSceneHandler : public scenes::DefaultSceneHandlerImpl } } - if (endpoint == kTestEndpoint3) + if (endpoint == kTestEndpoint1) { if (cluster == kOnOffClusterId || cluster == kLevelControlClusterId || cluster == kColorControlClusterId) { @@ -236,7 +241,7 @@ class TestSceneHandler : public scenes::DefaultSceneHandlerImpl break; } } - if (endpoint == kTestEndpoint2) + if (endpoint == kTestEndpoint1) { switch (cluster) { @@ -258,7 +263,7 @@ class TestSceneHandler : public scenes::DefaultSceneHandlerImpl break; } } - if (endpoint == kTestEndpoint3) + if (endpoint == kTestEndpoint1) { switch (cluster) { @@ -325,7 +330,7 @@ class TestSceneHandler : public scenes::DefaultSceneHandlerImpl } // Takes values from cluster in Endpoint 2 - if (endpoint == kTestEndpoint2) + if (endpoint == kTestEndpoint1) { switch (cluster) { @@ -347,7 +352,7 @@ class TestSceneHandler : public scenes::DefaultSceneHandlerImpl } // Takes values from cluster in Endpoint 3 - if (endpoint == kTestEndpoint3) + if (endpoint == kTestEndpoint1) { switch (cluster) { @@ -381,14 +386,14 @@ class TestSceneHandler : public scenes::DefaultSceneHandlerImpl class TestSceneTableImpl : public SceneTableImpl { public: - TestSceneTableImpl(uint8_t maxScenesPerFabric = scenes::kMaxScenesPerFabric, - uint8_t maxScenesGlobal = scenes::kMaxScenesGlobal) : + TestSceneTableImpl(uint16_t maxScenesPerFabric = scenes::kMaxScenesPerFabric, + uint16_t maxScenesGlobal = scenes::kMaxScenesPerEndpoint) : SceneTableImpl(maxScenesPerFabric, maxScenesGlobal) {} ~TestSceneTableImpl() override {} protected: - uint8_t GetClustersFromEndpoint(EndpointId endpoint, ClusterId * clusterList, uint8_t listLen) override + uint8_t GetClustersFromEndpoint(ClusterId * clusterList, uint8_t listLen) override { if (listLen >= 3) { @@ -405,7 +410,6 @@ class TestSceneTableImpl : public SceneTableImpl // Storage static chip::TestPersistentStorageDelegate testStorage; // Scene -static TestSceneTableImpl sSceneTable; static TestSceneHandler sHandler; void ResetSceneTable(SceneTable * sceneTable) @@ -417,7 +421,9 @@ void ResetSceneTable(SceneTable * sceneTable) void TestHandlerRegistration(nlTestSuite * aSuite, void * aContext) { - SceneTable * sceneTable = &sSceneTable; + SceneTable * sceneTable = scenes::GetSceneTableImpl(); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); TestSceneHandler tmpHandler[scenes::kMaxClustersPerScene]; for (uint8_t i = 0; i < scenes::kMaxClustersPerScene; i++) @@ -456,7 +462,9 @@ void TestHandlerRegistration(nlTestSuite * aSuite, void * aContext) void TestHandlerFunctions(nlTestSuite * aSuite, void * aContext) { - SceneTable * sceneTable = &sSceneTable; + SceneTable * sceneTable = scenes::GetSceneTableImpl(kTestEndpoint1, scenes::kMaxScenesPerEndpoint); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); app::Clusters::Scenes::Structs::ExtensionFieldSet::Type extensionFieldSetOut; app::Clusters::Scenes::Structs::ExtensionFieldSet::DecodableType extensionFieldSetIn; @@ -589,8 +597,8 @@ void TestHandlerFunctions(nlTestSuite * aSuite, void * aContext) NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == extensionFieldSetIn.attributeValueList.Decode(reader)); NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == reader.ExitContainer(outerRead)); - NL_TEST_ASSERT(aSuite, sHandler.SupportsCluster(kTestEndpoint2, extensionFieldSetIn.clusterID)); - NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sHandler.SerializeAdd(kTestEndpoint2, extensionFieldSetIn, buff_span)); + NL_TEST_ASSERT(aSuite, sHandler.SupportsCluster(kTestEndpoint1, extensionFieldSetIn.clusterID)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sHandler.SerializeAdd(kTestEndpoint1, extensionFieldSetIn, buff_span)); // Verify the handler extracted buffer matches the initial field sets NL_TEST_ASSERT(aSuite, 0 == memcmp(CC_list.data(), buff_span.data(), buff_span.size())); @@ -632,9 +640,9 @@ void TestHandlerFunctions(nlTestSuite * aSuite, void * aContext) memset(buffer, 0, buff_span.size()); // Verify Deserializing is properly filling out output extension field set for color control - NL_TEST_ASSERT(aSuite, sHandler.SupportsCluster(kTestEndpoint2, kColorControlClusterId)); + NL_TEST_ASSERT(aSuite, sHandler.SupportsCluster(kTestEndpoint1, kColorControlClusterId)); NL_TEST_ASSERT(aSuite, - CHIP_NO_ERROR == sHandler.Deserialize(kTestEndpoint2, kColorControlClusterId, CC_list, extensionFieldSetOut)); + CHIP_NO_ERROR == sHandler.Deserialize(kTestEndpoint1, kColorControlClusterId, CC_list, extensionFieldSetOut)); // Verify Encoding the Extension field set returns the same data as the one serialized for color control previously writer.Init(buff_span); @@ -703,13 +711,16 @@ void TestHandlerFunctions(nlTestSuite * aSuite, void * aContext) void TestStoreScenes(nlTestSuite * aSuite, void * aContext) { - SceneTable * sceneTable = &sSceneTable; + SceneTable * sceneTable = scenes::GetSceneTableImpl(kTestEndpoint1); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); SceneId sceneList[scenes::kMaxScenesPerFabric]; - NL_TEST_ASSERT(aSuite, sceneTable); - // Reset test ResetSceneTable(sceneTable); + sceneTable = scenes::GetSceneTableImpl(kTestEndpoint1); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); // Populate scene1's EFS (Endpoint1) NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SceneSaveEFS(scene1)); @@ -799,29 +810,37 @@ void TestStoreScenes(nlTestSuite * aSuite, void * aContext) NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetAllSceneIdsInGroup(kFabric1, kGroup1, sceneListSpan)); NL_TEST_ASSERT(aSuite, 4 == sceneListSpan.size()); NL_TEST_ASSERT(aSuite, kScene1 == sceneList[0]); - NL_TEST_ASSERT(aSuite, kScene4 == sceneList[1]); - NL_TEST_ASSERT(aSuite, kScene2 == sceneList[2]); + NL_TEST_ASSERT(aSuite, kScene2 == sceneList[1]); + NL_TEST_ASSERT(aSuite, kScene3 == sceneList[2]); NL_TEST_ASSERT(aSuite, kScene4 == sceneList[3]); NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetAllSceneIdsInGroup(kFabric1, kGroup2, sceneListSpan)); NL_TEST_ASSERT(aSuite, 2 == sceneListSpan.size()); - NL_TEST_ASSERT(aSuite, kScene3 == sceneList[0]); - NL_TEST_ASSERT(aSuite, kScene4 == sceneList[1]); + NL_TEST_ASSERT(aSuite, kScene5 == sceneList[0]); + NL_TEST_ASSERT(aSuite, kScene6 == sceneList[1]); NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetAllSceneIdsInGroup(kFabric1, kGroup3, sceneListSpan)); NL_TEST_ASSERT(aSuite, 1 == sceneListSpan.size()); - NL_TEST_ASSERT(aSuite, kScene1 == sceneList[0]); + NL_TEST_ASSERT(aSuite, kScene7 == sceneList[0]); NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetAllSceneIdsInGroup(kFabric1, kGroup4, sceneListSpan)); NL_TEST_ASSERT(aSuite, 1 == sceneListSpan.size()); - NL_TEST_ASSERT(aSuite, kScene1 == sceneList[0]); + NL_TEST_ASSERT(aSuite, kScene8 == sceneList[0]); + + uint8_t sceneCount = 0; + sceneTable->GetEndpointSceneCount(sceneCount); + sceneTable->GetFabricSceneCount(kFabric1, sceneCount); } void TestOverwriteScenes(nlTestSuite * aSuite, void * aContext) { - SceneTable * sceneTable = &sSceneTable; - NL_TEST_ASSERT(aSuite, sceneTable); + SceneTable * sceneTable = scenes::GetSceneTableImpl(kTestEndpoint1, scenes::kMaxScenesPerEndpoint); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); + uint8_t sceneCount = 0; + sceneTable->GetEndpointSceneCount(sceneCount); + sceneTable->GetFabricSceneCount(kFabric1, sceneCount); SceneTableEntry scene; // Overwriting the first entry NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric1, scene10)); @@ -843,8 +862,9 @@ void TestOverwriteScenes(nlTestSuite * aSuite, void * aContext) void TestIterateScenes(nlTestSuite * aSuite, void * aContext) { - SceneTable * sceneTable = &sSceneTable; - NL_TEST_ASSERT(aSuite, sceneTable); + SceneTable * sceneTable = scenes::GetSceneTableImpl(kTestEndpoint1, scenes::kMaxScenesPerEndpoint); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); SceneTableEntry scene; auto * iterator = sceneTable->IterateSceneEntries(kFabric1); @@ -879,8 +899,9 @@ void TestIterateScenes(nlTestSuite * aSuite, void * aContext) void TestRemoveScenes(nlTestSuite * aSuite, void * aContext) { - SceneTable * sceneTable = &sSceneTable; - NL_TEST_ASSERT(aSuite, sceneTable); + SceneTable * sceneTable = scenes::GetSceneTableImpl(kTestEndpoint1, scenes::kMaxScenesPerEndpoint); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); SceneTableEntry scene; @@ -912,7 +933,7 @@ void TestRemoveScenes(nlTestSuite * aSuite, void * aContext) iterator->Release(); // Remove first - NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->RemoveSceneTableEntryAtPosition(kFabric1, 0)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->RemoveSceneTableEntryAtPosition(kTestEndpoint1, kFabric1, 0)); iterator = sceneTable->IterateSceneEntries(kFabric1); NL_TEST_ASSERT(aSuite, iterator->Count() == 6); NL_TEST_ASSERT(aSuite, iterator->Next(scene)); @@ -965,8 +986,10 @@ void TestRemoveScenes(nlTestSuite * aSuite, void * aContext) iterator->Release(); // Remove at empty position, shouldn't trigger error - NL_TEST_ASSERT(aSuite, - CHIP_NO_ERROR == sceneTable->RemoveSceneTableEntryAtPosition(kFabric1, chip::scenes::kMaxScenesPerFabric - 1)); + NL_TEST_ASSERT( + aSuite, + CHIP_NO_ERROR == + sceneTable->RemoveSceneTableEntryAtPosition(kTestEndpoint1, kFabric1, chip::scenes::kMaxScenesPerFabric - 1)); iterator = sceneTable->IterateSceneEntries(kFabric1); NL_TEST_ASSERT(aSuite, iterator->Count() == 0); @@ -1000,11 +1023,15 @@ void TestRemoveScenes(nlTestSuite * aSuite, void * aContext) void TestFabricScenes(nlTestSuite * aSuite, void * aContext) { - SceneTable * sceneTable = &sSceneTable; - NL_TEST_ASSERT(aSuite, sceneTable); + SceneTable * sceneTable = scenes::GetSceneTableImpl(); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); // Reset test ResetSceneTable(sceneTable); + sceneTable = scenes::GetSceneTableImpl(kTestEndpoint1); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); SceneTableEntry scene; uint8_t fabric_capacity = 0; @@ -1029,6 +1056,10 @@ void TestFabricScenes(nlTestSuite * aSuite, void * aContext) NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetRemainingCapacity(kFabric1, fabric_capacity)); NL_TEST_ASSERT(aSuite, 0 == fabric_capacity); + uint8_t scene_count = 0; + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetFabricSceneCount(kFabric1, scene_count)); + NL_TEST_ASSERT(aSuite, scenes::kMaxScenesPerFabric == scene_count); + // Fabric 2 inserts NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetRemainingCapacity(kFabric2, fabric_capacity)); NL_TEST_ASSERT(aSuite, scenes::kMaxScenesPerFabric == fabric_capacity); @@ -1038,6 +1069,10 @@ void TestFabricScenes(nlTestSuite * aSuite, void * aContext) NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric2, scene4)); NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetRemainingCapacity(kFabric2, fabric_capacity)); NL_TEST_ASSERT(aSuite, (scenes::kMaxScenesPerFabric - 4) == fabric_capacity); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetFabricSceneCount(kFabric2, scene_count)); + NL_TEST_ASSERT(aSuite, 4 == scene_count); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetEndpointSceneCount(scene_count)); + NL_TEST_ASSERT(aSuite, scenes::kMaxScenesPerEndpoint - 4 == scene_count); // Fabric 3 inserts, should only be 4 spaces left at this point since 12 got taken NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetRemainingCapacity(kFabric3, fabric_capacity)); @@ -1046,6 +1081,10 @@ void TestFabricScenes(nlTestSuite * aSuite, void * aContext) NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric3, scene2)); NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric3, scene3)); NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric3, scene4)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetFabricSceneCount(kFabric2, scene_count)); + NL_TEST_ASSERT(aSuite, 4 == scene_count); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetEndpointSceneCount(scene_count)); + NL_TEST_ASSERT(aSuite, scenes::kMaxScenesPerEndpoint == scene_count); // Checks capacity is now 0 accross all fabrics NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetRemainingCapacity(kFabric1, fabric_capacity)); @@ -1098,7 +1137,10 @@ void TestFabricScenes(nlTestSuite * aSuite, void * aContext) // Remove Fabric 1 NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->RemoveFabric(kFabric1)); // Verify Fabric 1 removed - NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->RemoveFabric(kFabric1)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetFabricSceneCount(kFabric1, scene_count)); + NL_TEST_ASSERT(aSuite, 0 == scene_count); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetEndpointSceneCount(scene_count)); + NL_TEST_ASSERT(aSuite, 8 == scene_count); NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId1, scene)); NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId2, scene)); NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId3, scene)); @@ -1109,6 +1151,8 @@ void TestFabricScenes(nlTestSuite * aSuite, void * aContext) NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId8, scene)); // Verify Fabric 2 still there + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetFabricSceneCount(kFabric2, scene_count)); + NL_TEST_ASSERT(aSuite, 4 == scene_count); NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric2, sceneId1, scene)); NL_TEST_ASSERT(aSuite, scene == scene1); NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric2, sceneId2, scene)); @@ -1139,6 +1183,8 @@ void TestFabricScenes(nlTestSuite * aSuite, void * aContext) NL_TEST_ASSERT(aSuite, scene == scene7); NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric2, sceneId8, scene)); NL_TEST_ASSERT(aSuite, scene == scene8); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetFabricSceneCount(kFabric2, scene_count)); + NL_TEST_ASSERT(aSuite, 8 == scene_count); // Verify capacity updated properly NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetRemainingCapacity(kFabric1, fabric_capacity)); @@ -1159,7 +1205,10 @@ void TestFabricScenes(nlTestSuite * aSuite, void * aContext) // Remove Fabric 2 NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->RemoveFabric(kFabric2)); // Verify Fabric 2 removed - NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->RemoveFabric(kFabric2)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetFabricSceneCount(kFabric2, scene_count)); + NL_TEST_ASSERT(aSuite, 0 == scene_count); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetEndpointSceneCount(scene_count)); + NL_TEST_ASSERT(aSuite, 4 == scene_count); NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric2, sceneId1, scene)); NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric2, sceneId2, scene)); NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric2, sceneId3, scene)); @@ -1170,6 +1219,8 @@ void TestFabricScenes(nlTestSuite * aSuite, void * aContext) NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric2, sceneId8, scene)); // Verify Fabric 3 still there + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetFabricSceneCount(kFabric3, scene_count)); + NL_TEST_ASSERT(aSuite, 4 == scene_count); NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric3, sceneId1, scene)); NL_TEST_ASSERT(aSuite, scene == scene1); NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric3, sceneId2, scene)); @@ -1182,12 +1233,23 @@ void TestFabricScenes(nlTestSuite * aSuite, void * aContext) // Remove Fabric 3 NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->RemoveFabric(kFabric3)); // Verify Fabric 3 removed - NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->RemoveFabric(kFabric3)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetFabricSceneCount(kFabric2, scene_count)); + NL_TEST_ASSERT(aSuite, 0 == scene_count); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetEndpointSceneCount(scene_count)); + NL_TEST_ASSERT(aSuite, 0 == scene_count); NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric3, sceneId1, scene)); NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric3, sceneId2, scene)); NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric3, sceneId3, scene)); NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric3, sceneId4, scene)); + // Confirm all counts are at 0 + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetFabricSceneCount(kFabric1, scene_count)); + NL_TEST_ASSERT(aSuite, 0 == scene_count); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetFabricSceneCount(kFabric2, scene_count)); + NL_TEST_ASSERT(aSuite, 0 == scene_count); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetFabricSceneCount(kFabric3, scene_count)); + NL_TEST_ASSERT(aSuite, 0 == scene_count); + // Verify capacity updated for all fabrics NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetRemainingCapacity(kFabric1, fabric_capacity)); NL_TEST_ASSERT(aSuite, scenes::kMaxScenesPerFabric == fabric_capacity); @@ -1197,13 +1259,358 @@ void TestFabricScenes(nlTestSuite * aSuite, void * aContext) NL_TEST_ASSERT(aSuite, scenes::kMaxScenesPerFabric == fabric_capacity); } +void TestEndpointScenes(nlTestSuite * aSuite, void * aContext) +{ + // Get Count for Endpoint 1 + SceneTable * sceneTable = scenes::GetSceneTableImpl(); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); + + // Reset test + ResetSceneTable(sceneTable); + sceneTable = scenes::GetSceneTableImpl(kTestEndpoint1); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); + + SceneTableEntry scene; + + // Verify all endpoints are empty + uint8_t endpoint_scene_count = 0; + + // Get Count for Endpoint 1 + sceneTable = scenes::GetSceneTableImpl(kTestEndpoint1); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetEndpointSceneCount(endpoint_scene_count)); + NL_TEST_ASSERT(aSuite, 0 == endpoint_scene_count); + // Get Count for Endpoint 2 + sceneTable = scenes::GetSceneTableImpl(kTestEndpoint2); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetEndpointSceneCount(endpoint_scene_count)); + NL_TEST_ASSERT(aSuite, 0 == endpoint_scene_count); + // Get Count for Endpoint 3 + sceneTable = scenes::GetSceneTableImpl(kTestEndpoint3); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetEndpointSceneCount(endpoint_scene_count)); + NL_TEST_ASSERT(aSuite, 0 == endpoint_scene_count); + + // Test Scenes insertion not accessible accross all endpoints + sceneTable = scenes::GetSceneTableImpl(kTestEndpoint1); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric1, scene1)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric2, scene1)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetEndpointSceneCount(endpoint_scene_count)); + NL_TEST_ASSERT(aSuite, 2 == endpoint_scene_count); + + uint8_t fabric_capacity = 0; + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetRemainingCapacity(kFabric1, fabric_capacity)); + NL_TEST_ASSERT(aSuite, scenes::kMaxScenesPerFabric - 1 == fabric_capacity); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetRemainingCapacity(kFabric2, fabric_capacity)); + NL_TEST_ASSERT(aSuite, scenes::kMaxScenesPerFabric - 1 == fabric_capacity); + + // Endpoint2 + sceneTable = scenes::GetSceneTableImpl(kTestEndpoint2); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetEndpointSceneCount(endpoint_scene_count)); + NL_TEST_ASSERT(aSuite, 0 == endpoint_scene_count); + // Endpoint3 + sceneTable = scenes::GetSceneTableImpl(kTestEndpoint3); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetEndpointSceneCount(endpoint_scene_count)); + NL_TEST_ASSERT(aSuite, 0 == endpoint_scene_count); + + // Check if scene present in Endpoint 1 + sceneTable = scenes::GetSceneTableImpl(kTestEndpoint1); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric1, sceneId1, scene)); + NL_TEST_ASSERT(aSuite, scene == scene1); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric2, sceneId1, scene)); + NL_TEST_ASSERT(aSuite, scene == scene1); + + // Check if scene present in Endpoint 2 + sceneTable = scenes::GetSceneTableImpl(kTestEndpoint2); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); + NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId1, scene)); + NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric2, sceneId1, scene)); + // Check if scene present in Endpoint 3 + sceneTable = scenes::GetSceneTableImpl(kTestEndpoint3); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); + NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId1, scene)); + NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric2, sceneId1, scene)); + + // Test removal on different endpoints do not affect each endpoints + // Insertion on Endpoint2 + sceneTable = scenes::GetSceneTableImpl(kTestEndpoint2); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric1, scene1)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric1, sceneId1, scene)); + NL_TEST_ASSERT(aSuite, scene == scene1); + + // Removal on Endpoint1 + sceneTable = scenes::GetSceneTableImpl(kTestEndpoint1); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->RemoveSceneTableEntry(kFabric1, sceneId1)); + NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId1, scene)); + + // Scene present on Endpoint2 + sceneTable = scenes::GetSceneTableImpl(kTestEndpoint2); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric1, sceneId1, scene)); + NL_TEST_ASSERT(aSuite, scene == scene1); + + // Removal on Endpoint 2 + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->RemoveSceneTableEntry(kFabric1, sceneId1)); + + // Removal on Endpoint 1 + sceneTable = scenes::GetSceneTableImpl(kTestEndpoint1); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->RemoveSceneTableEntry(kFabric2, sceneId1)); + + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetEndpointSceneCount(endpoint_scene_count)); + NL_TEST_ASSERT(aSuite, 0 == endpoint_scene_count); + // Endpoint 2 + sceneTable = scenes::GetSceneTableImpl(kTestEndpoint2); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetEndpointSceneCount(endpoint_scene_count)); + NL_TEST_ASSERT(aSuite, 0 == endpoint_scene_count); + // Endpoint 3 + sceneTable = scenes::GetSceneTableImpl(kTestEndpoint3); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetEndpointSceneCount(endpoint_scene_count)); + NL_TEST_ASSERT(aSuite, 0 == endpoint_scene_count); + + // Test the fabric capacity accross endpoint + // Fill fabric 1 endpoint 1 + sceneTable = scenes::GetSceneTableImpl(kTestEndpoint1); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric1, scene1)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric1, scene2)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric1, scene3)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric1, scene4)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric1, scene5)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric1, scene6)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric1, scene7)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric1, scene8)); + + // Fill fabric 2 endpoint 1 + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric2, scene1)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric2, scene2)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric2, scene3)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric2, scene4)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric2, scene5)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric2, scene6)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric2, scene7)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric2, scene8)); + + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetRemainingCapacity(kFabric1, fabric_capacity)); + NL_TEST_ASSERT(aSuite, 0 == fabric_capacity); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetRemainingCapacity(kFabric2, fabric_capacity)); + NL_TEST_ASSERT(aSuite, 0 == fabric_capacity); + + // Endpoints 2 and 3 should be unaffected + sceneTable = scenes::GetSceneTableImpl(kTestEndpoint2); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetRemainingCapacity(kFabric1, fabric_capacity)); + NL_TEST_ASSERT(aSuite, scenes::kMaxScenesPerFabric == fabric_capacity); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetRemainingCapacity(kFabric2, fabric_capacity)); + NL_TEST_ASSERT(aSuite, scenes::kMaxScenesPerFabric == fabric_capacity); + + sceneTable = scenes::GetSceneTableImpl(kTestEndpoint3); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetRemainingCapacity(kFabric1, fabric_capacity)); + NL_TEST_ASSERT(aSuite, scenes::kMaxScenesPerFabric == fabric_capacity); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetRemainingCapacity(kFabric2, fabric_capacity)); + NL_TEST_ASSERT(aSuite, scenes::kMaxScenesPerFabric == fabric_capacity); + + // Verify filling Fabric on endpoint 2 does not affect on endpoint 3 despite Max per fabric being reached by adding Endpoint1 + // and Endpoint2 + // Fill fabric 1 endpoint 2 + sceneTable = scenes::GetSceneTableImpl(kTestEndpoint2); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric1, scene1)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric1, scene2)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric1, scene3)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric1, scene4)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric1, scene5)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric1, scene6)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric1, scene7)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric1, scene8)); + + // Fill fabric 2 endpoint 2 + sceneTable = scenes::GetSceneTableImpl(kTestEndpoint2); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric2, scene1)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric2, scene2)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric2, scene3)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric2, scene4)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric2, scene5)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric2, scene6)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric2, scene7)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric2, scene8)); + // scene count to Endpoint + + sceneTable = scenes::GetSceneTableImpl(kTestEndpoint3); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetRemainingCapacity(kFabric1, fabric_capacity)); + NL_TEST_ASSERT(aSuite, scenes::kMaxScenesPerFabric == fabric_capacity); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetRemainingCapacity(kFabric2, fabric_capacity)); + NL_TEST_ASSERT(aSuite, scenes::kMaxScenesPerFabric == fabric_capacity); + + // Test removal of fabric clears scene fabric on all endpoints + sceneTable = scenes::GetSceneTableImpl(kTestEndpoint1); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); + sceneTable->RemoveFabric(kFabric1); + NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId1, scene)); + NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId2, scene)); + NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId3, scene)); + NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId4, scene)); + NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId5, scene)); + NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId6, scene)); + NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId7, scene)); + NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId8, scene)); + + sceneTable = scenes::GetSceneTableImpl(kTestEndpoint2); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); + NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId1, scene)); + NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId2, scene)); + NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId3, scene)); + NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId4, scene)); + NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId5, scene)); + NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId6, scene)); + NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId7, scene)); + NL_TEST_ASSERT(aSuite, CHIP_ERROR_NOT_FOUND == sceneTable->GetSceneTableEntry(kFabric1, sceneId8, scene)); + + sceneTable->RemoveFabric(kFabric2); + + // Validate endpoints are empty + sceneTable = scenes::GetSceneTableImpl(kTestEndpoint1); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetEndpointSceneCount(endpoint_scene_count)); + NL_TEST_ASSERT(aSuite, 0 == endpoint_scene_count); + sceneTable = scenes::GetSceneTableImpl(kTestEndpoint2); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetEndpointSceneCount(endpoint_scene_count)); + NL_TEST_ASSERT(aSuite, 0 == endpoint_scene_count); + sceneTable = scenes::GetSceneTableImpl(kTestEndpoint3); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetEndpointSceneCount(endpoint_scene_count)); + NL_TEST_ASSERT(aSuite, 0 == endpoint_scene_count); + + // Validate Fabric capacities at maximum accross all endpoints + + // Endpoint 1 + sceneTable = scenes::GetSceneTableImpl(kTestEndpoint1); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetRemainingCapacity(kFabric1, fabric_capacity)); + NL_TEST_ASSERT(aSuite, scenes::kMaxScenesPerFabric == fabric_capacity); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetRemainingCapacity(kFabric2, fabric_capacity)); + NL_TEST_ASSERT(aSuite, scenes::kMaxScenesPerFabric == fabric_capacity); + // Endpoint 2 + sceneTable = scenes::GetSceneTableImpl(kTestEndpoint2); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetRemainingCapacity(kFabric1, fabric_capacity)); + NL_TEST_ASSERT(aSuite, scenes::kMaxScenesPerFabric == fabric_capacity); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetRemainingCapacity(kFabric2, fabric_capacity)); + NL_TEST_ASSERT(aSuite, scenes::kMaxScenesPerFabric == fabric_capacity); + // Endpoint 3 + sceneTable = scenes::GetSceneTableImpl(kTestEndpoint3); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetRemainingCapacity(kFabric1, fabric_capacity)); + NL_TEST_ASSERT(aSuite, scenes::kMaxScenesPerFabric == fabric_capacity); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetRemainingCapacity(kFabric2, fabric_capacity)); + NL_TEST_ASSERT(aSuite, scenes::kMaxScenesPerFabric == fabric_capacity); + + // Test of Get with changes to Endpoint capacity + // Endpoint 1 + sceneTable = scenes::GetSceneTableImpl(kTestEndpoint1, scenes::kMaxScenesPerEndpoint - 2); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetRemainingCapacity(kFabric1, fabric_capacity)); + NL_TEST_ASSERT(aSuite, scenes::kMaxScenesPerFabric - 1 == fabric_capacity); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetRemainingCapacity(kFabric2, fabric_capacity)); + NL_TEST_ASSERT(aSuite, scenes::kMaxScenesPerFabric - 1 == fabric_capacity); + + // Test Endpoint 2's capacity remains unaffected + sceneTable = scenes::GetSceneTableImpl(kTestEndpoint2); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetRemainingCapacity(kFabric1, fabric_capacity)); + NL_TEST_ASSERT(aSuite, scenes::kMaxScenesPerFabric == fabric_capacity); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetRemainingCapacity(kFabric2, fabric_capacity)); + NL_TEST_ASSERT(aSuite, scenes::kMaxScenesPerFabric == fabric_capacity); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); + + // Test Insertion then change of capacity + sceneTable = scenes::GetSceneTableImpl(kTestEndpoint1); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric1, scene1)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric1, scene2)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric1, scene3)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric1, scene4)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetRemainingCapacity(kFabric1, fabric_capacity)); + NL_TEST_ASSERT(aSuite, scenes::kMaxScenesPerFabric - 4 == fabric_capacity); + + sceneTable = scenes::GetSceneTableImpl(kTestEndpoint1, scenes::kMaxScenesPerFabric - 2); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetRemainingCapacity(kFabric1, fabric_capacity)); + NL_TEST_ASSERT(aSuite, scenes::kMaxScenesPerFabric - 6 == fabric_capacity); + + sceneTable = scenes::GetSceneTableImpl(kTestEndpoint1, scenes::kMaxScenesPerFabric - 4); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetRemainingCapacity(kFabric1, fabric_capacity)); + NL_TEST_ASSERT(aSuite, 0 == fabric_capacity); + + // Test making the endpoint scene table smaller than the actual number of scenes on it + sceneTable = scenes::GetSceneTableImpl(kTestEndpoint1, scenes::kMaxScenesPerFabric - 5); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetRemainingCapacity(kFabric1, fabric_capacity)); + NL_TEST_ASSERT(aSuite, 0 == fabric_capacity); +} + void TestOTAChanges(nlTestSuite * aSuite, void * aContext) { - SceneTable * sceneTable = &sSceneTable; - NL_TEST_ASSERT(aSuite, sceneTable); + SceneTable * sceneTable = scenes::GetSceneTableImpl(kTestEndpoint1); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); // Reset test ResetSceneTable(sceneTable); + sceneTable = scenes::GetSceneTableImpl(kTestEndpoint1); + NL_TEST_ASSERT(aSuite, nullptr != sceneTable); + VerifyOrReturn(nullptr != sceneTable); SceneTableEntry scene; uint8_t fabric_capacity = 0; @@ -1240,25 +1647,27 @@ void TestOTAChanges(nlTestSuite * aSuite, void * aContext) iterator->Release(); // SceneTable should be full at this point uint8_t scene_count; - NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetGlobalSceneCount(scene_count)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetEndpointSceneCount(scene_count)); // Global count should not have been modified - NL_TEST_ASSERT(aSuite, scenes::kMaxScenesGlobal == scene_count); + NL_TEST_ASSERT(aSuite, scenes::kMaxScenesPerEndpoint == scene_count); // Test failure to init a SceneTable with sizes above the defined max scenes per fabric or globaly - TestSceneTableImpl SceneTableTooManyPerFabric(scenes::kMaxScenesPerFabric + 1, scenes::kMaxScenesGlobal); + TestSceneTableImpl SceneTableTooManyPerFabric(scenes::kMaxScenesPerFabric + 1, scenes::kMaxScenesPerEndpoint); NL_TEST_ASSERT(aSuite, CHIP_ERROR_INVALID_INTEGER_VALUE == SceneTableTooManyPerFabric.Init(&testStorage)); SceneTableTooManyPerFabric.Finish(); - TestSceneTableImpl SceneTableTooManyGlobal(scenes::kMaxScenesPerFabric, scenes::kMaxScenesGlobal + 1); + TestSceneTableImpl SceneTableTooManyGlobal(scenes::kMaxScenesPerFabric, scenes::kMaxScenesPerEndpoint + 1); NL_TEST_ASSERT(aSuite, CHIP_ERROR_INVALID_INTEGER_VALUE == SceneTableTooManyGlobal.Init(&testStorage)); SceneTableTooManyGlobal.Finish(); // Create a new table with a different limit of scenes per fabric - TestSceneTableImpl ReducedSceneTable(scenes::kMaxScenesPerFabric - 1, scenes::kMaxScenesGlobal - 2); + TestSceneTableImpl ReducedSceneTable(scenes::kMaxScenesPerFabric - 1, scenes::kMaxScenesPerEndpoint - 2); NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == ReducedSceneTable.Init(&testStorage)); + ReducedSceneTable.SetEndpoint(kTestEndpoint1); + // Global count should not have been modified - NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == ReducedSceneTable.GetGlobalSceneCount(scene_count)); - NL_TEST_ASSERT(aSuite, scenes::kMaxScenesGlobal == scene_count); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == ReducedSceneTable.GetEndpointSceneCount(scene_count)); + NL_TEST_ASSERT(aSuite, scenes::kMaxScenesPerEndpoint == scene_count); NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == ReducedSceneTable.GetRemainingCapacity(kFabric1, fabric_capacity)); NL_TEST_ASSERT(aSuite, 0 == fabric_capacity); @@ -1272,9 +1681,9 @@ void TestOTAChanges(nlTestSuite * aSuite, void * aContext) iterator->Release(); // Capacity should still be 0 NL_TEST_ASSERT(aSuite, 0 == fabric_capacity); - NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == ReducedSceneTable.GetGlobalSceneCount(scene_count)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == ReducedSceneTable.GetEndpointSceneCount(scene_count)); // Global count should have been reduced by 1 - NL_TEST_ASSERT(aSuite, scenes::kMaxScenesGlobal - 1 == scene_count); + NL_TEST_ASSERT(aSuite, scenes::kMaxScenesPerEndpoint - 1 == scene_count); // Remove a Scene from the Fabric 1 NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == ReducedSceneTable.RemoveSceneTableEntry(kFabric1, scene.mStorageId)); @@ -1293,8 +1702,8 @@ void TestOTAChanges(nlTestSuite * aSuite, void * aContext) iterator->Release(); // Global count should now have been adjusted - NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == ReducedSceneTable.GetGlobalSceneCount(scene_count)); - NL_TEST_ASSERT(aSuite, scenes::kMaxScenesGlobal - 3 == scene_count); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == ReducedSceneTable.GetEndpointSceneCount(scene_count)); + NL_TEST_ASSERT(aSuite, scenes::kMaxScenesPerEndpoint - 3 == scene_count); // Confirm we now have capacity NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == ReducedSceneTable.GetRemainingCapacity(kFabric1, fabric_capacity)); NL_TEST_ASSERT(aSuite, 1 == fabric_capacity); @@ -1308,8 +1717,8 @@ void TestOTAChanges(nlTestSuite * aSuite, void * aContext) NL_TEST_ASSERT(aSuite, scenes::kMaxScenesPerFabric - 1 == iterator->Count()); iterator->Release(); // Global count should now have been adjusted - NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == ReducedSceneTable.GetGlobalSceneCount(scene_count)); - NL_TEST_ASSERT(aSuite, scenes::kMaxScenesGlobal - 4 == scene_count); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == ReducedSceneTable.GetEndpointSceneCount(scene_count)); + NL_TEST_ASSERT(aSuite, scenes::kMaxScenesPerEndpoint - 4 == scene_count); // Confirm we now have capacity in the first fabric since we previously removed 2 scenes form there NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == ReducedSceneTable.GetRemainingCapacity(kFabric1, fabric_capacity)); NL_TEST_ASSERT(aSuite, 2 == fabric_capacity); @@ -1342,7 +1751,9 @@ int TestSetup(void * inContext) VerifyOrReturnError(CHIP_NO_ERROR == chip::Platform::MemoryInit(), FAILURE); // Initialize Scene Table - VerifyOrReturnError(CHIP_NO_ERROR == TestScenes::sSceneTable.Init(&TestScenes::testStorage), FAILURE); + SceneTable * sceneTable = scenes::GetSceneTableImpl(); + VerifyOrReturnError(nullptr != sceneTable, FAILURE); + VerifyOrReturnError(CHIP_NO_ERROR == sceneTable->Init(&TestScenes::testStorage), FAILURE); return SUCCESS; } @@ -1352,7 +1763,10 @@ int TestSetup(void * inContext) */ int TestTeardown(void * inContext) { - TestScenes::sSceneTable.Finish(); + // Terminate Scene Table + SceneTable * sceneTable = scenes::GetSceneTableImpl(); + VerifyOrReturnError(nullptr != sceneTable, FAILURE); + sceneTable->Finish(); chip::Platform::MemoryShutdown(); return SUCCESS; @@ -1368,6 +1782,7 @@ int TestSceneTable() NL_TEST_DEF("TestIterateScenes", TestScenes::TestIterateScenes), NL_TEST_DEF("TestRemoveScenes", TestScenes::TestRemoveScenes), NL_TEST_DEF("TestFabricScenes", TestScenes::TestFabricScenes), + NL_TEST_DEF("TestEndpointScenes", TestScenes::TestEndpointScenes), NL_TEST_DEF("TestOTAChanges", TestScenes::TestOTAChanges), NL_TEST_SENTINEL() }; diff --git a/src/app/util/mock/attribute-storage.cpp b/src/app/util/mock/attribute-storage.cpp index a1925e7e70158c..d763f5646a8cfa 100644 --- a/src/app/util/mock/attribute-storage.cpp +++ b/src/app/util/mock/attribute-storage.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -270,12 +271,52 @@ uint16_t emberAfLongStringLength(const uint8_t * buffer) return (length == 0xFFFF ? 0 : length); } +// This will find the first server that has the clusterId given from the index of endpoint. +bool emberAfContainsServerFromIndex(uint16_t index, ClusterId clusterId) +{ + if (index == kEmberInvalidEndpointIndex) + { + return false; + } + + return clusterId; // Mock version return true as long as the endpoint is valid +} + namespace chip { namespace app { + AttributeAccessInterface * GetAttributeAccessOverride(EndpointId aEndpointId, ClusterId aClusterId) { return nullptr; } + +EnabledEndpointsWithServerCluster::EnabledEndpointsWithServerCluster(ClusterId clusterId) : mClusterId(clusterId) +{ + EnsureMatchingEndpoint(); +} +EnabledEndpointsWithServerCluster & EnabledEndpointsWithServerCluster::operator++() +{ + ++mEndpointIndex; + EnsureMatchingEndpoint(); + return *this; +} + +void EnabledEndpointsWithServerCluster::EnsureMatchingEndpoint() +{ + for (; mEndpointIndex < mEndpointCount; ++mEndpointIndex) + { + if (!emberAfEndpointIndexIsEnabled(mEndpointIndex)) + { + continue; + } + + if (emberAfContainsServerFromIndex(mEndpointIndex, mClusterId)) + { + break; + } + } +} + } // namespace app namespace Test { diff --git a/src/app/util/mock/include/zap-generated/endpoint_config.h b/src/app/util/mock/include/zap-generated/endpoint_config.h index e69de29bb2d1d6..33d1e87e320896 100644 --- a/src/app/util/mock/include/zap-generated/endpoint_config.h +++ b/src/app/util/mock/include/zap-generated/endpoint_config.h @@ -0,0 +1,2 @@ +// Number of fixed endpoints +#define FIXED_ENDPOINT_COUNT (3) diff --git a/src/app/zap-templates/zcl/data-model/chip/scene.xml b/src/app/zap-templates/zcl/data-model/chip/scene.xml index c05603a8cf77c4..cdc3a3d29f3374 100644 --- a/src/app/zap-templates/zcl/data-model/chip/scene.xml +++ b/src/app/zap-templates/zcl/data-model/chip/scene.xml @@ -51,7 +51,6 @@ limitations under the License. LastConfiguredBy SceneTableSize RemainingCapacity - Add a scene to the scene table. Extension field sets are supported, and are inputed as '{"ClusterID": VALUE, "AttributeValueList":[{"AttributeId": VALUE, "AttributeValue": VALUE}]}' diff --git a/src/app/zap-templates/zcl/zcl-with-test-extensions.json b/src/app/zap-templates/zcl/zcl-with-test-extensions.json index adccb7f9b85a46..9aa99576b33208 100644 --- a/src/app/zap-templates/zcl/zcl-with-test-extensions.json +++ b/src/app/zap-templates/zcl/zcl-with-test-extensions.json @@ -196,6 +196,7 @@ "FeatureMap", "ThreadMetrics" ], + "Scenes": ["SceneCount", "RemainingCapacity"], "Unit Testing": [ "struct_attr", "nullable_struct", diff --git a/src/app/zap-templates/zcl/zcl.json b/src/app/zap-templates/zcl/zcl.json index 89243b4dbb0daa..cdee396cb50811 100644 --- a/src/app/zap-templates/zcl/zcl.json +++ b/src/app/zap-templates/zcl/zcl.json @@ -194,6 +194,7 @@ "FeatureMap", "ThreadMetrics" ], + "Scenes": ["SceneCount", "RemainingCapacity"], "Unit Testing": [ "struct_attr", "nullable_struct", diff --git a/src/app/zap_cluster_list.json b/src/app/zap_cluster_list.json index 3a5d1470a3e916..f94ce04af4eaee 100644 --- a/src/app/zap_cluster_list.json +++ b/src/app/zap_cluster_list.json @@ -219,7 +219,7 @@ "PWM_CLUSTER": [], "REFRIGERATOR_ALARM_CLUSTER": ["refrigerator-alarm-server"], "RELATIVE_HUMIDITY_MEASUREMENT_CLUSTER": [], - "SCENES_CLUSTER": ["scenes"], + "SCENES_CLUSTER": ["scenes-server"], "SMOKE_CO_ALARM_CLUSTER": [], "SOFTWARE_DIAGNOSTICS_CLUSTER": ["software-diagnostics-server"], "SWITCH_CLUSTER": ["switch-server"], diff --git a/src/lib/core/CHIPConfig.h b/src/lib/core/CHIPConfig.h index bc368912e2965c..0a3b60f18b9c88 100644 --- a/src/lib/core/CHIPConfig.h +++ b/src/lib/core/CHIPConfig.h @@ -1379,8 +1379,8 @@ extern const char CHIP_NON_PRODUCTION_MARKER[]; /** * @brief The minimum number of scenes to support according to spec */ -#ifndef CHIP_CONFIG_SCENES_MAX_NUMBER -#define CHIP_CONFIG_SCENES_MAX_NUMBER 16 +#ifndef CHIP_CONFIG_MAX_SCENES_PER_ENDPOINT +#define CHIP_CONFIG_MAX_SCENES_PER_ENDPOINT 16 #endif /** @@ -1394,7 +1394,7 @@ extern const char CHIP_NON_PRODUCTION_MARKER[]; * @brief The maximum number of scenes allowed on a single fabric */ #ifndef CHIP_CONFIG_SCENES_MAX_PER_FABRIC -#define CHIP_CONFIG_SCENES_MAX_PER_FABRIC (CHIP_CONFIG_SCENES_MAX_NUMBER / 2) +#define CHIP_CONFIG_SCENES_MAX_PER_FABRIC (CHIP_CONFIG_MAX_SCENES_PER_ENDPOINT / 2) #endif /** diff --git a/src/lib/support/DefaultStorageKeyAllocator.h b/src/lib/support/DefaultStorageKeyAllocator.h index 418220a3f3d520..51e006cfa41b8e 100644 --- a/src/lib/support/DefaultStorageKeyAllocator.h +++ b/src/lib/support/DefaultStorageKeyAllocator.h @@ -198,11 +198,22 @@ class DefaultStorageKeyAllocator } static StorageKeyName SubscriptionResumptionMaxCount() { return StorageKeyName::Formatted("g/sum"); } - static StorageKeyName GlobalSceneCountKey() { return StorageKeyName::Formatted("g/scc"); } - static StorageKeyName FabricSceneDataKey(chip::FabricIndex fabric) { return StorageKeyName::Formatted("f/%x/sc", fabric); } - static StorageKeyName FabricSceneKey(chip::FabricIndex fabric, uint8_t id) + // Number of scenes stored in a given endpoint's scene table, across all fabrics. + static StorageKeyName EndpointSceneCountKey(EndpointId endpoint) { return StorageKeyName::Formatted("g/scc/e/%x", endpoint); } + + // Stores the scene count for a fabric for the given endpoint and a map between scene storage ids () and + // sceneIndex for a specific Fabric and endpoint. + static StorageKeyName FabricSceneDataKey(FabricIndex fabric, EndpointId endpoint) + { + return StorageKeyName::Formatted("f/%x/e/%x/sc", fabric, endpoint); + } + + // Stores the actual scene data for a given scene on a given endpoint for a particular fabric. + // idx corresponds to the indices read from FabricSceneDataKey. + // SceneIndex + static StorageKeyName FabricSceneKey(FabricIndex fabric, EndpointId endpoint, uint16_t idx) { - return StorageKeyName::Formatted("f/%x/sc/%x", fabric, id); + return StorageKeyName::Formatted("f/%x/e/%x/sc/%x", fabric, endpoint, idx); } }; diff --git a/zzz_generated/app-common/app-common/zap-generated/attributes/Accessors.cpp b/zzz_generated/app-common/app-common/zap-generated/attributes/Accessors.cpp index b305704c5807b4..e2707246b3e620 100644 --- a/zzz_generated/app-common/app-common/zap-generated/attributes/Accessors.cpp +++ b/zzz_generated/app-common/app-common/zap-generated/attributes/Accessors.cpp @@ -267,37 +267,6 @@ EmberAfStatus Set(chip::EndpointId endpoint, uint16_t value) namespace Scenes { namespace Attributes { -namespace SceneCount { - -EmberAfStatus Get(chip::EndpointId endpoint, uint8_t * value) -{ - using Traits = NumericAttributeTraits; - Traits::StorageType temp; - uint8_t * readable = Traits::ToAttributeStoreRepresentation(temp); - EmberAfStatus status = emberAfReadAttribute(endpoint, Clusters::Scenes::Id, Id, readable, sizeof(temp)); - VerifyOrReturnError(EMBER_ZCL_STATUS_SUCCESS == status, status); - if (!Traits::CanRepresentValue(/* isNullable = */ false, temp)) - { - return EMBER_ZCL_STATUS_CONSTRAINT_ERROR; - } - *value = Traits::StorageToWorking(temp); - return status; -} -EmberAfStatus Set(chip::EndpointId endpoint, uint8_t value) -{ - using Traits = NumericAttributeTraits; - if (!Traits::CanRepresentValue(/* isNullable = */ false, value)) - { - return EMBER_ZCL_STATUS_CONSTRAINT_ERROR; - } - Traits::StorageType storageValue; - Traits::WorkingToStorage(value, storageValue); - uint8_t * writable = Traits::ToAttributeStoreRepresentation(storageValue); - return emberAfWriteAttribute(endpoint, Clusters::Scenes::Id, Id, writable, ZCL_INT8U_ATTRIBUTE_TYPE); -} - -} // namespace SceneCount - namespace CurrentScene { EmberAfStatus Get(chip::EndpointId endpoint, uint8_t * value) @@ -506,37 +475,6 @@ EmberAfStatus Set(chip::EndpointId endpoint, uint16_t value) } // namespace SceneTableSize -namespace RemainingCapacity { - -EmberAfStatus Get(chip::EndpointId endpoint, uint8_t * value) -{ - using Traits = NumericAttributeTraits; - Traits::StorageType temp; - uint8_t * readable = Traits::ToAttributeStoreRepresentation(temp); - EmberAfStatus status = emberAfReadAttribute(endpoint, Clusters::Scenes::Id, Id, readable, sizeof(temp)); - VerifyOrReturnError(EMBER_ZCL_STATUS_SUCCESS == status, status); - if (!Traits::CanRepresentValue(/* isNullable = */ false, temp)) - { - return EMBER_ZCL_STATUS_CONSTRAINT_ERROR; - } - *value = Traits::StorageToWorking(temp); - return status; -} -EmberAfStatus Set(chip::EndpointId endpoint, uint8_t value) -{ - using Traits = NumericAttributeTraits; - if (!Traits::CanRepresentValue(/* isNullable = */ false, value)) - { - return EMBER_ZCL_STATUS_CONSTRAINT_ERROR; - } - Traits::StorageType storageValue; - Traits::WorkingToStorage(value, storageValue); - uint8_t * writable = Traits::ToAttributeStoreRepresentation(storageValue); - return emberAfWriteAttribute(endpoint, Clusters::Scenes::Id, Id, writable, ZCL_INT8U_ATTRIBUTE_TYPE); -} - -} // namespace RemainingCapacity - namespace FeatureMap { EmberAfStatus Get(chip::EndpointId endpoint, uint32_t * value) diff --git a/zzz_generated/app-common/app-common/zap-generated/attributes/Accessors.h b/zzz_generated/app-common/app-common/zap-generated/attributes/Accessors.h index 086820b98325db..730932955f6d19 100644 --- a/zzz_generated/app-common/app-common/zap-generated/attributes/Accessors.h +++ b/zzz_generated/app-common/app-common/zap-generated/attributes/Accessors.h @@ -84,11 +84,6 @@ EmberAfStatus Set(chip::EndpointId endpoint, uint16_t value); namespace Scenes { namespace Attributes { -namespace SceneCount { -EmberAfStatus Get(chip::EndpointId endpoint, uint8_t * value); // int8u -EmberAfStatus Set(chip::EndpointId endpoint, uint8_t value); -} // namespace SceneCount - namespace CurrentScene { EmberAfStatus Get(chip::EndpointId endpoint, uint8_t * value); // int8u EmberAfStatus Set(chip::EndpointId endpoint, uint8_t value); @@ -121,11 +116,6 @@ EmberAfStatus Get(chip::EndpointId endpoint, uint16_t * value); // int16u EmberAfStatus Set(chip::EndpointId endpoint, uint16_t value); } // namespace SceneTableSize -namespace RemainingCapacity { -EmberAfStatus Get(chip::EndpointId endpoint, uint8_t * value); // int8u -EmberAfStatus Set(chip::EndpointId endpoint, uint8_t value); -} // namespace RemainingCapacity - namespace FeatureMap { EmberAfStatus Get(chip::EndpointId endpoint, uint32_t * value); // bitmap32 EmberAfStatus Set(chip::EndpointId endpoint, uint32_t value); diff --git a/zzz_generated/app-common/app-common/zap-generated/callback.h b/zzz_generated/app-common/app-common/zap-generated/callback.h index 0c2f241fb9e519..cf4da888a6982f 100644 --- a/zzz_generated/app-common/app-common/zap-generated/callback.h +++ b/zzz_generated/app-common/app-common/zap-generated/callback.h @@ -10728,66 +10728,6 @@ bool emberAfGroupsClusterRemoveAllGroupsCallback( bool emberAfGroupsClusterAddGroupIfIdentifyingCallback( chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, const chip::app::Clusters::Groups::Commands::AddGroupIfIdentifying::DecodableType & commandData); -/** - * @brief Scenes Cluster AddScene Command callback (from client) - */ -bool emberAfScenesClusterAddSceneCallback(chip::app::CommandHandler * commandObj, - const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::Scenes::Commands::AddScene::DecodableType & commandData); -/** - * @brief Scenes Cluster ViewScene Command callback (from client) - */ -bool emberAfScenesClusterViewSceneCallback(chip::app::CommandHandler * commandObj, - const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::Scenes::Commands::ViewScene::DecodableType & commandData); -/** - * @brief Scenes Cluster RemoveScene Command callback (from client) - */ -bool emberAfScenesClusterRemoveSceneCallback(chip::app::CommandHandler * commandObj, - const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::Scenes::Commands::RemoveScene::DecodableType & commandData); -/** - * @brief Scenes Cluster RemoveAllScenes Command callback (from client) - */ -bool emberAfScenesClusterRemoveAllScenesCallback( - chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::Scenes::Commands::RemoveAllScenes::DecodableType & commandData); -/** - * @brief Scenes Cluster StoreScene Command callback (from client) - */ -bool emberAfScenesClusterStoreSceneCallback(chip::app::CommandHandler * commandObj, - const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::Scenes::Commands::StoreScene::DecodableType & commandData); -/** - * @brief Scenes Cluster RecallScene Command callback (from client) - */ -bool emberAfScenesClusterRecallSceneCallback(chip::app::CommandHandler * commandObj, - const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::Scenes::Commands::RecallScene::DecodableType & commandData); -/** - * @brief Scenes Cluster GetSceneMembership Command callback (from client) - */ -bool emberAfScenesClusterGetSceneMembershipCallback( - chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::Scenes::Commands::GetSceneMembership::DecodableType & commandData); -/** - * @brief Scenes Cluster EnhancedAddScene Command callback (from client) - */ -bool emberAfScenesClusterEnhancedAddSceneCallback( - chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::Scenes::Commands::EnhancedAddScene::DecodableType & commandData); -/** - * @brief Scenes Cluster EnhancedViewScene Command callback (from client) - */ -bool emberAfScenesClusterEnhancedViewSceneCallback( - chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::Scenes::Commands::EnhancedViewScene::DecodableType & commandData); -/** - * @brief Scenes Cluster CopyScene Command callback (from client) - */ -bool emberAfScenesClusterCopySceneCallback(chip::app::CommandHandler * commandObj, - const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::Scenes::Commands::CopyScene::DecodableType & commandData); /** * @brief On/Off Cluster Off Command callback (from client) */