Coverage Report

Created: 2025-09-02 06:46

/src/connectedhomeip/src/app/clusters/scenes-server/scenes-server.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 *
3
 *    Copyright (c) 2021-2023 Project CHIP Authors
4
 *    All rights reserved.
5
 *
6
 *    Licensed under the Apache License, Version 2.0 (the "License");
7
 *    you may not use this file except in compliance with the License.
8
 *    You may obtain a copy of the License at
9
 *
10
 *        http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 *    Unless required by applicable law or agreed to in writing, software
13
 *    distributed under the License is distributed on an "AS IS" BASIS,
14
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 *    See the License for the specific language governing permissions and
16
 *    limitations under the License.
17
 */
18
19
#include "scenes-server.h"
20
21
#include <app-common/zap-generated/attributes/Accessors.h>
22
#include <app-common/zap-generated/cluster-objects.h>
23
#include <app/AttributeAccessInterfaceRegistry.h>
24
#include <app/CommandHandlerInterface.h>
25
#include <app/CommandHandlerInterfaceRegistry.h>
26
#include <app/InteractionModelEngine.h>
27
#include <app/clusters/scenes-server/SceneTableImpl.h>
28
#include <app/reporting/reporting.h>
29
#include <app/server/Server.h>
30
#include <app/util/attribute-storage.h>
31
#include <app/util/config.h>
32
#include <credentials/GroupDataProvider.h>
33
#include <lib/support/CommonIterator.h>
34
#include <lib/support/Span.h>
35
#include <lib/support/logging/CHIPLogging.h>
36
#include <platform/PlatformManager.h>
37
#include <tracing/macros.h>
38
39
using SceneTableEntry   = chip::scenes::DefaultSceneTableImpl::SceneTableEntry;
40
using SceneStorageId    = chip::scenes::DefaultSceneTableImpl::SceneStorageId;
41
using SceneData         = chip::scenes::DefaultSceneTableImpl::SceneData;
42
using HandlerContext    = chip::app::CommandHandlerInterface::HandlerContext;
43
using ExtensionFieldSet = chip::scenes::ExtensionFieldSet;
44
using GroupDataProvider = chip::Credentials::GroupDataProvider;
45
using SceneTable        = chip::scenes::SceneTable<chip::scenes::ExtensionFieldSetsImpl>;
46
using AuthMode          = chip::Access::AuthMode;
47
using ScenesServer      = chip::app::Clusters::ScenesManagement::ScenesServer;
48
using chip::Protocols::InteractionModel::Status;
49
50
namespace chip {
51
namespace app {
52
namespace Clusters {
53
namespace ScenesManagement {
54
55
namespace {
56
57
Protocols::InteractionModel::Status ResponseStatus(CHIP_ERROR err)
58
0
{
59
    // TODO : Properly fix mapping between error types (issue https://github.com/project-chip/connectedhomeip/issues/26885)
60
0
    if (CHIP_ERROR_NOT_FOUND == err)
61
0
    {
62
0
        return Protocols::InteractionModel::Status::NotFound;
63
0
    }
64
0
    if (CHIP_ERROR_NO_MEMORY == err)
65
0
    {
66
0
        return Protocols::InteractionModel::Status::ResourceExhausted;
67
0
    }
68
0
    if (CHIP_IM_GLOBAL_STATUS(UnsupportedAttribute) == err)
69
0
    {
70
        // TODO: Confirm if we need to add UnsupportedAttribute status as a return for Scene Commands
71
0
        return Protocols::InteractionModel::Status::InvalidCommand;
72
0
    }
73
0
    return StatusIB(err).mStatus;
74
0
}
75
76
/// @brief Generate and add a response to a command handler context if err parameter is not CHIP_NO_ERROR
77
/// @tparam ResponseType Type of response, depends on the command
78
/// @param ctx Command Handler context where to add reponse
79
/// @param resp Response to add in ctx
80
/// @param status Status to verify
81
/// @return CHIP_ERROR
82
template <typename ResponseType>
83
CHIP_ERROR AddResponseOnError(CommandHandlerInterface::HandlerContext & ctx, ResponseType & resp, CHIP_ERROR err)
84
0
{
85
0
    if (CHIP_NO_ERROR != err)
86
0
    {
87
0
        resp.status = to_underlying(ResponseStatus(err));
88
0
        ctx.mCommandHandler.AddResponse(ctx.mRequestPath, resp);
89
0
    }
90
0
    return err;
91
0
}
Unexecuted instantiation: scenes-server.cpp:chip::ChipError chip::app::Clusters::ScenesManagement::(anonymous namespace)::AddResponseOnError<chip::app::Clusters::ScenesManagement::Commands::AddSceneResponse::Type>(chip::app::CommandHandlerInterface::HandlerContext&, chip::app::Clusters::ScenesManagement::Commands::AddSceneResponse::Type&, chip::ChipError)
Unexecuted instantiation: scenes-server.cpp:chip::ChipError chip::app::Clusters::ScenesManagement::(anonymous namespace)::AddResponseOnError<chip::app::Clusters::ScenesManagement::Commands::ViewSceneResponse::Type>(chip::app::CommandHandlerInterface::HandlerContext&, chip::app::Clusters::ScenesManagement::Commands::ViewSceneResponse::Type&, chip::ChipError)
Unexecuted instantiation: scenes-server.cpp:chip::ChipError chip::app::Clusters::ScenesManagement::(anonymous namespace)::AddResponseOnError<chip::app::Clusters::ScenesManagement::Commands::RemoveSceneResponse::Type>(chip::app::CommandHandlerInterface::HandlerContext&, chip::app::Clusters::ScenesManagement::Commands::RemoveSceneResponse::Type&, chip::ChipError)
Unexecuted instantiation: scenes-server.cpp:chip::ChipError chip::app::Clusters::ScenesManagement::(anonymous namespace)::AddResponseOnError<chip::app::Clusters::ScenesManagement::Commands::RemoveAllScenesResponse::Type>(chip::app::CommandHandlerInterface::HandlerContext&, chip::app::Clusters::ScenesManagement::Commands::RemoveAllScenesResponse::Type&, chip::ChipError)
Unexecuted instantiation: scenes-server.cpp:chip::ChipError chip::app::Clusters::ScenesManagement::(anonymous namespace)::AddResponseOnError<chip::app::Clusters::ScenesManagement::Commands::StoreSceneResponse::Type>(chip::app::CommandHandlerInterface::HandlerContext&, chip::app::Clusters::ScenesManagement::Commands::StoreSceneResponse::Type&, chip::ChipError)
Unexecuted instantiation: scenes-server.cpp:chip::ChipError chip::app::Clusters::ScenesManagement::(anonymous namespace)::AddResponseOnError<chip::app::Clusters::ScenesManagement::Commands::GetSceneMembershipResponse::Type>(chip::app::CommandHandlerInterface::HandlerContext&, chip::app::Clusters::ScenesManagement::Commands::GetSceneMembershipResponse::Type&, chip::ChipError)
Unexecuted instantiation: scenes-server.cpp:chip::ChipError chip::app::Clusters::ScenesManagement::(anonymous namespace)::AddResponseOnError<chip::app::Clusters::ScenesManagement::Commands::CopySceneResponse::Type>(chip::app::CommandHandlerInterface::HandlerContext&, chip::app::Clusters::ScenesManagement::Commands::CopySceneResponse::Type&, chip::ChipError)
92
93
/// @brief Generate and add a response to a command handler context depending on an InteractionModel::Status
94
/// @tparam ResponseType Type of response, depends on the command
95
/// @param ctx Command Handler context where to add reponse
96
/// @param resp Response to add in ctx
97
/// @param status Status to verify
98
/// @return InteractionModel::Status -> CHIP_ERROR
99
template <typename ResponseType>
100
CHIP_ERROR AddResponseOnError(CommandHandlerInterface::HandlerContext & ctx, ResponseType & resp, Status status)
101
0
{
102
    // TODO: this seems odd: we convert `status` to a CHIP_ERROR and then back to status. This seems
103
    //       potentially lossy and not ideal.
104
0
    return AddResponseOnError(ctx, resp, StatusIB(status).ToChipError());
105
0
}
Unexecuted instantiation: scenes-server.cpp:chip::ChipError chip::app::Clusters::ScenesManagement::(anonymous namespace)::AddResponseOnError<chip::app::Clusters::ScenesManagement::Commands::AddSceneResponse::Type>(chip::app::CommandHandlerInterface::HandlerContext&, chip::app::Clusters::ScenesManagement::Commands::AddSceneResponse::Type&, chip::Protocols::InteractionModel::Status)
Unexecuted instantiation: scenes-server.cpp:chip::ChipError chip::app::Clusters::ScenesManagement::(anonymous namespace)::AddResponseOnError<chip::app::Clusters::ScenesManagement::Commands::ViewSceneResponse::Type>(chip::app::CommandHandlerInterface::HandlerContext&, chip::app::Clusters::ScenesManagement::Commands::ViewSceneResponse::Type&, chip::Protocols::InteractionModel::Status)
Unexecuted instantiation: scenes-server.cpp:chip::ChipError chip::app::Clusters::ScenesManagement::(anonymous namespace)::AddResponseOnError<chip::app::Clusters::ScenesManagement::Commands::RemoveSceneResponse::Type>(chip::app::CommandHandlerInterface::HandlerContext&, chip::app::Clusters::ScenesManagement::Commands::RemoveSceneResponse::Type&, chip::Protocols::InteractionModel::Status)
Unexecuted instantiation: scenes-server.cpp:chip::ChipError chip::app::Clusters::ScenesManagement::(anonymous namespace)::AddResponseOnError<chip::app::Clusters::ScenesManagement::Commands::RemoveAllScenesResponse::Type>(chip::app::CommandHandlerInterface::HandlerContext&, chip::app::Clusters::ScenesManagement::Commands::RemoveAllScenesResponse::Type&, chip::Protocols::InteractionModel::Status)
Unexecuted instantiation: scenes-server.cpp:chip::ChipError chip::app::Clusters::ScenesManagement::(anonymous namespace)::AddResponseOnError<chip::app::Clusters::ScenesManagement::Commands::GetSceneMembershipResponse::Type>(chip::app::CommandHandlerInterface::HandlerContext&, chip::app::Clusters::ScenesManagement::Commands::GetSceneMembershipResponse::Type&, chip::Protocols::InteractionModel::Status)
Unexecuted instantiation: scenes-server.cpp:chip::ChipError chip::app::Clusters::ScenesManagement::(anonymous namespace)::AddResponseOnError<chip::app::Clusters::ScenesManagement::Commands::CopySceneResponse::Type>(chip::app::CommandHandlerInterface::HandlerContext&, chip::app::Clusters::ScenesManagement::Commands::CopySceneResponse::Type&, chip::Protocols::InteractionModel::Status)
106
107
/// @brief Helper function to update the FabricSceneInfo attribute for a given Endpoint and fabric
108
/// @param endpoint Endpoint to update
109
/// @param fabric Fabric to update
110
/// @param group Group to update, if not provided, will be assigned 0 for a new SceneInfoStruct or keep previous value for an
111
/// existing one
112
/// @param scene Scene to update, if not provided, will be assigned 0 for a new SceneInfoStruct or keep previous value for an
113
/// existing one
114
/// @param sceneValid sceneValid status, if not provided, will be assigned false for a new SceneInfoStruct or keep previous
115
/// value for an existing one
116
/// @return
117
CHIP_ERROR UpdateFabricSceneInfo(EndpointId endpoint, FabricIndex fabric, Optional<GroupId> group, Optional<SceneId> scene,
118
                                 Optional<bool> sceneValid)
119
0
{
120
0
    VerifyOrReturnError(kInvalidEndpointId != endpoint, CHIP_ERROR_INVALID_ARGUMENT);
121
0
    VerifyOrReturnError(kUndefinedFabricIndex != fabric, CHIP_ERROR_INVALID_ARGUMENT);
122
123
0
    SceneTable * sceneTable                    = scenes::GetSceneTableImpl(endpoint);
124
0
    Structs::SceneInfoStruct::Type * sceneInfo = ScenesServer::Instance().GetSceneInfoStruct(endpoint, fabric);
125
0
    if (nullptr != sceneInfo)
126
0
    {
127
0
        if (group.HasValue())
128
0
        {
129
0
            sceneInfo->currentGroup = group.Value();
130
0
        }
131
132
0
        if (scene.HasValue())
133
0
        {
134
0
            sceneInfo->currentScene = scene.Value();
135
0
        }
136
137
0
        if (sceneValid.HasValue())
138
0
        {
139
0
            sceneInfo->sceneValid = sceneValid.Value();
140
0
        }
141
142
0
        ReturnErrorOnFailure(sceneTable->GetFabricSceneCount(fabric, sceneInfo->sceneCount));
143
0
        ReturnErrorOnFailure(sceneTable->GetRemainingCapacity(fabric, sceneInfo->remainingCapacity));
144
0
    }
145
0
    else
146
0
    {
147
        // If we couldn't find a SceneInfoStruct for the fabric, create one
148
0
        Structs::SceneInfoStruct::Type newSceneInfo;
149
0
        newSceneInfo.fabricIndex = fabric;
150
151
0
        newSceneInfo.currentGroup = group.ValueOr(0);
152
0
        newSceneInfo.currentScene = scene.ValueOr(0);
153
0
        newSceneInfo.sceneValid   = sceneValid.ValueOr(false);
154
155
0
        ReturnErrorOnFailure(sceneTable->GetFabricSceneCount(fabric, newSceneInfo.sceneCount));
156
0
        ReturnErrorOnFailure(sceneTable->GetRemainingCapacity(fabric, newSceneInfo.remainingCapacity));
157
0
        ReturnErrorOnFailure(ScenesServer::Instance().SetSceneInfoStruct(endpoint, fabric, newSceneInfo));
158
0
    }
159
160
0
    MatterReportingAttributeChangeCallback(endpoint, Id, Attributes::FabricSceneInfo::Id);
161
0
    return CHIP_NO_ERROR;
162
0
}
163
164
} // namespace
165
166
/// @brief Gets the SceneInfoStruct array associated to an endpoint
167
/// @param endpoint target endpoint
168
/// @return Optional with no value not found, Span of SceneInfoStruct
169
Span<Structs::SceneInfoStruct::Type> ScenesServer::FabricSceneInfo::GetFabricSceneInfo(EndpointId endpoint)
170
0
{
171
0
    size_t endpointIndex = 0;
172
0
    Span<Structs::SceneInfoStruct::Type> fabricSceneInfoSpan;
173
0
    CHIP_ERROR status = FindFabricSceneInfoIndex(endpoint, endpointIndex);
174
0
    if (CHIP_NO_ERROR == status)
175
0
    {
176
0
        fabricSceneInfoSpan =
177
0
            Span<Structs::SceneInfoStruct::Type>(&mSceneInfoStructs[endpointIndex][0], mSceneInfoStructsCount[endpointIndex]);
178
0
    }
179
0
    return fabricSceneInfoSpan;
180
0
}
181
182
/// @brief Gets the SceneInfoStruct for a specific fabric for a specific endpoint
183
/// @param endpoint target endpoint
184
/// @param fabric target fabric
185
/// @param index
186
/// @return Nullptr if not found, pointer to the SceneInfoStruct otherwise
187
Structs::SceneInfoStruct::Type * ScenesServer::FabricSceneInfo::GetSceneInfoStruct(EndpointId endpoint, FabricIndex fabric)
188
0
{
189
0
    size_t endpointIndex = 0;
190
0
    VerifyOrReturnValue(CHIP_NO_ERROR == FindFabricSceneInfoIndex(endpoint, endpointIndex), nullptr);
191
0
    uint8_t sceneInfoStructIndex = 0;
192
0
    VerifyOrReturnValue(CHIP_NO_ERROR == FindSceneInfoStructIndex(fabric, endpointIndex, sceneInfoStructIndex), nullptr);
193
194
0
    return &mSceneInfoStructs[endpointIndex][sceneInfoStructIndex];
195
0
}
196
197
/// @brief Sets the SceneInfoStruct for a specific fabric for a specific endpoint
198
/// @param endpoint target endpoint
199
/// @param fabric target fabric
200
/// @param [in] sceneInfoStruct SceneInfoStruct to set
201
/// @return CHIP_NO_ERROR, CHIP_ERROR_NOT_FOUND if the endpoint is not found, CHIP_ERROR_NO_MEMORY if the number of fabrics is
202
/// exceeded, CHIP_ERROR_INVALID_ARGUMENT if invalid fabric or endpoint
203
CHIP_ERROR ScenesServer::FabricSceneInfo::SetSceneInfoStruct(EndpointId endpoint, FabricIndex fabric,
204
                                                             Structs::SceneInfoStruct::Type & sceneInfoStruct)
205
0
{
206
0
    VerifyOrReturnError(kInvalidEndpointId != endpoint, CHIP_ERROR_INVALID_ARGUMENT);
207
0
    VerifyOrReturnError(kUndefinedFabricIndex != fabric, CHIP_ERROR_INVALID_ARGUMENT);
208
209
0
    size_t endpointIndex = 0;
210
0
    ReturnErrorOnFailure(FindFabricSceneInfoIndex(endpoint, endpointIndex));
211
0
    uint8_t sceneInfoStructIndex = 0;
212
0
    if (CHIP_ERROR_NOT_FOUND == FindSceneInfoStructIndex(fabric, endpointIndex, sceneInfoStructIndex))
213
0
    {
214
0
        VerifyOrReturnError(mSceneInfoStructsCount[endpointIndex] < MATTER_ARRAY_SIZE(mSceneInfoStructs[endpointIndex]),
215
0
                            CHIP_ERROR_NO_MEMORY);
216
0
        sceneInfoStructIndex = mSceneInfoStructsCount[endpointIndex];
217
218
        // Increment number of populated ScenesInfoStructs
219
0
        mSceneInfoStructsCount[endpointIndex]++;
220
0
    }
221
0
    mSceneInfoStructs[endpointIndex][sceneInfoStructIndex] = sceneInfoStruct;
222
223
0
    return CHIP_NO_ERROR;
224
0
}
225
226
/// @brief Clears the SceneInfoStruct associated to a fabric and compresses the array to leave uninitialised structs at the end
227
/// @param[in] endpoint target endpoint
228
/// @param[in] fabric target fabric
229
void ScenesServer::FabricSceneInfo::ClearSceneInfoStruct(EndpointId endpoint, FabricIndex fabric)
230
0
{
231
0
    size_t endpointIndex = 0;
232
0
    ReturnOnFailure(FindFabricSceneInfoIndex(endpoint, endpointIndex));
233
0
    uint8_t sceneInfoStructIndex = 0;
234
0
    ReturnOnFailure(FindSceneInfoStructIndex(fabric, endpointIndex, sceneInfoStructIndex));
235
236
0
    uint8_t nextIndex = static_cast<uint8_t>(sceneInfoStructIndex + 1);
237
0
    uint8_t moveNum   = static_cast<uint8_t>(MATTER_ARRAY_SIZE(mSceneInfoStructs[endpointIndex]) - nextIndex);
238
    // Compress the endpoint's SceneInfoStruct array
239
0
    if (moveNum)
240
0
    {
241
0
        for (size_t i = 0; i < moveNum; ++i)
242
0
        {
243
0
            mSceneInfoStructs[endpointIndex][sceneInfoStructIndex + i] = mSceneInfoStructs[endpointIndex][nextIndex + i];
244
0
        }
245
0
    }
246
247
    // Decrement the SceneInfoStruct count
248
0
    mSceneInfoStructsCount[endpointIndex]--;
249
250
    // Clear the last populated SceneInfoStruct
251
0
    mSceneInfoStructs[endpointIndex][mSceneInfoStructsCount[endpointIndex]].fabricIndex       = kUndefinedFabricIndex;
252
0
    mSceneInfoStructs[endpointIndex][mSceneInfoStructsCount[endpointIndex]].sceneCount        = 0;
253
0
    mSceneInfoStructs[endpointIndex][mSceneInfoStructsCount[endpointIndex]].currentScene      = 0;
254
0
    mSceneInfoStructs[endpointIndex][mSceneInfoStructsCount[endpointIndex]].currentGroup      = 0;
255
0
    mSceneInfoStructs[endpointIndex][mSceneInfoStructsCount[endpointIndex]].remainingCapacity = 0;
256
0
}
257
258
/// @brief Returns the index of the FabricSceneInfo associated to an endpoint
259
/// @param[in] endpoint target endpoint
260
/// @param[out] endpointIndex index of the corresponding FabricSceneInfo for an endpoint, corresponds to a row in the
261
/// mSceneInfoStructs array,
262
/// @return CHIP_NO_ERROR or CHIP_ERROR_NOT_FOUND, CHIP_ERROR_INVALID_ARGUMENT if invalid endpoint
263
CHIP_ERROR ScenesServer::FabricSceneInfo::FindFabricSceneInfoIndex(EndpointId endpoint, size_t & endpointIndex)
264
0
{
265
0
    VerifyOrReturnError(kInvalidEndpointId != endpoint, CHIP_ERROR_INVALID_ARGUMENT);
266
267
0
    uint16_t index =
268
0
        emberAfGetClusterServerEndpointIndex(endpoint, ScenesManagement::Id, MATTER_DM_SCENES_CLUSTER_SERVER_ENDPOINT_COUNT);
269
270
0
    if (index < MATTER_ARRAY_SIZE(mSceneInfoStructs))
271
0
    {
272
0
        endpointIndex = index;
273
0
        return CHIP_NO_ERROR;
274
0
    }
275
0
    return CHIP_ERROR_NOT_FOUND;
276
0
}
277
278
/// @brief Returns the SceneInfoStruct associated to a fabric
279
/// @param[in] fabric target fabric index
280
/// @param[in] endpointIndex index of the corresponding FabricSceneInfo for an endpoint, corresponds to a row in the
281
/// mSceneInfoStructs array
282
/// @param[out] index index of the corresponding SceneInfoStruct if found, otherwise the index value will be invalid and
283
/// should not be used. This is safe to store in a uint8_t because the index is guaranteed to be smaller than
284
/// CHIP_CONFIG_MAX_FABRICS.
285
/// @return CHIP_NO_ERROR or CHIP_ERROR_NOT_FOUND, CHIP_ERROR_INVALID_ARGUMENT if invalid fabric or endpointIndex are provided
286
CHIP_ERROR ScenesServer::FabricSceneInfo::FindSceneInfoStructIndex(FabricIndex fabric, size_t endpointIndex, uint8_t & index)
287
0
{
288
0
    VerifyOrReturnError(endpointIndex < MATTER_ARRAY_SIZE(mSceneInfoStructs), CHIP_ERROR_INVALID_ARGUMENT);
289
0
    VerifyOrReturnError(kUndefinedFabricIndex != fabric, CHIP_ERROR_INVALID_ARGUMENT);
290
291
0
    index = 0;
292
293
0
    for (auto & info : mSceneInfoStructs[endpointIndex])
294
0
    {
295
0
        if (info.fabricIndex == fabric)
296
0
        {
297
0
            return CHIP_NO_ERROR;
298
0
        }
299
0
        index++;
300
0
    }
301
302
0
    return CHIP_ERROR_NOT_FOUND;
303
0
}
304
305
ScenesServer ScenesServer::mInstance;
306
307
ScenesServer & ScenesServer::Instance()
308
6
{
309
6
    return mInstance;
310
6
}
311
0
void ReportAttributeOnAllEndpoints(AttributeId attribute) {}
312
313
class ScenesClusterFabricDelegate : public chip::FabricTable::Delegate
314
{
315
    void OnFabricRemoved(const FabricTable & fabricTable, FabricIndex fabricIndex) override
316
0
    {
317
0
        SceneTable * sceneTable = scenes::GetSceneTableImpl();
318
0
        VerifyOrReturn(nullptr != sceneTable);
319
        // The implementation of SceneTable::RemoveFabric() must not call back into the FabricTable
320
0
        sceneTable->RemoveFabric(fabricIndex);
321
0
    }
322
};
323
324
static ScenesClusterFabricDelegate gFabricDelegate;
325
326
CHIP_ERROR ScenesServer::Init()
327
1
{
328
    // Prevents re-initializing
329
1
    VerifyOrReturnError(!mIsInitialized, CHIP_ERROR_INCORRECT_STATE);
330
331
1
    ReturnErrorOnFailure(CommandHandlerInterfaceRegistry::Instance().RegisterCommandHandler(this));
332
1
    VerifyOrReturnError(AttributeAccessInterfaceRegistry::Instance().Register(this), CHIP_ERROR_INCORRECT_STATE);
333
1
    mGroupProvider = Credentials::GetGroupDataProvider();
334
335
1
    SceneTable * sceneTable = scenes::GetSceneTableImpl();
336
1
    ReturnErrorOnFailure(sceneTable->Init(Server::GetInstance().GetPersistentStorage()));
337
1
    ReturnErrorOnFailure(Server::GetInstance().GetFabricTable().AddFabricDelegate(&gFabricDelegate));
338
339
1
    mIsInitialized = true;
340
1
    return CHIP_NO_ERROR;
341
1
}
342
343
void ScenesServer::Shutdown()
344
0
{
345
0
    Server::GetInstance().GetFabricTable().RemoveFabricDelegate(&gFabricDelegate);
346
0
    CommandHandlerInterfaceRegistry::Instance().UnregisterCommandHandler(this);
347
0
    AttributeAccessInterfaceRegistry::Instance().Unregister(this);
348
349
0
    mGroupProvider = nullptr;
350
0
    mIsInitialized = false;
351
0
}
352
353
template <typename CommandData, typename ResponseType>
354
void AddSceneParse(CommandHandlerInterface::HandlerContext & ctx, const CommandData & req, GroupDataProvider * groupProvider)
355
0
{
356
0
    ResponseType response;
357
0
    uint16_t endpointTableSize = 0;
358
359
0
    ReturnOnFailure(
360
0
        AddResponseOnError(ctx, response, Attributes::SceneTableSize::Get(ctx.mRequestPath.mEndpointId, &endpointTableSize)));
361
362
    // Get Scene Table Instance
363
0
    SceneTable * sceneTable = scenes::GetSceneTableImpl(ctx.mRequestPath.mEndpointId, endpointTableSize);
364
365
    // Response data
366
0
    response.groupID = req.groupID;
367
0
    response.sceneID = req.sceneID;
368
369
    // Verify the attributes are respecting constraints
370
0
    if (req.transitionTime > scenes::kScenesMaxTransitionTime || req.sceneName.size() > scenes::kSceneNameMaxLength ||
371
0
        req.sceneID == scenes::kUndefinedSceneId)
372
0
    {
373
0
        response.status = to_underlying(Protocols::InteractionModel::Status::ConstraintError);
374
0
        ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response);
375
0
        return;
376
0
    }
377
378
    // Verify Endpoint in group
379
0
    VerifyOrReturn(nullptr != groupProvider);
380
0
    if (0 != req.groupID &&
381
0
        !groupProvider->HasEndpoint(ctx.mCommandHandler.GetAccessingFabricIndex(), req.groupID, ctx.mRequestPath.mEndpointId))
382
0
    {
383
0
        response.status = to_underlying(Protocols::InteractionModel::Status::InvalidCommand);
384
0
        ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response);
385
0
        return;
386
0
    }
387
388
0
    uint32_t featureMap = 0;
389
0
    ReturnOnFailure(AddResponseOnError(ctx, response, Attributes::FeatureMap::Get(ctx.mRequestPath.mEndpointId, &featureMap)));
390
391
0
    SceneData storageData(CharSpan(), req.transitionTime);
392
0
    if (featureMap & to_underlying(Feature::kSceneNames))
393
0
    {
394
0
        storageData.SetName(req.sceneName);
395
0
    }
396
397
0
    auto fieldSetIter = req.extensionFieldSetStructs.begin();
398
0
    uint8_t EFSCount  = 0;
399
    // Goes through all EFS in command
400
0
    while (fieldSetIter.Next() && EFSCount < scenes::kMaxClustersPerScene)
401
0
    {
402
0
        scenes::ExtensionFieldSet tempEFS;
403
0
        tempEFS.mID = fieldSetIter.GetValue().clusterID;
404
405
0
        MutableByteSpan buff_span(tempEFS.mBytesBuffer);
406
407
        // Check if a handler is registered for the EFS's cluster
408
0
        for (auto & handler : sceneTable->mHandlerList)
409
0
        {
410
0
            if (handler.SupportsCluster(ctx.mRequestPath.mEndpointId, tempEFS.mID))
411
0
            {
412
0
                ReturnOnFailure(AddResponseOnError(
413
0
                    ctx, response, handler.SerializeAdd(ctx.mRequestPath.mEndpointId, fieldSetIter.GetValue(), buff_span)));
414
0
                break;
415
0
            }
416
0
        }
417
418
0
        static_assert(sizeof(tempEFS.mBytesBuffer) <= UINT8_MAX, "Serialized EFS number of bytes must fit in a uint8");
419
0
        tempEFS.mUsedBytes = static_cast<uint8_t>(buff_span.size());
420
421
0
        if (!tempEFS.IsEmpty())
422
0
        {
423
0
            storageData.mExtensionFieldSets.InsertFieldSet(tempEFS);
424
0
        }
425
0
    }
426
0
    ReturnOnFailure(AddResponseOnError(ctx, response, fieldSetIter.GetStatus()));
427
428
    // Create scene from data and ID
429
0
    SceneTableEntry scene(SceneStorageId(req.sceneID, req.groupID), storageData);
430
431
    // Get Capacity
432
0
    VerifyOrReturn(nullptr != sceneTable);
433
0
    uint8_t capacity = 0;
434
0
    ReturnOnFailure(AddResponseOnError(ctx, response,
435
0
                                       sceneTable->GetRemainingCapacity(ctx.mCommandHandler.GetAccessingFabricIndex(), capacity)));
436
437
0
    if (0 == capacity)
438
0
    {
439
0
        response.status = to_underlying(Protocols::InteractionModel::Status::ResourceExhausted);
440
0
        ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response);
441
0
        return;
442
0
    }
443
444
    //  Insert in table
445
0
    ReturnOnFailure(
446
0
        AddResponseOnError(ctx, response, sceneTable->SetSceneTableEntry(ctx.mCommandHandler.GetAccessingFabricIndex(), scene)));
447
448
    // Update FabricSceneInfo
449
0
    ReturnOnFailure(
450
0
        AddResponseOnError(ctx, response,
451
0
                           UpdateFabricSceneInfo(ctx.mRequestPath.mEndpointId, ctx.mCommandHandler.GetAccessingFabricIndex(),
452
0
                                                 Optional<GroupId>(), Optional<SceneId>(), Optional<bool>())));
453
454
    // Write response
455
0
    response.status = to_underlying(Protocols::InteractionModel::Status::Success);
456
0
    ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response);
457
0
}
458
459
template <typename CommandData, typename ResponseType>
460
void ViewSceneParse(HandlerContext & ctx, const CommandData & req, GroupDataProvider * groupProvider)
461
0
{
462
0
    ResponseType response;
463
464
0
    uint16_t endpointTableSize = 0;
465
466
0
    ReturnOnFailure(
467
0
        AddResponseOnError(ctx, response, Attributes::SceneTableSize::Get(ctx.mRequestPath.mEndpointId, &endpointTableSize)));
468
469
    // Get Scene Table Instance
470
0
    SceneTable * sceneTable = scenes::GetSceneTableImpl(ctx.mRequestPath.mEndpointId, endpointTableSize);
471
472
    // Response data
473
0
    response.groupID = req.groupID;
474
0
    response.sceneID = req.sceneID;
475
476
    // Verify the attributes are respecting constraints
477
0
    if (req.sceneID == scenes::kUndefinedSceneId)
478
0
    {
479
0
        response.status = to_underlying(Protocols::InteractionModel::Status::ConstraintError);
480
0
        ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response);
481
0
        return;
482
0
    }
