@@ -38,20 +38,45 @@ using namespace chip::app::Clusters::FanControl::Attributes;
38
38
using Protocols::InteractionModel::Status;
39
39
40
40
namespace {
41
- class ChefFanControlManager : public AttributeAccessInterface , public Delegate
41
+ class ChefFanControlManager : public Delegate
42
42
{
43
43
public:
44
- ChefFanControlManager (EndpointId aEndpointId) :
45
- AttributeAccessInterface (Optional<EndpointId>(aEndpointId), FanControl::Id), Delegate(aEndpointId)
46
- {}
44
+ ChefFanControlManager (EndpointId aEndpointId) : Delegate(aEndpointId) {}
47
45
48
- CHIP_ERROR Write ( const ConcreteDataAttributePath & aPath, AttributeValueDecoder & aDecoder) override ;
49
- CHIP_ERROR Read ( const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override ;
46
+ void Init () ;
47
+ void HandleFanControlAttributeChange (AttributeId attributeId, uint8_t type, uint16_t size, uint8_t * value) ;
50
48
Status HandleStep (StepDirectionEnum aDirection, bool aWrap, bool aLowestOff) override ;
49
+ DataModel::Nullable<uint8_t > GetSpeedSetting ();
50
+ DataModel::Nullable<Percent> GetPercentSetting ();
51
51
52
52
private:
53
- Nullable<uint8_t > mPercentSetting {};
54
- Nullable<uint8_t > mSpeedSetting {};
53
+ uint8_t mPercentCurrent = 0 ;
54
+ uint8_t mSpeedCurrent = 0 ;
55
+
56
+ // Fan Mode Limits
57
+ struct Range
58
+ {
59
+ bool Contains (int value) const { return value >= low && value <= high; }
60
+ int Low () const { return low; }
61
+ int High () const { return high; }
62
+
63
+ int low;
64
+ int high;
65
+ };
66
+ static constexpr Range kFanModeLowSpeedRange = { 1 , 3 };
67
+ static constexpr Range kFanModeMediumSpeedRange = { 4 , 7 };
68
+ static constexpr Range kFanModeHighSpeedRange = { 8 , 10 };
69
+
70
+ static_assert (kFanModeLowSpeedRange .low <= kFanModeLowSpeedRange .high);
71
+ static_assert (kFanModeLowSpeedRange .high + 1 == kFanModeMediumSpeedRange .low);
72
+ static_assert (kFanModeMediumSpeedRange .high + 1 == kFanModeHighSpeedRange .low);
73
+ static_assert (kFanModeHighSpeedRange .low <= kFanModeHighSpeedRange .high);
74
+
75
+ void FanModeWriteCallback (FanControl::FanModeEnum aNewFanMode);
76
+ void SetSpeedCurrent (uint8_t aNewSpeedCurrent);
77
+ void SetPercentCurrent (uint8_t aNewPercentCurrent);
78
+ void SetSpeedSetting (DataModel::Nullable<uint8_t > aNewSpeedSetting);
79
+ static FanControl::FanModeEnum SpeedToFanMode (uint8_t speed);
55
80
};
56
81
57
82
static std::unique_ptr<ChefFanControlManager> mFanControlManager ;
@@ -99,98 +124,222 @@ Status ChefFanControlManager::HandleStep(StepDirectionEnum aDirection, bool aWra
99
124
return SpeedSetting::Set (mEndpoint , newSpeedSetting);
100
125
}
101
126
102
- CHIP_ERROR ChefFanControlManager::Write ( const ConcreteDataAttributePath & aPath, AttributeValueDecoder & aDecoder )
127
+ void ChefFanControlManager::HandleFanControlAttributeChange (AttributeId attributeId, uint8_t type, uint16_t size, uint8_t * value )
103
128
{
104
- VerifyOrDie (aPath.mClusterId == FanControl::Id);
105
- VerifyOrDie (aPath.mEndpointId == mEndpoint );
106
-
107
- switch (aPath.mAttributeId )
129
+ ChipLogProgress (NotSpecified, " ChefFanControlManager::HandleFanControlAttributeChange" );
130
+ switch (attributeId)
108
131
{
109
- case SpeedSetting::Id: {
110
- Nullable<uint8_t > newSpeedSetting;
111
- ReturnErrorOnFailure (aDecoder.Decode (newSpeedSetting));
112
-
113
- // Ensure new speed is in bounds
132
+ case FanControl::Attributes::PercentSetting::Id: {
133
+ ChipLogProgress (NotSpecified, " ChefFanControlManager::HandleFanControlAttributeChange PercentSetting" );
134
+ DataModel::Nullable<Percent> percentSetting;
135
+ if (!NumericAttributeTraits<Percent>::IsNullValue (*value))
114
136
{
115
- uint8_t maxSpeedSetting = 0 ;
116
- Protocols::InteractionModel::Status status = SpeedMax::Get (mEndpoint , &maxSpeedSetting);
117
- VerifyOrReturnError (status == Protocols::InteractionModel::Status::Success, CHIP_IM_GLOBAL_STATUS (Failure));
118
-
119
- if (!newSpeedSetting.IsNull () && newSpeedSetting.Value () > maxSpeedSetting)
120
- {
121
- return CHIP_IM_GLOBAL_STATUS (ConstraintError);
122
- }
137
+ percentSetting.SetNonNull (NumericAttributeTraits<Percent>::StorageToWorking (*value));
123
138
}
124
-
125
- // Only act on changed.
126
- if (newSpeedSetting != mSpeedSetting )
139
+ else
127
140
{
128
- mSpeedSetting = newSpeedSetting;
129
-
130
- // Mark both the setting AND the current dirty, since the current always
131
- // tracks the target for our product.
132
- MatterReportingAttributeChangeCallback (mEndpoint , FanControl::Id, Attributes::SpeedSetting::Id);
133
- MatterReportingAttributeChangeCallback (mEndpoint , FanControl::Id, Attributes::SpeedCurrent::Id);
141
+ percentSetting.SetNull ();
134
142
}
135
143
144
+ // The cluster code in fan-control-server.cpp is the only one allowed to set PercentSetting to null.
145
+ // This happens as a consequence of setting the FanMode to kAuto. In auto mode, percentCurrent should continue to report the
146
+ // real fan speed percentage. In this example, we set PercentCurrent to 0 here as we don't have a real value for the Fan
147
+ // speed or a FanAutoMode simulator.
148
+ // When not Null, SpeedCurrent tracks SpeedSetting's value.
149
+ SetPercentCurrent (percentSetting.ValueOr (0 ));
136
150
break ;
137
151
}
138
- case PercentSetting::Id: {
139
- Nullable<uint8_t > newPercentSetting;
140
- ReturnErrorOnFailure (aDecoder.Decode (newPercentSetting));
141
152
142
- // Ensure new speed in percent is valid.
143
- if (!newPercentSetting.IsNull () && newPercentSetting.Value () > 100 )
153
+ case FanControl::Attributes::SpeedSetting::Id: {
154
+ ChipLogProgress (NotSpecified, " ChefFanControlManager::HandleFanControlAttributeChange SpeedSetting" );
155
+ DataModel::Nullable<uint8_t > speedSetting;
156
+ if (!NumericAttributeTraits<uint8_t >::IsNullValue (*value))
144
157
{
145
- return CHIP_IM_GLOBAL_STATUS (ConstraintError );
158
+ speedSetting. SetNonNull (NumericAttributeTraits< uint8_t >:: StorageToWorking (*value) );
146
159
}
147
-
148
- // Only act on changed.
149
- if (newPercentSetting != mPercentSetting )
160
+ else
150
161
{
151
- mPercentSetting = newPercentSetting;
152
-
153
- // Mark both the setting AND the current dirty, since the current always
154
- // tracks the target for our product.
155
- MatterReportingAttributeChangeCallback (mEndpoint , FanControl::Id, Attributes::PercentSetting::Id);
156
- MatterReportingAttributeChangeCallback (mEndpoint , FanControl::Id, Attributes::PercentCurrent::Id);
162
+ speedSetting.SetNull ();
157
163
}
158
164
165
+ // The cluster code in fan-control-server.cpp is the only one allowed to set speedSetting to null.
166
+ // This happens as a consequence of setting the FanMode to kAuto. In auto mode, speedCurrent should continue to report the
167
+ // real fan speed. In this example, we set SpeedCurrent to 0 here as we don't have a real value for the Fan speed or a
168
+ // FanAutoMode simulator.
169
+ // When not Null, SpeedCurrent tracks SpeedSetting's value.
170
+ SetSpeedCurrent (speedSetting.ValueOr (0 ));
171
+ // Determine if the speed change should also change the fan mode
172
+ FanControl::Attributes::FanMode::Set (mEndpoint , SpeedToFanMode (mSpeedCurrent ));
159
173
break ;
160
174
}
161
- default :
175
+
176
+ case FanControl::Attributes::FanMode::Id: {
177
+ ChipLogProgress (NotSpecified, " ChefFanControlManager::HandleFanControlAttributeChange FanMode" );
178
+
179
+ static_assert (sizeof (FanControl::FanModeEnum) == 1 );
180
+ FanControl::FanModeEnum fanMode = static_cast <FanControl::FanModeEnum>(*value);
181
+ FanModeWriteCallback (fanMode);
162
182
break ;
163
183
}
164
184
165
- // Fall through goes to attribute store legacy handling.
166
- return CHIP_NO_ERROR;
185
+ default : {
186
+ break ;
187
+ }
188
+ }
189
+ }
190
+
191
+ FanControl::FanModeEnum ChefFanControlManager::SpeedToFanMode (uint8_t speed)
192
+ {
193
+ if (speed == 0 )
194
+ {
195
+ return FanControl::FanModeEnum::kOff ;
196
+ }
197
+ if (kFanModeLowSpeedRange .Contains (speed))
198
+ {
199
+ return FanControl::FanModeEnum::kLow ;
200
+ }
201
+ if (kFanModeMediumSpeedRange .Contains (speed))
202
+ {
203
+ return FanControl::FanModeEnum::kMedium ;
204
+ }
205
+ return FanControl::FanModeEnum::kHigh ;
167
206
}
168
207
169
- CHIP_ERROR ChefFanControlManager::Read ( const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder )
208
+ void ChefFanControlManager::SetPercentCurrent ( uint8_t aNewPercentCurrent )
170
209
{
171
- VerifyOrDie (aPath.mClusterId == FanControl::Id);
172
- VerifyOrDie (aPath.mEndpointId == mEndpoint );
210
+ if (aNewPercentCurrent == mPercentCurrent )
211
+ {
212
+ return ;
213
+ }
173
214
174
- switch (aPath.mAttributeId )
215
+ ChipLogDetail (NotSpecified, " ChefFanControlManager::SetPercentCurrent: %d" , aNewPercentCurrent);
216
+ mPercentCurrent = aNewPercentCurrent;
217
+ Status status = FanControl::Attributes::PercentCurrent::Set (mEndpoint , mPercentCurrent );
218
+ if (status != Status::Success)
175
219
{
176
- case PercentCurrent::Id: {
177
- // Current percents always tracks setting immediately in our implementation.
178
- return aEncoder.Encode (mPercentSetting .ValueOr (0 ));
220
+ ChipLogError (NotSpecified, " ChefFanControlManager::SetPercentCurrent: failed to set PercentCurrent attribute: %d" ,
221
+ to_underlying (status));
179
222
}
180
- case PercentSetting::Id: {
181
- return aEncoder.Encode (mPercentSetting );
223
+ }
224
+
225
+ void ChefFanControlManager::SetSpeedCurrent (uint8_t aNewSpeedCurrent)
226
+ {
227
+ if (aNewSpeedCurrent == mSpeedCurrent )
228
+ {
229
+ return ;
182
230
}
183
- case SpeedCurrent::Id: {
184
- // Current speed always tracks setting immediately in our implementation.
185
- return aEncoder.Encode (mSpeedSetting .ValueOr (0 ));
231
+
232
+ ChipLogDetail (NotSpecified, " ChefFanControlManager::SetSpeedCurrent: %d" , aNewSpeedCurrent);
233
+ mSpeedCurrent = aNewSpeedCurrent;
234
+ Status status = FanControl::Attributes::SpeedCurrent::Set (mEndpoint , aNewSpeedCurrent);
235
+ if (status != Status::Success)
236
+ {
237
+ ChipLogError (NotSpecified, " ChefFanControlManager::SetSpeedCurrent: failed to set SpeedCurrent attribute: %d" ,
238
+ to_underlying (status));
186
239
}
187
- case SpeedSetting::Id: {
188
- return aEncoder.Encode (mSpeedSetting );
240
+ }
241
+
242
+ void ChefFanControlManager::FanModeWriteCallback (FanControl::FanModeEnum aNewFanMode)
243
+ {
244
+ ChipLogDetail (NotSpecified, " ChefFanControlManager::FanModeWriteCallback: %d" , to_underlying (aNewFanMode));
245
+ switch (aNewFanMode)
246
+ {
247
+ case FanControl::FanModeEnum::kOff : {
248
+ if (mSpeedCurrent != 0 )
249
+ {
250
+ DataModel::Nullable<uint8_t > speedSetting (0 );
251
+ SetSpeedSetting (speedSetting);
252
+ }
253
+ break ;
189
254
}
190
- default :
255
+ case FanControl::FanModeEnum::kLow : {
256
+ if (!kFanModeLowSpeedRange .Contains (mSpeedCurrent ))
257
+ {
258
+ DataModel::Nullable<uint8_t > speedSetting (kFanModeLowSpeedRange .Low ());
259
+ SetSpeedSetting (speedSetting);
260
+ }
191
261
break ;
192
262
}
193
- return CHIP_NO_ERROR;
263
+ case FanControl::FanModeEnum::kMedium : {
264
+ if (!kFanModeMediumSpeedRange .Contains (mSpeedCurrent ))
265
+ {
266
+ DataModel::Nullable<uint8_t > speedSetting (kFanModeMediumSpeedRange .Low ());
267
+ SetSpeedSetting (speedSetting);
268
+ }
269
+ break ;
270
+ }
271
+ case FanControl::FanModeEnum::kOn :
272
+ case FanControl::FanModeEnum::kHigh : {
273
+ if (!kFanModeHighSpeedRange .Contains (mSpeedCurrent ))
274
+ {
275
+ DataModel::Nullable<uint8_t > speedSetting (kFanModeHighSpeedRange .Low ());
276
+ SetSpeedSetting (speedSetting);
277
+ }
278
+ break ;
279
+ }
280
+ case FanControl::FanModeEnum::kSmart :
281
+ case FanControl::FanModeEnum::kAuto : {
282
+ ChipLogProgress (NotSpecified, " ChefFanControlManager::FanModeWriteCallback: Auto" );
283
+ break ;
284
+ }
285
+ case FanControl::FanModeEnum::kUnknownEnumValue : {
286
+ ChipLogProgress (NotSpecified, " ChefFanControlManager::FanModeWriteCallback: Unknown" );
287
+ break ;
288
+ }
289
+ }
290
+ }
291
+
292
+ void ChefFanControlManager::SetSpeedSetting (DataModel::Nullable<uint8_t > aNewSpeedSetting)
293
+ {
294
+ if (aNewSpeedSetting.IsNull ())
295
+ {
296
+ ChipLogError (NotSpecified, " ChefFanControlManager::SetSpeedSetting: null value is invalid" );
297
+ return ;
298
+ }
299
+
300
+ if (aNewSpeedSetting.Value () != mSpeedCurrent )
301
+ {
302
+ Status status = FanControl::Attributes::SpeedSetting::Set (mEndpoint , aNewSpeedSetting);
303
+ if (status != Status::Success)
304
+ {
305
+ ChipLogError (NotSpecified, " ChefFanControlManager::SetSpeedSetting: failed to set SpeedSetting attribute: %d" ,
306
+ to_underlying (status));
307
+ }
308
+ }
309
+ }
310
+
311
+ void ChefFanControlManager::Init ()
312
+ {
313
+ SetPercentCurrent (GetPercentSetting ().ValueOr (0 ));
314
+ SetSpeedCurrent (GetSpeedSetting ().ValueOr (0 ));
315
+ }
316
+
317
+ DataModel::Nullable<Percent> ChefFanControlManager::GetPercentSetting ()
318
+ {
319
+ DataModel::Nullable<Percent> percentSetting;
320
+ Status status = FanControl::Attributes::PercentSetting::Get (mEndpoint , percentSetting);
321
+
322
+ if (status != Status::Success)
323
+ {
324
+ ChipLogError (NotSpecified, " ChefFanControlManager::GetPercentSetting: failed to get PercentSetting attribute: %d" ,
325
+ to_underlying (status));
326
+ }
327
+
328
+ return percentSetting;
329
+ }
330
+
331
+ DataModel::Nullable<uint8_t > ChefFanControlManager::GetSpeedSetting ()
332
+ {
333
+ DataModel::Nullable<uint8_t > speedSetting;
334
+ Status status = FanControl::Attributes::SpeedSetting::Get (mEndpoint , speedSetting);
335
+
336
+ if (status != Status::Success)
337
+ {
338
+ ChipLogError (NotSpecified, " ChefFanControlManager::GetSpeedSetting: failed to get SpeedSetting attribute: %d" ,
339
+ to_underlying (status));
340
+ }
341
+
342
+ return speedSetting;
194
343
}
195
344
196
345
} // anonymous namespace
@@ -199,6 +348,11 @@ void emberAfFanControlClusterInitCallback(EndpointId endpoint)
199
348
{
200
349
VerifyOrDie (!mFanControlManager );
201
350
mFanControlManager = std::make_unique<ChefFanControlManager>(endpoint);
202
- AttributeAccessInterfaceRegistry::Instance ().Register (mFanControlManager .get ());
203
351
FanControl::SetDefaultDelegate (endpoint, mFanControlManager .get ());
352
+ mFanControlManager ->Init ();
353
+ }
354
+
355
+ void HandleFanControlAttributeChange (AttributeId attributeId, uint8_t type, uint16_t size, uint8_t * value)
356
+ {
357
+ mFanControlManager ->HandleFanControlAttributeChange (attributeId, type, size, value);
204
358
}
0 commit comments