@@ -38,20 +38,45 @@ using namespace chip::app::Clusters::FanControl::Attributes;
3838using Protocols::InteractionModel::Status;
3939
4040namespace {
41- class ChefFanControlManager : public AttributeAccessInterface , public Delegate
41+ class ChefFanControlManager : public Delegate
4242{
4343public:
44- ChefFanControlManager (EndpointId aEndpointId) :
45- AttributeAccessInterface (Optional<EndpointId>(aEndpointId), FanControl::Id), Delegate(aEndpointId)
46- {}
44+ ChefFanControlManager (EndpointId aEndpointId) : Delegate(aEndpointId) {}
4745
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) ;
5048 Status HandleStep (StepDirectionEnum aDirection, bool aWrap, bool aLowestOff) override ;
49+ DataModel::Nullable<uint8_t > GetSpeedSetting ();
50+ DataModel::Nullable<Percent> GetPercentSetting ();
5151
5252private:
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);
5580};
5681
5782static std::unique_ptr<ChefFanControlManager> mFanControlManager ;
@@ -99,98 +124,222 @@ Status ChefFanControlManager::HandleStep(StepDirectionEnum aDirection, bool aWra
99124 return SpeedSetting::Set (mEndpoint , newSpeedSetting);
100125}
101126
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 )
103128{
104- VerifyOrDie (aPath.mClusterId == FanControl::Id);
105- VerifyOrDie (aPath.mEndpointId == mEndpoint );
106-
107- switch (aPath.mAttributeId )
129+ ChipLogProgress (NotSpecified, " ChefFanControlManager::HandleFanControlAttributeChange" );
130+ switch (attributeId)
108131 {
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))
114136 {
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));
123138 }
124-
125- // Only act on changed.
126- if (newSpeedSetting != mSpeedSetting )
139+ else
127140 {
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 ();
134142 }
135143
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 ));
136150 break ;
137151 }
138- case PercentSetting::Id: {
139- Nullable<uint8_t > newPercentSetting;
140- ReturnErrorOnFailure (aDecoder.Decode (newPercentSetting));
141152
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))
144157 {
145- return CHIP_IM_GLOBAL_STATUS (ConstraintError );
158+ speedSetting. SetNonNull (NumericAttributeTraits< uint8_t >:: StorageToWorking (*value) );
146159 }
147-
148- // Only act on changed.
149- if (newPercentSetting != mPercentSetting )
160+ else
150161 {
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 ();
157163 }
158164
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 ));
159173 break ;
160174 }
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);
162182 break ;
163183 }
164184
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 ;
167206}
168207
169- CHIP_ERROR ChefFanControlManager::Read ( const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder )
208+ void ChefFanControlManager::SetPercentCurrent ( uint8_t aNewPercentCurrent )
170209{
171- VerifyOrDie (aPath.mClusterId == FanControl::Id);
172- VerifyOrDie (aPath.mEndpointId == mEndpoint );
210+ if (aNewPercentCurrent == mPercentCurrent )
211+ {
212+ return ;
213+ }
173214
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)
175219 {
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));
179222 }
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 ;
182230 }
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));
186239 }
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 ;
189254 }
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+ }
191261 break ;
192262 }
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;
194343}
195344
196345} // anonymous namespace
@@ -199,6 +348,11 @@ void emberAfFanControlClusterInitCallback(EndpointId endpoint)
199348{
200349 VerifyOrDie (!mFanControlManager );
201350 mFanControlManager = std::make_unique<ChefFanControlManager>(endpoint);
202- AttributeAccessInterfaceRegistry::Instance ().Register (mFanControlManager .get ());
203351 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);
204358}
0 commit comments