483
484
    // Verify Endpoint in group
485
0
    VerifyOrReturn(nullptr != groupProvider);
486
0
    if (0 != req.groupID &&
487
0
        !groupProvider->HasEndpoint(ctx.mCommandHandler.GetAccessingFabricIndex(), req.groupID, ctx.mRequestPath.mEndpointId))
488
0
    {
489
0
        response.status = to_underlying(Protocols::InteractionModel::Status::InvalidCommand);
490
0
        ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response);
491
0
        return;
492
0
    }
493
494
0
    SceneTableEntry scene;
495
496
    //  Gets the scene from the table
497
0
    ReturnOnFailure(AddResponseOnError(ctx, response,
498
0
                                       sceneTable->GetSceneTableEntry(ctx.mCommandHandler.GetAccessingFabricIndex(),
499
0
                                                                      SceneStorageId(req.sceneID, req.groupID), scene)));
500
501
    // Response Extension Field Sets buffer
502
0
    Structs::ExtensionFieldSetStruct::Type responseEFSBuffer[scenes::kMaxClustersPerScene];
503
0
    uint8_t deserializedEFSCount = 0;
504
505
    // Adds extension field sets to the scene
506
0
    for (uint8_t i = 0; i < scene.mStorageData.mExtensionFieldSets.GetFieldSetCount(); i++)
