/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 | } |