/src/connectedhomeip/src/app/clusters/dishwasher-alarm-server/CodegenIntegration.cpp
Line | Count | Source |
1 | | /* |
2 | | * |
3 | | * Copyright (c) 2026 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 <app-common/zap-generated/attributes/Accessors.h> |
20 | | #include <app/clusters/alarm-base-server/alarm-base-cluster-objects.h> |
21 | | #include <app/clusters/dishwasher-alarm-server/CodegenIntegration.h> |
22 | | #include <app/clusters/dishwasher-alarm-server/DishwasherAlarmCluster.h> |
23 | | #include <app/static-cluster-config/DishwasherAlarm.h> |
24 | | #include <app/util/attribute-storage.h> |
25 | | #include <app/util/endpoint-config-api.h> |
26 | | #include <app/util/generic-callbacks.h> |
27 | | #include <data-model-providers/codegen/ClusterIntegration.h> |
28 | | #include <data-model-providers/codegen/CodegenDataModelProvider.h> |
29 | | #include <lib/support/BitFlags.h> |
30 | | #include <lib/support/CodeUtils.h> |
31 | | |
32 | | using namespace chip; |
33 | | using namespace chip::app; |
34 | | using namespace chip::app::Clusters; |
35 | | using namespace chip::app::Clusters::AlarmBase; |
36 | | using namespace chip::app::Clusters::DishwasherAlarm; |
37 | | using namespace chip::app::Clusters::DishwasherAlarm::Attributes; |
38 | | using chip::Protocols::InteractionModel::Status; |
39 | | |
40 | | namespace { |
41 | | |
42 | | constexpr size_t kDishwasherAlarmFixedClusterCount = DishwasherAlarm::StaticApplicationConfig::kFixedClusterConfig.size(); |
43 | | constexpr size_t kDishwasherAlarmMaxClusterCount = kDishwasherAlarmFixedClusterCount + CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT; |
44 | | |
45 | | static_assert(kDishwasherAlarmFixedClusterCount == MATTER_DM_DISHWASHER_ALARM_CLUSTER_SERVER_ENDPOINT_COUNT, |
46 | | "DishwasherAlarm static cluster config must match ZAP server endpoint count"); |
47 | | static_assert(kDishwasherAlarmMaxClusterCount <= kEmberInvalidEndpointIndex, "DishwasherAlarm cluster table size error"); |
48 | | |
49 | | BitMask<DishwasherAlarm::AlarmMap> ToAlarmMap(AlarmBase::AlarmMap map) |
50 | 0 | { |
51 | 0 | return BitMask<DishwasherAlarm::AlarmMap>(map.Raw()); |
52 | 0 | } |
53 | | |
54 | | AlarmBase::AlarmMap FromAlarmMap(const BitMask<DishwasherAlarm::AlarmMap> map) |
55 | 0 | { |
56 | 0 | return AlarmBase::AlarmMap(map.Raw()); |
57 | 0 | } |
58 | | |
59 | | bool EndpointHasModifyEnabledAlarmsCommand(EndpointId endpointId) |
60 | 0 | { |
61 | 0 | constexpr CommandId kCommandId = DishwasherAlarm::Commands::ModifyEnabledAlarms::Id; |
62 | 0 | const EmberAfCluster * cluster = emberAfFindServerCluster(endpointId, DishwasherAlarm::Id); |
63 | 0 | VerifyOrReturnValue(cluster != nullptr, false); |
64 | 0 | VerifyOrReturnValue(cluster->acceptedCommandList != nullptr, false); |
65 | | |
66 | 0 | for (const CommandId * cmd = cluster->acceptedCommandList; *cmd != kInvalidCommandId; cmd++) |
67 | 0 | { |
68 | 0 | if (*cmd == kCommandId) |
69 | 0 | { |
70 | 0 | return true; |
71 | 0 | } |
72 | 0 | } |
73 | 0 | return false; |
74 | 0 | } |
75 | | |
76 | | // Proxy delegate used only by the codegen integration layer. |
77 | | // |
78 | | // AlarmBaseCluster is constructed with a mandatory AlarmBase::Delegate& (no nullptr). Apps may register |
79 | | // a DishwasherAlarm::Delegate later (or never) via SetDefaultDelegate, so the cluster cannot assume an |
80 | | // application delegate exists at construction time. |
81 | | // |
82 | | // This wrapper is the object passed into AlarmBaseCluster::Config: it lives for the endpoint slot and |
83 | | // holds an optional pointer to the application delegate. When registered, command callbacks are forwarded |
84 | | // to the app. When mWrapped is null, ModifyEnabledAlarms and ResetAlarms return true so the cluster still |
85 | | // updates Matter attributes. That matches master Ember behavior (GetDelegate() == nullptr skipped the |
86 | | // callback and allowed the attribute update) and lets example/simulator apps work without product logic. |
87 | | class DishwasherAlarmIntegrationDelegateWrapper final : public AlarmBase::Delegate |
88 | | { |
89 | | public: |
90 | 0 | void SetWrapped(DishwasherAlarm::Delegate * wrapped) { mWrapped = wrapped; } |
91 | | |
92 | 0 | DishwasherAlarm::Delegate * GetWrapped() const { return mWrapped; } |
93 | | |
94 | | bool ModifyEnabledAlarms(AlarmBase::AlarmMap mask) override |
95 | 0 | { |
96 | 0 | if (mWrapped == nullptr) |
97 | 0 | { |
98 | 0 | return true; |
99 | 0 | } |
100 | 0 | return mWrapped->ModifyEnabledAlarmsCallback(BitMask<DishwasherAlarm::AlarmBitmap>(mask.Raw())); |
101 | 0 | } |
102 | | |
103 | | bool ResetAlarms(AlarmBase::AlarmMap alarms) override |
104 | 0 | { |
105 | 0 | if (mWrapped == nullptr) |
106 | 0 | { |
107 | 0 | return true; |
108 | 0 | } |
109 | 0 | return mWrapped->ResetAlarmsCallback(BitMask<DishwasherAlarm::AlarmBitmap>(alarms.Raw())); |
110 | 0 | } |
111 | | |
112 | | private: |
113 | | DishwasherAlarm::Delegate * mWrapped = nullptr; |
114 | | }; |
115 | | |
116 | | struct DishwasherAlarmClusterSlot |
117 | | { |
118 | | LazyRegisteredServerCluster<DishwasherAlarmCluster> cluster; |
119 | | DishwasherAlarmIntegrationDelegateWrapper integrationDelegateWrapper; |
120 | | }; |
121 | | |
122 | | DishwasherAlarmClusterSlot gDishwasherAlarmClusters[kDishwasherAlarmMaxClusterCount]; |
123 | | |
124 | | class DishwasherAlarmIntegrationDelegate : public CodegenClusterIntegration::Delegate |
125 | | { |
126 | | public: |
127 | | ServerClusterRegistration & CreateRegistration(EndpointId endpointId, unsigned clusterInstanceIndex, uint32_t, |
128 | | uint32_t featureMap) override |
129 | 0 | { |
130 | 0 | BitFlags<DishwasherAlarm::Feature> features(featureMap); |
131 | |
|
132 | 0 | AlarmBase::AlarmMap supported{}; |
133 | 0 | BitMask<DishwasherAlarm::AlarmBitmap> supportedDefault{}; |
134 | 0 | if (Supported::GetDefault(endpointId, supportedDefault) == Status::Success) |
135 | 0 | { |
136 | 0 | supported = AlarmBase::AlarmMap(supportedDefault.Raw()); |
137 | 0 | } |
138 | |
|
139 | 0 | AlarmBase::AlarmMap latch{}; |
140 | 0 | BitMask<DishwasherAlarm::AlarmBitmap> latchDefault{}; |
141 | 0 | if (Latch::GetDefault(endpointId, latchDefault) == Status::Success) |
142 | 0 | { |
143 | 0 | latch = AlarmBase::AlarmMap(latchDefault.Raw()); |
144 | 0 | } |
145 | |
|
146 | 0 | DishwasherAlarmCluster::Config config{ |
147 | 0 | .delegate = gDishwasherAlarmClusters[clusterInstanceIndex].integrationDelegateWrapper, |
148 | 0 | .feature = features, |
149 | 0 | .supported = supported, |
150 | 0 | .latch = latch, |
151 | 0 | .supportsModifyEnabledAlarms = EndpointHasModifyEnabledAlarmsCommand(endpointId), |
152 | 0 | }; |
153 | |
|
154 | 0 | gDishwasherAlarmClusters[clusterInstanceIndex].cluster.Create(endpointId, config); |
155 | |
|
156 | 0 | DishwasherAlarmCluster & cluster = gDishwasherAlarmClusters[clusterInstanceIndex].cluster.Cluster(); |
157 | |
|
158 | 0 | BitMask<DishwasherAlarm::AlarmBitmap> maskDefault{}; |
159 | 0 | if (Mask::GetDefault(endpointId, maskDefault) == Status::Success) |
160 | 0 | { |
161 | 0 | cluster.SetMask(AlarmBase::AlarmMap(maskDefault.Raw())); |
162 | 0 | } |
163 | |
|
164 | 0 | BitMask<DishwasherAlarm::AlarmBitmap> stateDefault{}; |
165 | 0 | if (State::GetDefault(endpointId, stateDefault) == Status::Success) |
166 | 0 | { |
167 | 0 | cluster.SetState(AlarmBase::AlarmMap(stateDefault.Raw())); |
168 | 0 | } |
169 | |
|
170 | 0 | return gDishwasherAlarmClusters[clusterInstanceIndex].cluster.Registration(); |
171 | 0 | } |
172 | | |
173 | | ServerClusterInterface * FindRegistration(unsigned clusterInstanceIndex) override |
174 | 0 | { |
175 | 0 | VerifyOrReturnValue(gDishwasherAlarmClusters[clusterInstanceIndex].cluster.IsConstructed(), nullptr); |
176 | 0 | return &gDishwasherAlarmClusters[clusterInstanceIndex].cluster.Cluster(); |
177 | 0 | } |
178 | | |
179 | | void ReleaseRegistration(unsigned clusterInstanceIndex) override |
180 | 0 | { |
181 | 0 | gDishwasherAlarmClusters[clusterInstanceIndex].cluster.Destroy(); |
182 | 0 | } |
183 | | }; |
184 | | |
185 | | } // namespace |
186 | | |
187 | | void MatterDishwasherAlarmClusterInitCallback(EndpointId endpointId) |
188 | 0 | { |
189 | 0 | DishwasherAlarmIntegrationDelegate integrationDelegate; |
190 | 0 | CodegenClusterIntegration::RegisterServer( |
191 | 0 | { |
192 | 0 | .endpointId = endpointId, |
193 | 0 | .clusterId = DishwasherAlarm::Id, |
194 | 0 | .fixedClusterInstanceCount = kDishwasherAlarmFixedClusterCount, |
195 | 0 | .maxClusterInstanceCount = kDishwasherAlarmMaxClusterCount, |
196 | 0 | .fetchFeatureMap = true, |
197 | 0 | .fetchOptionalAttributes = false, |
198 | 0 | }, |
199 | 0 | integrationDelegate); |
200 | 0 | } |
201 | | |
202 | | void MatterDishwasherAlarmClusterShutdownCallback(EndpointId endpointId, MatterClusterShutdownType shutdownType) |
203 | 0 | { |
204 | 0 | DishwasherAlarmIntegrationDelegate integrationDelegate; |
205 | 0 | CodegenClusterIntegration::UnregisterServer( |
206 | 0 | { |
207 | 0 | .endpointId = endpointId, |
208 | 0 | .clusterId = DishwasherAlarm::Id, |
209 | 0 | .fixedClusterInstanceCount = kDishwasherAlarmFixedClusterCount, |
210 | 0 | .maxClusterInstanceCount = kDishwasherAlarmMaxClusterCount, |
211 | 0 | }, |
212 | 0 | integrationDelegate, shutdownType); |
213 | 0 | } |
214 | | |
215 | | __attribute__((weak)) void MatterDishwasherAlarmPluginServerInitCallback() {} |
216 | 0 | __attribute__((weak)) void MatterDishwasherAlarmPluginServerShutdownCallback() {} |
217 | | |
218 | | namespace chip::app::Clusters::DishwasherAlarm { |
219 | | |
220 | | DishwasherAlarmCluster * FindClusterOnEndpoint(EndpointId endpointId) |
221 | 0 | { |
222 | 0 | DishwasherAlarmIntegrationDelegate integrationDelegate; |
223 | 0 | return static_cast<DishwasherAlarmCluster *>(CodegenClusterIntegration::FindClusterOnEndpoint( |
224 | 0 | { |
225 | 0 | .endpointId = endpointId, |
226 | 0 | .clusterId = DishwasherAlarm::Id, |
227 | 0 | .fixedClusterInstanceCount = kDishwasherAlarmFixedClusterCount, |
228 | 0 | .maxClusterInstanceCount = kDishwasherAlarmMaxClusterCount, |
229 | 0 | }, |
230 | 0 | integrationDelegate)); |
231 | 0 | } |
232 | | |
233 | | Delegate * GetDelegate(EndpointId endpoint) |
234 | 0 | { |
235 | 0 | uint16_t ep = emberAfGetClusterServerEndpointIndex(endpoint, DishwasherAlarm::Id, |
236 | 0 | MATTER_DM_DISHWASHER_ALARM_CLUSTER_SERVER_ENDPOINT_COUNT); |
237 | 0 | return (ep >= kDishwasherAlarmMaxClusterCount ? nullptr : gDishwasherAlarmClusters[ep].integrationDelegateWrapper.GetWrapped()); |
238 | 0 | } |
239 | | |
240 | | void SetDefaultDelegate(EndpointId endpoint, Delegate * delegate) |
241 | 0 | { |
242 | 0 | uint16_t ep = emberAfGetClusterServerEndpointIndex(endpoint, DishwasherAlarm::Id, |
243 | 0 | MATTER_DM_DISHWASHER_ALARM_CLUSTER_SERVER_ENDPOINT_COUNT); |
244 | 0 | if (ep < kDishwasherAlarmMaxClusterCount) |
245 | 0 | { |
246 | 0 | gDishwasherAlarmClusters[ep].integrationDelegateWrapper.SetWrapped(delegate); |
247 | 0 | } |
248 | 0 | } |
249 | | |
250 | | DishwasherAlarmServer DishwasherAlarmServer::instance; |
251 | | |
252 | | DishwasherAlarmServer & DishwasherAlarmServer::Instance() |
253 | 0 | { |
254 | 0 | return instance; |
255 | 0 | } |
256 | | |
257 | | Status DishwasherAlarmServer::GetMaskValue(EndpointId endpoint, BitMask<AlarmMap> * mask) |
258 | 0 | { |
259 | 0 | DishwasherAlarmCluster * cluster = FindClusterOnEndpoint(endpoint); |
260 | 0 | VerifyOrReturnError(cluster != nullptr, Status::UnsupportedEndpoint); |
261 | | |
262 | 0 | if (mask != nullptr) |
263 | 0 | { |
264 | 0 | *mask = ToAlarmMap(cluster->GetMask()); |
265 | 0 | } |
266 | 0 | return Status::Success; |
267 | 0 | } |
268 | | |
269 | | Status DishwasherAlarmServer::GetLatchValue(EndpointId endpoint, BitMask<AlarmMap> * latch) |
270 | 0 | { |
271 | 0 | DishwasherAlarmCluster * cluster = FindClusterOnEndpoint(endpoint); |
272 | 0 | VerifyOrReturnError(cluster != nullptr, Status::UnsupportedEndpoint); |
273 | 0 | VerifyOrReturnError(HasResetFeature(endpoint), Status::UnsupportedAttribute); |
274 | | |
275 | 0 | if (latch != nullptr) |
276 | 0 | { |
277 | 0 | *latch = ToAlarmMap(cluster->GetLatch()); |
278 | 0 | } |
279 | 0 | return Status::Success; |
280 | 0 | } |
281 | | |
282 | | Status DishwasherAlarmServer::GetStateValue(EndpointId endpoint, BitMask<AlarmMap> * state) |
283 | 0 | { |
284 | 0 | DishwasherAlarmCluster * cluster = FindClusterOnEndpoint(endpoint); |
285 | 0 | VerifyOrReturnError(cluster != nullptr, Status::UnsupportedEndpoint); |
286 | | |
287 | 0 | if (state != nullptr) |
288 | 0 | { |
289 | 0 | *state = ToAlarmMap(cluster->GetState()); |
290 | 0 | } |
291 | 0 | return Status::Success; |
292 | 0 | } |
293 | | |
294 | | Status DishwasherAlarmServer::GetSupportedValue(EndpointId endpoint, BitMask<AlarmMap> * supported) |
295 | 0 | { |
296 | 0 | DishwasherAlarmCluster * cluster = FindClusterOnEndpoint(endpoint); |
297 | 0 | VerifyOrReturnError(cluster != nullptr, Status::UnsupportedEndpoint); |
298 | | |
299 | 0 | if (supported != nullptr) |
300 | 0 | { |
301 | 0 | *supported = ToAlarmMap(cluster->GetSupported()); |
302 | 0 | } |
303 | 0 | return Status::Success; |
304 | 0 | } |
305 | | |
306 | | Status DishwasherAlarmServer::SetMaskValue(EndpointId endpoint, const BitMask<AlarmMap> mask) |
307 | 0 | { |
308 | 0 | DishwasherAlarmCluster * cluster = FindClusterOnEndpoint(endpoint); |
309 | 0 | VerifyOrReturnError(cluster != nullptr, Status::UnsupportedEndpoint); |
310 | 0 | return cluster->SetMask(FromAlarmMap(mask)); |
311 | 0 | } |
312 | | |
313 | | Status DishwasherAlarmServer::SetStateValue(EndpointId endpoint, const BitMask<AlarmMap> newState, bool ignoreLatchState) |
314 | 0 | { |
315 | 0 | DishwasherAlarmCluster * cluster = FindClusterOnEndpoint(endpoint); |
316 | 0 | VerifyOrReturnError(cluster != nullptr, Status::UnsupportedEndpoint); |
317 | 0 | if (ignoreLatchState) |
318 | 0 | { |
319 | 0 | return cluster->SetStateIgnoringLatch(FromAlarmMap(newState)); |
320 | 0 | } |
321 | 0 | return cluster->SetState(FromAlarmMap(newState)); |
322 | 0 | } |
323 | | |
324 | | Status DishwasherAlarmServer::ResetLatchedAlarms(EndpointId endpoint, const BitMask<AlarmMap> alarms) |
325 | 0 | { |
326 | 0 | DishwasherAlarmCluster * cluster = FindClusterOnEndpoint(endpoint); |
327 | 0 | VerifyOrReturnError(cluster != nullptr, Status::UnsupportedEndpoint); |
328 | 0 | return cluster->ResetLatchedAlarms(FromAlarmMap(alarms)); |
329 | 0 | } |
330 | | |
331 | | bool DishwasherAlarmServer::HasResetFeature(EndpointId endpoint) |
332 | 0 | { |
333 | 0 | DishwasherAlarmCluster * cluster = FindClusterOnEndpoint(endpoint); |
334 | 0 | VerifyOrReturnError(cluster != nullptr, false); |
335 | 0 | return cluster->GetFeatures().Has(Feature::kReset); |
336 | 0 | } |
337 | | |
338 | | namespace Attributes { |
339 | | |
340 | | namespace Mask { |
341 | | |
342 | | Status Get(EndpointId endpoint, BitMask<AlarmMap> * value) |
343 | 0 | { |
344 | 0 | return DishwasherAlarmServer::Instance().GetMaskValue(endpoint, value); |
345 | 0 | } |
346 | | |
347 | | Status Set(EndpointId endpoint, BitMask<AlarmMap> value) |
348 | 0 | { |
349 | 0 | return DishwasherAlarmServer::Instance().SetMaskValue(endpoint, value); |
350 | 0 | } |
351 | | |
352 | | } // namespace Mask |
353 | | |
354 | | namespace Latch { |
355 | | |
356 | | Status Get(EndpointId endpoint, BitMask<AlarmMap> * value) |
357 | 0 | { |
358 | 0 | return DishwasherAlarmServer::Instance().GetLatchValue(endpoint, value); |
359 | 0 | } |
360 | | |
361 | | } // namespace Latch |
362 | | |
363 | | namespace State { |
364 | | |
365 | | Status Get(EndpointId endpoint, BitMask<AlarmMap> * value) |
366 | 0 | { |
367 | 0 | return DishwasherAlarmServer::Instance().GetStateValue(endpoint, value); |
368 | 0 | } |
369 | | |
370 | | Status Set(EndpointId endpoint, BitMask<AlarmMap> value) |
371 | 0 | { |
372 | 0 | return DishwasherAlarmServer::Instance().SetStateValue(endpoint, value); |
373 | 0 | } |
374 | | |
375 | | } // namespace State |
376 | | |
377 | | namespace Supported { |
378 | | |
379 | | Status Get(EndpointId endpoint, BitMask<AlarmMap> * value) |
380 | 0 | { |
381 | 0 | return DishwasherAlarmServer::Instance().GetSupportedValue(endpoint, value); |
382 | 0 | } |
383 | | |
384 | | } // namespace Supported |
385 | | |
386 | | } // namespace Attributes |
387 | | |
388 | | } // namespace chip::app::Clusters::DishwasherAlarm |