507
0
    {
508
        // gets data from the field in the scene
509
0
        ExtensionFieldSet tempField;
510
0
        scene.mStorageData.mExtensionFieldSets.GetFieldSetAtPosition(tempField, i);
511
0
        ByteSpan efsSpan(tempField.mBytesBuffer, tempField.mUsedBytes);
512
513
        // This should only find one handle per cluster
514
0
        for (auto & handler : sceneTable->mHandlerList)
515
0
        {
516
0
            if (handler.SupportsCluster(ctx.mRequestPath.mEndpointId, tempField.mID))
517
0
            {
518
0
                ReturnOnFailure(AddResponseOnError(
519
0
                    ctx, response,
520
0
                    handler.Deserialize(ctx.mRequestPath.mEndpointId, tempField.mID, efsSpan, responseEFSBuffer[i])));
521
0
                deserializedEFSCount++;
522
0
                break;
523
0
            }
524
0
        }
525
0
    }
526
527
0
    response.status = to_underlying(Protocols::InteractionModel::Status::Success);
528
0
    response.transitionTime.SetValue(scene.mStorageData.mSceneTransitionTimeMs);
529
530
0
    response.sceneName.SetValue(CharSpan(scene.mStorageData.mName, scene.mStorageData.mNameLength));
531
0
    Span<Structs::ExtensionFieldSetStruct::Type> responseEFSSpan(responseEFSBuffer, deserializedEFSCount);
532
0
    response.extensionFieldSetStructs.SetValue(responseEFSSpan);
533
534
0
    ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response);
535
0
}
536
537
CHIP_ERROR StoreSceneParse(const FabricIndex & fabricIdx, const EndpointId & endpointID, const GroupId & groupID,
538
                           const SceneId & sceneID, GroupDataProvider * groupProvider)
539
0
{
540
    // Make the current fabric's SceneValid false before storing a scene
541
0
    ScenesServer::Instance().MakeSceneInvalid(endpointID, fabricIdx);
542
543
0
    uint16_t endpointTableSize = 0;
544
0
    ReturnErrorOnFailure(StatusIB(Attributes::SceneTableSize::Get(endpointID, &endpointTableSize)).ToChipError());
545
546
    // Get Scene Table Instance
547
0
    SceneTable * sceneTable = scenes::GetSceneTableImpl(endpointID, endpointTableSize);
548
549
    // Verify Endpoint in group
550
0
    VerifyOrReturnError(nullptr != groupProvider, CHIP_ERROR_INTERNAL);
551
0
    if (0 != groupID && !groupProvider->HasEndpoint(fabricIdx, groupID, endpointID))
552
0
    {
553
0
        return CHIP_IM_GLOBAL_STATUS(InvalidCommand);
554
0
    }
555
556
    // Scene Table interface data
557
0
    SceneTableEntry scene(SceneStorageId(sceneID, groupID));
558
559
0
    VerifyOrReturnError(nullptr != sceneTable, CHIP_ERROR_INTERNAL);
560
0
    CHIP_ERROR err = sceneTable->GetSceneTableEntry(fabricIdx, scene.mStorageId, scene);
561
0
    if (CHIP_NO_ERROR != err && CHIP_ERROR_NOT_FOUND != err)
562
0
    {
563
0
        return err;
564
0
    }
565
566
0
    if (CHIP_ERROR_NOT_FOUND == err)
567
0
    {
568
0
        scene.mStorageData.SetName(CharSpan());
569
0
        scene.mStorageData.mSceneTransitionTimeMs = 0;
570
0
    }
571
0
    else
572
0
    {
573
0
        uint32_t featureMap = 0;
574
0
        ReturnErrorOnFailure(StatusIB(Attributes::FeatureMap::Get(endpointID, &featureMap)).ToChipError());
575
        // Check if we still support scenes name in case an OTA changed that, if we don't, set name to empty
576
0
        if (!(featureMap & to_underlying(Feature::kSceneNames)))
577
0
        {
578
0
            scene.mStorageData.SetName(CharSpan());
579
0
        }
580
0
        scene.mStorageData.mExtensionFieldSets.Clear();
581
0
    }
582
583
    // Gets the EFS
584
0
    ReturnErrorOnFailure(sceneTable->SceneSaveEFS(scene));
585
    // Insert in Scene Table
586
0
    ReturnErrorOnFailure(sceneTable->SetSceneTableEntry(fabricIdx, scene));
587
588
    // Update SceneInfo Attribute
589
0
    ReturnErrorOnFailure(UpdateFabricSceneInfo(endpointID, fabricIdx, MakeOptional(groupID), MakeOptional(sceneID),
590
0
                                               MakeOptional(static_cast<bool>(true))));
591
592
0
    return CHIP_NO_ERROR;
593
0
}
594
595
CHIP_ERROR RecallSceneParse(const FabricIndex & fabricIdx, const EndpointId & endpointID, const GroupId & groupID,
596
                            const SceneId & sceneID, const Optional<DataModel::Nullable<uint32_t>> & transitionTime,
597
                            GroupDataProvider * groupProvider)
598
0
{
599
    // Make SceneValid false for all fabrics before recalling a scene
600
0
    ScenesServer::Instance().MakeSceneInvalidForAllFabrics(endpointID);
601
602
0
    uint16_t endpointTableSize = 0;
603
0
    ReturnErrorOnFailure(StatusIB(Attributes::SceneTableSize::Get(endpointID, &endpointTableSize)).ToChipError());
604
605
    // Get Scene Table Instance
606
0
    SceneTable * sceneTable = scenes::GetSceneTableImpl(endpointID, endpointTableSize);
607
608
    // Verify Endpoint in group
609
0
    VerifyOrReturnError(nullptr != groupProvider, CHIP_ERROR_INTERNAL);
610
0
    if (0 != groupID && !groupProvider->HasEndpoint(fabricIdx, groupID, endpointID))
611
0
    {
612
0
        return CHIP_IM_GLOBAL_STATUS(InvalidCommand);
613
0
    }
614
615
    // Scene Table interface data
616
0
    SceneTableEntry scene(SceneStorageId(sceneID, groupID));
617
618
0
    VerifyOrReturnError(nullptr != sceneTable, CHIP_ERROR_INTERNAL);
619
0
    ReturnErrorOnFailure(sceneTable->GetSceneTableEntry(fabricIdx, scene.mStorageId, scene));
620
621
    // Check for optional
622
0
    if (transitionTime.HasValue())
623
0
    {
624
        // Check for nullable
625
0
        if (!transitionTime.Value().IsNull())
626
0
        {
627
0
            scene.mStorageData.mSceneTransitionTimeMs = transitionTime.Value().Value();
628
0
        }
629
0
    }
630
631
0
    ReturnErrorOnFailure(sceneTable->SceneApplyEFS(scene));
632
633
    // Update FabricSceneInfo, at this point the scene is considered valid
634
0
    ReturnErrorOnFailure(
635
0
        UpdateFabricSceneInfo(endpointID, fabricIdx, Optional<GroupId>(groupID), Optional<SceneId>(sceneID), Optional<bool>(true)));
636
637
0
    return CHIP_NO_ERROR;
638
0
}
639
640
// CommandHanlerInterface
641
void ScenesServer::InvokeCommand(HandlerContext & ctxt)
642
0
{
643
0
    switch (ctxt.mRequestPath.mCommandId)
644
0
    {
645
0
    case Commands::AddScene::Id:
646
0
        HandleCommand<Commands::AddScene::DecodableType>(
647
0
            ctxt, [this](HandlerContext & ctx, const auto & req) { HandleAddScene(ctx, req); });
648
0
        return;
649
0
    case Commands::ViewScene::Id:
650
0
        HandleCommand<Commands::ViewScene::DecodableType>(
651
0
            ctxt, [this](HandlerContext & ctx, const auto & req) { HandleViewScene(ctx, req); });
652
0
        return;
653
0
    case Commands::RemoveScene::Id:
654
0
        HandleCommand<Commands::RemoveScene::DecodableType>(
655
0
            ctxt, [this](HandlerContext & ctx, const auto & req) { HandleRemoveScene(ctx, req); });
656
0
        return;
657
0
    case Commands::RemoveAllScenes::Id:
658
0
        HandleCommand<Commands::RemoveAllScenes::DecodableType>(
659
0
            ctxt, [this](HandlerContext & ctx, const auto & req) { HandleRemoveAllScenes(ctx, req); });
660
0
        return;
661
0
    case Commands::StoreScene::Id:
662
0
        HandleCommand<Commands::StoreScene::DecodableType>(
663
0
            ctxt, [this](HandlerContext & ctx, const auto & req) { HandleStoreScene(ctx, req); });
664
0
        return;
665
0
    case Commands::RecallScene::Id:
666
0
        HandleCommand<Commands::RecallScene::DecodableType>(
667
0
            ctxt, [this](HandlerContext & ctx, const auto & req) { HandleRecallScene(ctx, req); });
668
0
        return;
669
0
    case Commands::GetSceneMembership::Id:
670
0
        HandleCommand<Commands::GetSceneMembership::DecodableType>(
671
0
            ctxt, [this](HandlerContext & ctx, const auto & req) { HandleGetSceneMembership(ctx, req); });
672
0
        return;
673
0
    case Commands::CopyScene::Id:
674
0
        HandleCommand<Commands::CopyScene::DecodableType>(
675
0
            ctxt, [this](HandlerContext & ctx, const auto & req) { HandleCopyScene(ctx, req); });
676
0
        return;
677
0
    }
678
0
}
679
680
// AttributeAccessInterface
681
CHIP_ERROR ScenesServer::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder)
682
0
{
683
0
    uint16_t endpointTableSize = 0;
684
0
    ReturnErrorOnFailure(StatusIB(Attributes::SceneTableSize::Get(aPath.mEndpointId, &endpointTableSize)).ToChipError());
685
686
    // Get Scene Table Instance
687
0
    SceneTable * sceneTable = scenes::GetSceneTableImpl(aPath.mEndpointId, endpointTableSize);
688
689
0
    switch (aPath.mAttributeId)
690
0
    {
691
0
    case Attributes::FabricSceneInfo::Id: {
692
0
        return aEncoder.EncodeList([&, sceneTable](const auto & encoder) -> CHIP_ERROR {
693
0
            Span<Structs::SceneInfoStruct::Type> fabricSceneInfoSpan = mFabricSceneInfo.GetFabricSceneInfo(aPath.mEndpointId);
694
0
            for (auto & info : fabricSceneInfoSpan)
695
0
            {
696
                // Update the SceneInfoStruct's Capacity in case it's capacity was limited by other fabrics
697
0
                sceneTable->GetRemainingCapacity(info.fabricIndex, info.remainingCapacity);
698
0
                ReturnErrorOnFailure(encoder.Encode(info));
699
0
            }
700
0
            return CHIP_NO_ERROR;
701
0
        });
702
0
    }
703
0
    default:
704
0
        return CHIP_NO_ERROR;
705
0
    }
706
0
}
707
708
Structs::SceneInfoStruct::Type * ScenesServer::GetSceneInfoStruct(EndpointId endpoint, FabricIndex fabric)
709
0
{
710
0
    Structs::SceneInfoStruct::Type * sceneInfoStruct = mFabricSceneInfo.GetSceneInfoStruct(endpoint, fabric);
711
0
    return sceneInfoStruct;
712
0
}
713
714
CHIP_ERROR ScenesServer::SetSceneInfoStruct(EndpointId endpoint, FabricIndex fabric,
715
                                            Structs::SceneInfoStruct::Type & sceneInfoStruct)
716
0
{
717
0
    ReturnErrorOnFailure(mFabricSceneInfo.SetSceneInfoStruct(endpoint, fabric, sceneInfoStruct));
718
0
    return CHIP_NO_ERROR;
719
0
}
720
721
void ScenesServer::GroupWillBeRemoved(FabricIndex aFabricIx, EndpointId aEndpointId, GroupId aGroupId)
722
0
{
723
    // Get Scene Table Instance
724
0
    SceneTable * sceneTable = scenes::GetSceneTableImpl(aEndpointId);
725
0
    VerifyOrReturn(nullptr != sceneTable);
726
727
0
    Structs::SceneInfoStruct::Type * sceneInfo = mFabricSceneInfo.GetSceneInfoStruct(aEndpointId, aFabricIx);
728
0
    chip::GroupId currentGroup                 = (nullptr != sceneInfo) ? sceneInfo->currentGroup : 0x0000;
729
730
    // If currentGroup is what is being removed, we can't possibly still have a valid scene,
731
    // because the scene we have (if any) will also be removed.
732
0
    if (aGroupId == currentGroup)
733
0
    {
734
0
        MakeSceneInvalid(aEndpointId, aFabricIx);
735
0
    }
736
737
0
    VerifyOrReturn(nullptr != mGroupProvider);
738
0
    if (0 != aGroupId && !mGroupProvider->HasEndpoint(aFabricIx, aGroupId, aEndpointId))
739
0
    {
740
0
        return;
741
0
    }
742
743
0
    sceneTable->DeleteAllScenesInGroup(aFabricIx, aGroupId);
744
0
}
745
746
void ScenesServer::MakeSceneInvalid(EndpointId aEndpointId, FabricIndex aFabricIx)
747
0
{
748
0
    UpdateFabricSceneInfo(aEndpointId, aFabricIx, Optional<GroupId>(), Optional<SceneId>(), Optional<bool>(false));
749
0
}
750
751
void ScenesServer::MakeSceneInvalidForAllFabrics(EndpointId aEndpointId)
752
0
{
753
0
    for (auto & info : chip::Server::GetInstance().GetFabricTable())
754
0
    {
755
0
        MakeSceneInvalid(aEndpointId, info.GetFabricIndex());
756
0
    }
757
0
}
758
759
void ScenesServer::StoreCurrentScene(FabricIndex aFabricIx, EndpointId aEndpointId, GroupId aGroupId, SceneId aSceneId)
760
0
{
761
0
    StoreSceneParse(aFabricIx, aEndpointId, aGroupId, aSceneId, mGroupProvider);
762
0
}
763
void ScenesServer::RecallScene(FabricIndex aFabricIx, EndpointId aEndpointId, GroupId aGroupId, SceneId aSceneId)
764
0
{
765
0
    Optional<DataModel::Nullable<uint32_t>> transitionTime;
766
767
0
    RecallSceneParse(aFabricIx, aEndpointId, aGroupId, aSceneId, transitionTime, mGroupProvider);
768
0
}
769
770
bool ScenesServer::IsHandlerRegistered(EndpointId aEndpointId, scenes::SceneHandler * handler)
771
5
{
772
5
    SceneTable * sceneTable = scenes::GetSceneTableImpl(aEndpointId);
773
5
    return sceneTable->mHandlerList.Contains(handler);
774
5
}
775
776
void ScenesServer::RegisterSceneHandler(EndpointId aEndpointId, scenes::SceneHandler * handler)
777
5
{
778
5
    SceneTable * sceneTable = scenes::GetSceneTableImpl(aEndpointId);
779
780
5
    if (!IsHandlerRegistered(aEndpointId, handler))
781
4
    {
782
4
        sceneTable->RegisterHandler(handler);
783
4
    }
784
5
}
785
786
void ScenesServer::UnregisterSceneHandler(EndpointId aEndpointId, scenes::SceneHandler * handler)
787
0
{
788
0
    SceneTable * sceneTable = scenes::GetSceneTableImpl(aEndpointId);
789
790
0
    if (IsHandlerRegistered(aEndpointId, handler))
791
0
    {
792
0
        sceneTable->UnregisterHandler(handler);
793
0
    }
794
0
}
795
796
void ScenesServer::RemoveFabric(EndpointId aEndpointId, FabricIndex aFabricIndex)
797
0
{
798
0
    SceneTable * sceneTable = scenes::GetSceneTableImpl(aEndpointId);
799
0
    sceneTable->RemoveFabric(aFabricIndex);
800
0
    mFabricSceneInfo.ClearSceneInfoStruct(aEndpointId, aFabricIndex);
801
0
}
802
803
void ScenesServer::HandleAddScene(HandlerContext & ctx, const Commands::AddScene::DecodableType & req)
804
0
{
805
0
    MATTER_TRACE_SCOPE("AddScene", "Scenes");
806
0
    AddSceneParse<Commands::AddScene::DecodableType, Commands::AddSceneResponse::Type>(ctx, req, mGroupProvider);
807
0
}
808
809
void ScenesServer::HandleViewScene(HandlerContext & ctx, const Commands::ViewScene::DecodableType & req)
810
0
{
811
0
    MATTER_TRACE_SCOPE("ViewScene", "Scenes");
812
0
    ViewSceneParse<Commands::ViewScene::DecodableType, Commands::ViewSceneResponse::Type>(ctx, req, mGroupProvider);
813
0
}
814
815
void ScenesServer::HandleRemoveScene(HandlerContext & ctx, const Commands::RemoveScene::DecodableType & req)
816
0
{
817
0
    MATTER_TRACE_SCOPE("RemoveScene", "Scenes");
818
0
    Commands::RemoveSceneResponse::Type response;
819
820
0
    uint16_t endpointTableSize = 0;
821
0
    ReturnOnFailure(
822
0
        AddResponseOnError(ctx, response, Attributes::SceneTableSize::Get(ctx.mRequestPath.mEndpointId, &endpointTableSize)));
823
824
    // Get Scene Table Instance
825
0
    SceneTable * sceneTable = scenes::GetSceneTableImpl(ctx.mRequestPath.mEndpointId, endpointTableSize);
826
827
    // Response data
828
0
    response.groupID = req.groupID;
829
0
    response.sceneID = req.sceneID;
830
831
    // Verify the attributes are respecting constraints
832
0
    if (req.sceneID == scenes::kUndefinedSceneId)
833
0
    {
834
0
        response.status = to_underlying(Protocols::InteractionModel::Status::ConstraintError);
835
0
        ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response);
836
0
        return;
837
0
    }
838
839
    // Scene Table interface data
840
0
    SceneTableEntry scene(SceneStorageId(req.sceneID, req.groupID));
841
842
    // Verify Endpoint in group
843
0
    VerifyOrReturn(nullptr != mGroupProvider);
844
0
    if (0 != req.groupID &&
845
0
        !mGroupProvider->HasEndpoint(ctx.mCommandHandler.GetAccessingFabricIndex(), req.groupID, ctx.mRequestPath.mEndpointId))
846
0
    {
847
0
        response.status = to_underlying(Protocols::InteractionModel::Status::InvalidCommand);
848
0
        ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response);
849
0
        return;
850
0
    }
851
852
    //  Gets the scene from the table
853
0
    ReturnOnFailure(AddResponseOnError(
854
0
        ctx, response, sceneTable->GetSceneTableEntry(ctx.mCommandHandler.GetAccessingFabricIndex(), scene.mStorageId, scene)));
855
856
    // Remove the scene from the scene table
857
0
    ReturnOnFailure(AddResponseOnError(
858
0
        ctx, response, sceneTable->RemoveSceneTableEntry(ctx.mCommandHandler.GetAccessingFabricIndex(), scene.mStorageId)));
859
860
    // Update SceneInfoStruct Attributes
861
0
    Structs::SceneInfoStruct::Type * sceneInfo =
862
0
        GetSceneInfoStruct(ctx.mRequestPath.mEndpointId, ctx.mCommandHandler.GetAccessingFabricIndex());
863
0
    Optional<bool> sceneValid;
864
0
    if (nullptr != sceneInfo && req.groupID == sceneInfo->currentGroup && req.sceneID == sceneInfo->currentScene)
865
0
    {
866
0
        sceneValid.Emplace(false);
867
0
    }
868
869
0
    ReturnOnFailure(
870
0
        AddResponseOnError(ctx, response,
871
0
                           UpdateFabricSceneInfo(ctx.mRequestPath.mEndpointId, ctx.mCommandHandler.GetAccessingFabricIndex(),
872
0
                                                 Optional<GroupId>(), Optional<SceneId>(), sceneValid)));
873
874
    // Write response
875
0
    response.status = to_underlying(Protocols::InteractionModel::Status::Success);
876
0
    ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response);
877
0
}
878
879
void ScenesServer::HandleRemoveAllScenes(HandlerContext & ctx, const Commands::RemoveAllScenes::DecodableType & req)
880
0
{
881
0
    MATTER_TRACE_SCOPE("RemoveAllScenes", "Scenes");
882
0
    Commands::RemoveAllScenesResponse::Type response;
883
884
0
    uint16_t endpointTableSize = 0;
885
0
    ReturnOnFailure(
886
0
        AddResponseOnError(ctx, response, Attributes::SceneTableSize::Get(ctx.mRequestPath.mEndpointId, &endpointTableSize)));
887
888
    // Get Scene Table Instance
889
0
    SceneTable * sceneTable = scenes::GetSceneTableImpl(ctx.mRequestPath.mEndpointId, endpointTableSize);
890
891
    // Response data
892
0
    response.groupID = req.groupID;
893
894
    // Verify Endpoint in group
895
0
    VerifyOrReturn(nullptr != mGroupProvider);
896
0
    if (0 != req.groupID &&
897
0
        !mGroupProvider->HasEndpoint(ctx.mCommandHandler.GetAccessingFabricIndex(), req.groupID, ctx.mRequestPath.mEndpointId))
898
0
    {
899
0
        response.status = to_underlying(Protocols::InteractionModel::Status::InvalidCommand);
900
0
        ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response);
901
0
        return;
902
0
    }
903
904
0
    ReturnOnFailure(AddResponseOnError(
905
0
        ctx, response, sceneTable->DeleteAllScenesInGroup(ctx.mCommandHandler.GetAccessingFabricIndex(), req.groupID)));
906
907
    // Update Attributes
908
0
    Structs::SceneInfoStruct::Type * sceneInfo =
909
0
        GetSceneInfoStruct(ctx.mRequestPath.mEndpointId, ctx.mCommandHandler.GetAccessingFabricIndex());
910
911
0
    Optional<bool> sceneValid;
912
0
    if (nullptr != sceneInfo && req.groupID == sceneInfo->currentGroup)
913
0
    {
914
0
        sceneValid.Emplace(false);
915
0
    }
916
917
0
    ReturnOnFailure(
918
0
        AddResponseOnError(ctx, response,
919
0
                           UpdateFabricSceneInfo(ctx.mRequestPath.mEndpointId, ctx.mCommandHandler.GetAccessingFabricIndex(),
920
0
                                                 Optional<GroupId>(), Optional<SceneId>(), sceneValid)));
921
922
    // Write response
923
0
    response.status = to_underlying(Protocols::InteractionModel::Status::Success);
924
0
    ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response);
925
0
}
926
927
void ScenesServer::HandleStoreScene(HandlerContext & ctx, const Commands::StoreScene::DecodableType & req)
928
0
{
929
0
    MATTER_TRACE_SCOPE("StoreScene", "Scenes");
930
0
    Commands::StoreSceneResponse::Type response;
931
932
    // Response data
933
0
    response.groupID = req.groupID;
934
0
    response.sceneID = req.sceneID;
935
936
    // Verify the attributes are respecting constraints
937
0
    if (req.sceneID == scenes::kUndefinedSceneId)
938
0
    {
939
0
        response.status = to_underlying(Protocols::InteractionModel::Status::ConstraintError);
940
0
        ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response);
941
0
        return;
942
0
    }
943
944
0
    CHIP_ERROR err = StoreSceneParse(ctx.mCommandHandler.GetAccessingFabricIndex(), ctx.mRequestPath.mEndpointId, req.groupID,
945
0
                                     req.sceneID, mGroupProvider);
946
947
0
    ReturnOnFailure(AddResponseOnError(ctx, response, err));
948
0
    response.status = to_underlying(Protocols::InteractionModel::Status::Success);
949
0
    ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response);
950
0
}
951
952
void ScenesServer::HandleRecallScene(HandlerContext & ctx, const Commands::RecallScene::DecodableType & req)
953
0
{
954
0
    MATTER_TRACE_SCOPE("RecallScene", "Scenes");
955
956
    // Verify the attributes are respecting constraints
957
0
    if (req.sceneID == scenes::kUndefinedSceneId)
958
0
    {
959
0
        ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Protocols::InteractionModel::Status::ConstraintError);
960
0
        return;
961
0
    }
962
963
0
    CHIP_ERROR err = RecallSceneParse(ctx.mCommandHandler.GetAccessingFabricIndex(), ctx.mRequestPath.mEndpointId, req.groupID,
964
0
                                      req.sceneID, req.transitionTime, mGroupProvider);
965
966
0
    if (CHIP_NO_ERROR == err)
967
0
    {
968
0
        ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Protocols::InteractionModel::Status::Success);
969
0
        return;
970
0
    }
971
972
0
    if (CHIP_ERROR_NOT_FOUND == err)
973
0
    {
974
        // TODO : implement proper mapping between CHIP_ERROR and IM Status
975
0
        ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Protocols::InteractionModel::Status::NotFound);
976
0
        return;
977
0
    }
978
979
0
    ctx.mCommandHandler.AddStatus(ctx.mRequestPath, StatusIB(err).mStatus);
980
0
}
981
982
void ScenesServer::HandleGetSceneMembership(HandlerContext & ctx, const Commands::GetSceneMembership::DecodableType & req)
983
0
{
984
0
    MATTER_TRACE_SCOPE("GetSceneMembership", "Scenes");
985
0
    Commands::GetSceneMembershipResponse::Type response;
986
987
0
    uint16_t endpointTableSize = 0;
988
0
    ReturnOnFailure(
989
0
        AddResponseOnError(ctx, response, Attributes::SceneTableSize::Get(ctx.mRequestPath.mEndpointId, &endpointTableSize)));
990
991
    // Get Scene Table Instance
992
0
    SceneTable * sceneTable = scenes::GetSceneTableImpl(ctx.mRequestPath.mEndpointId, endpointTableSize);
993
994
    // Response data
995
0
    response.groupID = req.groupID;
996
997
    // Scene Table interface data
998
0
    SceneId scenesInGroup[scenes::kMaxScenesPerFabric];
999
0
    Span<SceneId> sceneList = Span<SceneId>(scenesInGroup);
1000
0
    SceneTableEntry scene;
1001
1002
    // Verify Endpoint in group
1003
0
    VerifyOrReturn(nullptr != mGroupProvider);
1004
0
    if (0 != req.groupID &&
1005
0
        !mGroupProvider->HasEndpoint(ctx.mCommandHandler.GetAccessingFabricIndex(), req.groupID, ctx.mRequestPath.mEndpointId))
1006
0
    {
1007
0
        response.status = to_underlying(Protocols::InteractionModel::Status::InvalidCommand);
1008
0
        ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response);
1009
0
        return;
1010
0
    }
1011
1012
0
    uint8_t capacity = 0;
1013
    // Get Capacity
1014
0
    ReturnOnFailure(AddResponseOnError(ctx, response,
1015
0
                                       sceneTable->GetRemainingCapacity(ctx.mCommandHandler.GetAccessingFabricIndex(), capacity)));
1016
0
    response.capacity.SetNonNull(capacity);
1017
1018
    // populate scene list
1019
0
    ReturnOnFailure(AddResponseOnError(
1020
0
        ctx, response, sceneTable->GetAllSceneIdsInGroup(ctx.mCommandHandler.GetAccessingFabricIndex(), req.groupID, sceneList)));
1021
1022
0
    response.sceneList.SetValue(sceneList);
1023
1024
    // Write response
1025
0
    response.status = to_underlying(Protocols::InteractionModel::Status::Success);
1026
0
    ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response);
1027
0
}
1028
1029
void ScenesServer::HandleCopyScene(HandlerContext & ctx, const Commands::CopyScene::DecodableType & req)
1030
0
{
1031
0
    MATTER_TRACE_SCOPE("CopyScene", "Scenes");
1032
0
    Commands::CopySceneResponse::Type response;
1033
1034
0
    uint16_t endpointTableSize = 0;
1035
0
    ReturnOnFailure(
1036
0
        AddResponseOnError(ctx, response, Attributes::SceneTableSize::Get(ctx.mRequestPath.mEndpointId, &endpointTableSize)));
1037
1038
    // Get Scene Table Instance
1039
0
    SceneTable * sceneTable = scenes::GetSceneTableImpl(ctx.mRequestPath.mEndpointId, endpointTableSize);
1040
1041
    // Response data
1042
0
    response.groupIdentifierFrom = req.groupIdentifierFrom;
1043
0
    response.sceneIdentifierFrom = req.sceneIdentifierFrom;
1044
1045
    // Verify the attributes are respecting constraints
1046
0
    if (req.sceneIdentifierFrom == scenes::kUndefinedSceneId || req.sceneIdentifierTo == scenes::kUndefinedSceneId)
1047
0
    {
1048
0
        response.status = to_underlying(Protocols::InteractionModel::Status::ResourceExhausted);
1049
0
        ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response);
1050
0
        return;
1051
0
    }
1052
1053
    // Verify Endpoint in group
1054
0
    VerifyOrReturn(nullptr != mGroupProvider);
1055
0
    if ((0 != req.groupIdentifierFrom &&
1056
0
         !mGroupProvider->HasEndpoint(ctx.mCommandHandler.GetAccessingFabricIndex(), req.groupIdentifierFrom,
1057
0
                                      ctx.mRequestPath.mEndpointId)) ||
1058
0
        (0 != req.groupIdentifierTo &&
1059
0
         !mGroupProvider->HasEndpoint(ctx.mCommandHandler.GetAccessingFabricIndex(), req.groupIdentifierTo,
1060
0
                                      ctx.mRequestPath.mEndpointId)))
1061
0
    {
1062
0
        response.status = to_underlying(Protocols::InteractionModel::Status::InvalidCommand);
1063
0
        ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response);
1064
0
        return;
1065
0
    }
1066
1067
0
    uint8_t capacity = 0;
1068
    // Get Capacity
1069
0
    ReturnOnFailure(AddResponseOnError(ctx, response,
1070
0
                                       sceneTable->GetRemainingCapacity(ctx.mCommandHandler.GetAccessingFabricIndex(), capacity)));
1071
1072
0
    if (0 == capacity)
1073
0
    {
1074
0
        response.status = to_underlying(Protocols::InteractionModel::Status::ResourceExhausted);
1075
0
        ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response);
1076
0
        return;
1077
0
    }
1078
1079
    // Checks if we copy a single scene or all of them
1080
0
    if (req.mode.GetField(app::Clusters::ScenesManagement::CopyModeBitmap::kCopyAllScenes))
1081
0
    {
1082
        // Scene Table interface data
1083
0
        SceneId scenesInGroup[scenes::kMaxScenesPerFabric];
1084
0
        Span<SceneId> sceneList = Span<SceneId>(scenesInGroup);
1085
1086
        // populate scene list
1087
0
        ReturnOnFailure(AddResponseOnError(
1088
0
            ctx, response,
1089
0
            sceneTable->GetAllSceneIdsInGroup(ctx.mCommandHandler.GetAccessingFabricIndex(), req.groupIdentifierFrom, sceneList)));
1090
1091
0
        for (auto & sceneId : sceneList)
1092
0
        {
1093
0
            SceneTableEntry scene(SceneStorageId(sceneId, req.groupIdentifierFrom));
1094
            //  Insert in table
1095
0
            ReturnOnFailure(AddResponseOnError(
1096
0
                ctx, response,
1097
0
                sceneTable->GetSceneTableEntry(ctx.mCommandHandler.GetAccessingFabricIndex(), scene.mStorageId, scene)));
1098
1099
0
            scene.mStorageId = SceneStorageId(sceneId, req.groupIdentifierTo);
1100
1101
0
            ReturnOnFailure(AddResponseOnError(
1102
0
                ctx, response, sceneTable->SetSceneTableEntry(ctx.mCommandHandler.GetAccessingFabricIndex(), scene)));
1103
1104
            // Update SceneInfoStruct Attributes after each insert in case we hit max capacity in the middle of the loop
1105
0
            ReturnOnFailure(AddResponseOnError(
1106
0
                ctx, response,
1107
0
                UpdateFabricSceneInfo(ctx.mRequestPath.mEndpointId, ctx.mCommandHandler.GetAccessingFabricIndex(),
1108
0
                                      Optional<GroupId>(), Optional<SceneId>(), Optional<bool>() /* = sceneValid*/)));
1109
0
        }
1110
1111
0
        response.status = to_underlying(Protocols::InteractionModel::Status::Success);
1112
0
        ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response);
1113
0
        return;
1114
0
    }
1115
1116
0
    SceneTableEntry scene(SceneStorageId(req.sceneIdentifierFrom, req.groupIdentifierFrom));
1117
0
    ReturnOnFailure(AddResponseOnError(
1118
0
        ctx, response, sceneTable->GetSceneTableEntry(ctx.mCommandHandler.GetAccessingFabricIndex(), scene.mStorageId, scene)));
1119
1120
0
    scene.mStorageId = SceneStorageId(req.sceneIdentifierTo, req.groupIdentifierTo);
1121
1122
0
    ReturnOnFailure(
1123
0
        AddResponseOnError(ctx, response, sceneTable->SetSceneTableEntry(ctx.mCommandHandler.GetAccessingFabricIndex(), scene)));
1124
1125
    // Update Attributes
1126
0
    ReturnOnFailure(
1127
0
        AddResponseOnError(ctx, response,
1128
0
                           UpdateFabricSceneInfo(ctx.mRequestPath.mEndpointId, ctx.mCommandHandler.GetAccessingFabricIndex(),
1129
0
                                                 Optional<GroupId>(), Optional<SceneId>(), Optional<bool>())));
1130
1131
0
    response.status = to_underlying(Protocols::InteractionModel::Status::Success);
1132
0
    ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response);
1133
0
}
1134
1135
} // namespace ScenesManagement
1136
} // namespace Clusters
1137
} // namespace app
1138
} // namespace chip
1139
1140
using namespace chip;
1141
using namespace chip::app::Clusters;
1142
using namespace chip::app::Clusters::ScenesManagement;
1143
1144
void emberAfScenesManagementClusterServerInitCallback(EndpointId endpoint)
1145
2
{
1146
    // Initialize the FabricSceneInfo by getting the number of scenes and the remaining capacity for storing fabric scene data
1147
2
    for (auto & info : chip::Server::GetInstance().GetFabricTable())
1148
0
    {
1149
0
        auto fabric = info.GetFabricIndex();
1150
0
        UpdateFabricSceneInfo(endpoint, fabric, Optional<GroupId>(), Optional<SceneId>(), Optional<bool>());
1151
0
    }
1152
2
}
1153
1154
void MatterScenesManagementClusterServerShutdownCallback(EndpointId endpoint)
1155
0
{
1156
0
    uint16_t endpointTableSize = 0;
1157
0
    VerifyOrReturn(Status::Success == Attributes::SceneTableSize::Get(endpoint, &endpointTableSize));
1158
1159
    // Get Scene Table Instance
1160
0
    SceneTable * sceneTable = scenes::GetSceneTableImpl(endpoint, endpointTableSize);
1161
0
    sceneTable->RemoveEndpoint();
1162
0
}
1163
1164
void MatterScenesManagementPluginServerInitCallback()
1165
1
{
1166
1
    CHIP_ERROR err = ScenesServer::Instance().Init();
1167
1
    if (err != CHIP_NO_ERROR)
1168
0
    {
1169
0
        ChipLogError(Zcl, "ScenesServer::Instance().Init() error: %" CHIP_ERROR_FORMAT, err.Format());
1170
0
    }
1171
1
}
1172
1173
void MatterScenesManagementPluginServerShutdownCallback()
1174
0
{
1175
0
    ScenesServer::Instance().Shutdown();
1176
0
}