From a82b7ff8a50b09dbef6da3d927c2701dc2efaea5 Mon Sep 17 00:00:00 2001 From: lpbeliveau-silabs Date: Fri, 28 Apr 2023 17:31:36 -0400 Subject: [PATCH 1/5] Added the color-control cluster handler for scenes EFS --- .../color-control-server.cpp | 688 +++++++++++++----- .../color-control-server.h | 11 + 2 files changed, 531 insertions(+), 168 deletions(-) diff --git a/src/app/clusters/color-control-server/color-control-server.cpp b/src/app/clusters/color-control-server/color-control-server.cpp index bd0ac0293e5ea0..d75e059cc718b2 100644 --- a/src/app/clusters/color-control-server/color-control-server.cpp +++ b/src/app/clusters/color-control-server/color-control-server.cpp @@ -25,11 +25,303 @@ #include #include +#ifdef EMBER_AF_PLUGIN_SCENES +#include +#endif + using namespace chip; using namespace chip::app::Clusters; using namespace chip::app::Clusters::ColorControl; using chip::Protocols::InteractionModel::Status; +#ifdef EMBER_AF_PLUGIN_SCENES +class DefaultColorControlSceneHandler : public scenes::DefaultSceneHandlerImpl +{ +public: + enum ColorControlEFS : uint8_t + { + kCurrentX = 0, + kCurrentY, + kEnhancedCurrentHue, + kCurrentSaturation, + kColorLoopActive, + kColorLoopDirection, + kColorLoopTime, + kColorTemperatureMireds, + kEnhancedColorMode, + }; + + // As per spec, 2 attributes are scenable in the level control cluster + static constexpr uint8_t kColorControlMaxScenableAttributes = 9; + + DefaultColorControlSceneHandler() = default; + ~DefaultColorControlSceneHandler() override {} + + // Default function for ColorControl cluster, only puts the ColorControl cluster ID in the span if supported on the caller + // endpoint + void GetSupportedClusters(EndpointId endpoint, Span & clusterBuffer) override + { + ClusterId * buffer = clusterBuffer.data(); + if (emberAfContainsServer(endpoint, ColorControl::Id) && clusterBuffer.size() >= 1) + { + buffer[0] = ColorControl::Id; + clusterBuffer.reduce_size(1); + } + } + + // Default function for ColorControl cluster, only checks if ColorControl is enabled on the endpoint + bool SupportsCluster(EndpointId endpoint, ClusterId cluster) override + { + return (cluster == ColorControl::Id) && (emberAfContainsServer(endpoint, ColorControl::Id)); + } + + /// @brief Serialize the Cluster's EFS value + /// @param endpoint target endpoint + /// @param cluster target cluster + /// @param serialisedBytes data to serialize into EFS + /// @return CHIP_NO_ERROR if successfully serialized the data, CHIP_ERROR_INVALID_ARGUMENT otherwise + CHIP_ERROR SerializeSave(EndpointId endpoint, ClusterId cluster, MutableByteSpan & serializedBytes) override + { + using AttributeValuePair = Scenes::Structs::AttributeValuePair::Type; + + AttributeValuePair pairs[kColorControlMaxScenableAttributes]; + size_t attributeCount = 0; + + if (ColorControlServer::Instance().HasFeature(endpoint, ColorControlServer::Feature::kXy)) + { + uint16_t xValue = 0x616B; // Default X value according to spec + Attributes::CurrentX::Get(endpoint, &xValue); + AddAttributeValuePair(pairs, Attributes::CurrentX::Id, xValue, attributeCount); + + uint16_t yValue = 0x607D; // Default Y value according to spec + Attributes::CurrentY::Get(endpoint, &yValue); + AddAttributeValuePair(pairs, Attributes::CurrentY::Id, yValue, attributeCount); + } + + if (ColorControlServer::Instance().HasFeature(endpoint, ColorControlServer::Feature::kEnhancedHue)) + { + uint16_t hueValue = 0x0000; + Attributes::EnhancedCurrentHue::Get(endpoint, &hueValue); + AddAttributeValuePair(pairs, Attributes::EnhancedCurrentHue::Id, hueValue, attributeCount); + } + + if (ColorControlServer::Instance().HasFeature(endpoint, ColorControlServer::Feature::kHueAndSaturation)) + { + uint8_t saturationValue = 0x00; + Attributes::CurrentSaturation::Get(endpoint, &saturationValue); + AddAttributeValuePair(pairs, Attributes::CurrentSaturation::Id, saturationValue, attributeCount); + } + + if (ColorControlServer::Instance().HasFeature(endpoint, ColorControlServer::Feature::kColorLoop)) + { + uint8_t loopActiveValue = 0x00; + Attributes::ColorLoopActive::Get(endpoint, &loopActiveValue); + AddAttributeValuePair(pairs, Attributes::ColorLoopActive::Id, loopActiveValue, attributeCount); + + uint8_t loopDirectionValue = 0x00; + Attributes::ColorLoopDirection::Get(endpoint, &loopDirectionValue); + AddAttributeValuePair(pairs, Attributes::ColorLoopDirection::Id, loopDirectionValue, attributeCount); + + uint16_t loopTimeValue = 0x0019; // Default loop time value according to spec + Attributes::ColorLoopTime::Get(endpoint, &loopTimeValue); + AddAttributeValuePair(pairs, Attributes::ColorLoopTime::Id, loopTimeValue, attributeCount); + } + + if (ColorControlServer::Instance().HasFeature(endpoint, ColorControlServer::Feature::kColorTemperature)) + { + uint16_t temperatureValue = 0x00FA; + Attributes::ColorTemperatureMireds::Get(endpoint, &temperatureValue); + AddAttributeValuePair(pairs, Attributes::ColorTemperatureMireds::Id, temperatureValue, attributeCount); + } + + uint8_t modeValue = ColorControlServer::ColorMode::COLOR_MODE_CIE_XY; + Attributes::EnhancedColorMode::Get(endpoint, &modeValue); + AddAttributeValuePair(pairs, Attributes::EnhancedColorMode::Id, modeValue, attributeCount); + + app::DataModel::List attributeValueList(pairs, attributeCount); + + return EncodeAttributeValueList(attributeValueList, serializedBytes); + } + + /// @brief Default EFS interaction when applying scene to the ColorControl Cluster + /// @param endpoint target endpoint + /// @param cluster target cluster + /// @param serialisedBytes Data from nvm + /// @param timeMs transition time in ms + /// @return CHIP_NO_ERROR if value as expected, CHIP_ERROR_INVALID_ARGUMENT otherwise + CHIP_ERROR ApplyScene(EndpointId endpoint, ClusterId cluster, const ByteSpan & serializedBytes, + scenes::TransitionTimeMs timeMs) override + { + app::DataModel::DecodableList attributeValueList; + + ReturnErrorOnFailure(DecodeAttributeValueList(serializedBytes, attributeValueList)); + + size_t attributeCount = 0; + uint32_t attributeBuffer[kColorControlMaxScenableAttributes]; + auto pair_iterator = attributeValueList.begin(); + + // The color control cluster should have a maximum of 9 scenable attributes + ReturnErrorOnFailure(attributeValueList.ComputeSize(&attributeCount)); + VerifyOrReturnError(attributeCount <= kColorControlMaxScenableAttributes, CHIP_ERROR_BUFFER_TOO_SMALL); + + uint8_t attIdx; + while (pair_iterator.Next()) + { + auto & decodePair = pair_iterator.GetValue(); + + switch (decodePair.attributeID) + { + case Attributes::CurrentX::Id: + attIdx = ColorControlEFS::kCurrentX; + break; + case Attributes::CurrentY::Id: + attIdx = ColorControlEFS::kCurrentY; + break; + case Attributes::EnhancedCurrentHue::Id: + attIdx = ColorControlEFS::kEnhancedCurrentHue; + break; + case Attributes::CurrentSaturation::Id: + attIdx = ColorControlEFS::kCurrentSaturation; + break; + case Attributes::ColorLoopActive::Id: + attIdx = ColorControlEFS::kColorLoopActive; + break; + case Attributes::ColorLoopDirection::Id: + attIdx = ColorControlEFS::kColorLoopDirection; + break; + case Attributes::ColorLoopTime::Id: + attIdx = ColorControlEFS::kColorLoopTime; + break; + case Attributes::ColorTemperatureMireds::Id: + attIdx = ColorControlEFS::kColorTemperatureMireds; + break; + case Attributes::EnhancedColorMode::Id: + attIdx = ColorControlEFS::kEnhancedColorMode; + break; + default: + return CHIP_ERROR_INVALID_ARGUMENT; + break; + } + + attributeBuffer[attIdx] = decodePair.attributeValue; + } + + ReturnErrorOnFailure(pair_iterator.GetStatus()); + + // Switch to the mode saved in the scene + if (SupportsColorMode(endpoint, static_cast(attributeBuffer[ColorControlEFS::kEnhancedColorMode]))) + { + ColorControlServer::Instance().handleModeSwitch( + endpoint, static_cast(attributeBuffer[ColorControlEFS::kEnhancedColorMode])); + } + else + { + return CHIP_ERROR_INVALID_ARGUMENT; + } + + uint16_t transitionTime10th = static_cast(timeMs / 100); + +#ifdef EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_HSV + ColorControlServer::ColorHueTransitionState * colorHueTransitionState = + ColorControlServer::Instance().getColorHueTransitionState(endpoint); + ColorControlServer::Color16uTransitionState * colorSaturationTransitionState = + ColorControlServer::Instance().getSaturationTransitionState(endpoint); + colorSaturationTransitionState->finalValue = static_cast(attributeBuffer[ColorControlEFS::kCurrentSaturation]); + colorHueTransitionState->finalEnhancedHue = static_cast(attributeBuffer[ColorControlEFS::kEnhancedCurrentHue]); +#endif +#ifdef EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_XY + ColorControlServer::Color16uTransitionState * colorXTransitionState = + ColorControlServer::Instance().getXTransitionState(endpoint); + ColorControlServer::Color16uTransitionState * colorYTransitionState = + ColorControlServer::Instance().getYTransitionState(endpoint); + colorXTransitionState->finalValue = static_cast(attributeBuffer[ColorControlEFS::kCurrentX]); + colorYTransitionState->finalValue = static_cast(attributeBuffer[ColorControlEFS::kCurrentY]); +#endif +#ifdef EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_TEMP + ColorControlServer::Color16uTransitionState * colorTempTransitionState = + ColorControlServer::Instance().getTempTransitionState(endpoint); + colorTempTransitionState->finalValue = static_cast(attributeBuffer[ColorControlEFS::kColorTemperatureMireds]); +#endif + + // Set Loop Scene Attributes and start loop if scene stored active loop + Attributes::ColorLoopDirection::Set(endpoint, static_cast(attributeBuffer[ColorControlEFS::kColorLoopDirection])); + Attributes::ColorLoopTime::Set(endpoint, static_cast(attributeBuffer[ColorControlEFS::kColorLoopTime])); + if (attributeBuffer[ColorControlEFS::kColorLoopActive] == 1) + { + // Tries to applie color control loop + ColorControlServer::Instance().startColorLoop(endpoint, true); + } + else + { + // Execute movement to value depending on the mode in the saved scene + switch (attributeBuffer[ColorControlEFS::kEnhancedColorMode]) + { + case ColorControlServer::ColorMode::COLOR_MODE_HSV: +#ifdef EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_HSV + ColorControlServer::Instance().moveToSaturation( + static_cast(attributeBuffer[ColorControlEFS::kCurrentSaturation]), transitionTime10th, endpoint); +#endif // EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_HSV + break; + case ColorControlServer::ColorMode::COLOR_MODE_CIE_XY: +#ifdef EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_XY + ColorControlServer::Instance().moveToColor(static_cast(attributeBuffer[ColorControlEFS::kCurrentX]), + static_cast(attributeBuffer[ColorControlEFS::kCurrentY]), + transitionTime10th, endpoint); +#endif // EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_XY + break; + case ColorControlServer::ColorMode::COLOR_MODE_TEMPERATURE: +#ifdef EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_TEMP + ColorControlServer::Instance().moveToColorTemp( + endpoint, static_cast(attributeBuffer[ColorControlEFS::kColorTemperatureMireds]), transitionTime10th); +#endif // EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_TEMP + break; + case ColorControlServer::ColorMode::COLOR_MODE_EHSV: +#ifdef EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_HSV + ColorControlServer::Instance().moveToHueAndSaturation( + static_cast(attributeBuffer[ColorControlEFS::kEnhancedCurrentHue]), + static_cast(attributeBuffer[ColorControlEFS::kCurrentSaturation]), transitionTime10th, true, endpoint); +#endif // EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_HSV + break; + default: + return CHIP_ERROR_INVALID_ARGUMENT; + } + } + return CHIP_NO_ERROR; + } + +private: + bool SupportsColorMode(EndpointId endpoint, uint8_t mode) + { + switch (mode) + { + case EMBER_ZCL_ENHANCED_COLOR_MODE_CURRENT_HUE_AND_CURRENT_SATURATION: + return ColorControlServer::Instance().HasFeature(endpoint, ColorControlServer::Feature::kHueAndSaturation); + break; + case EMBER_ZCL_ENHANCED_COLOR_MODE_CURRENT_X_AND_CURRENT_Y: + return ColorControlServer::Instance().HasFeature(endpoint, ColorControlServer::Feature::kXy); + break; + case EMBER_ZCL_ENHANCED_COLOR_MODE_COLOR_TEMPERATURE: + return ColorControlServer::Instance().HasFeature(endpoint, ColorControlServer::Feature::kColorTemperature); + break; + case EMBER_ZCL_ENHANCED_COLOR_MODE_ENHANCED_CURRENT_HUE_AND_CURRENT_SATURATION: + return ColorControlServer::Instance().HasFeature(endpoint, ColorControlServer::Feature::kEnhancedHue); + break; + default: + return false; + } + } + + void AddAttributeValuePair(Scenes::Structs::AttributeValuePair::Type * pairs, AttributeId id, uint32_t value, + size_t & attributeCount) + { + pairs[attributeCount].attributeID = id; + pairs[attributeCount].attributeValue = value; + attributeCount++; + } +}; +static DefaultColorControlSceneHandler sColorControlSceneHandler; +#endif // EMBER_AF_PLUGIN_SCENES + /********************************************************** * Matter timer scheduling glue logic *********************************************************/ @@ -79,6 +371,16 @@ ColorControlServer & ColorControlServer::Instance() return instance; } +chip::scenes::SceneHandler * ColorControlServer::GetSceneHandler() +{ + +#ifdef EMBER_AF_PLUGIN_SCENES + return &sColorControlSceneHandler; +#else + return nullptr; +#endif // EMBER_AF_PLUGIN_SCENES +} + bool ColorControlServer::HasFeature(chip::EndpointId endpoint, Feature feature) { bool success; @@ -850,6 +1152,151 @@ EmberEventControl * ColorControlServer::configureHSVEventControl(EndpointId endp return controller; } +/** + * @brief executes move to saturation command + * + * @param saturation target saturation + * @param transitionTime transition time in 10th of seconds + * @param endpoint target endpoint where to execute move + * @return Status::Success if successful,Status::UnsupportedEndpoint if the saturation transition state doesn't exist, + * Status::ConstraintError if the saturation is above maximum + */ +Status ColorControlServer::moveToSaturation(uint8_t saturation, uint16_t transitionTime, EndpointId endpoint) +{ + Color16uTransitionState * colorSaturationTransitionState = getSaturationTransitionState(endpoint); + VerifyOrReturnError(nullptr != colorSaturationTransitionState, Status::UnsupportedEndpoint); + + // limit checking: hue and saturation are 0..254. Spec dictates we ignore + // this and report a malformed packet. + if (saturation > MAX_SATURATION_VALUE) + { + return Status::ConstraintError; + } + + if (transitionTime == 0) + { + transitionTime++; + } + + // New command. Need to stop any active transitions. + stopAllColorTransitions(endpoint); + + // Handle color mode transition, if necessary. + handleModeSwitch(endpoint, COLOR_MODE_HSV); + + // now, kick off the state machine. + initSaturationTransitionState(endpoint, colorSaturationTransitionState); + colorSaturationTransitionState->finalValue = saturation; + colorSaturationTransitionState->stepsRemaining = transitionTime; + colorSaturationTransitionState->stepsTotal = transitionTime; + colorSaturationTransitionState->endpoint = endpoint; + colorSaturationTransitionState->lowLimit = MIN_SATURATION_VALUE; + colorSaturationTransitionState->highLimit = MAX_SATURATION_VALUE; + + SetHSVRemainingTime(endpoint); + + // kick off the state machine: + scheduleTimerCallbackMs(configureHSVEventControl(endpoint), TRANSITION_UPDATE_TIME_MS.count()); + + return Status::Success; +} + +/** + * @brief executes move to hue and saturatioan command + * + * @param[in] hue target hue + * @param[in] saturation target saturation + * @param[in] transitionTime transition time in 10th of seconds + * @param[in] isEnhanced If True, function was called by EnhancedMoveHue command and rate is a uint16 value. If False function + * was called by MoveHue command and rate is a uint8 value + * @param[in] endpoint + * @return Status::Success if successful,Status::UnsupportedEndpoint if the saturation transition state doesn't exist, + * Status::ConstraintError if the saturation is above maximum + */ +Status ColorControlServer::moveToHueAndSaturation(uint16_t hue, uint8_t saturation, uint16_t transitionTime, bool isEnhanced, + EndpointId endpoint) +{ + uint16_t currentHue = 0; + uint16_t halfWay = isEnhanced ? HALF_MAX_UINT16T : HALF_MAX_UINT8T; + bool moveUp; + + Color16uTransitionState * colorSaturationTransitionState = getSaturationTransitionState(endpoint); + ColorHueTransitionState * colorHueTransitionState = getColorHueTransitionState(endpoint); + + VerifyOrReturnError(nullptr != colorSaturationTransitionState, Status::UnsupportedEndpoint); + VerifyOrReturnError(nullptr != colorHueTransitionState, Status::UnsupportedEndpoint); + + // limit checking: hue and saturation are 0..254. Spec dictates we ignore + // this and report a constraint error. + if ((!isEnhanced && hue > MAX_HUE_VALUE) || saturation > MAX_SATURATION_VALUE) + { + return Status::ConstraintError; + } + + if (transitionTime == 0) + { + transitionTime++; + } + + // New command. Need to stop any active transitions. + stopAllColorTransitions(endpoint); + + // Handle color mode transition, if necessary. + if (isEnhanced) + { + handleModeSwitch(endpoint, ColorMode::COLOR_MODE_EHSV); + } + else + { + handleModeSwitch(endpoint, ColorMode::COLOR_MODE_HSV); + } + + // now, kick off the state machine. + initHueTransitionState(endpoint, colorHueTransitionState, isEnhanced); + + if (isEnhanced) + { + currentHue = colorHueTransitionState->currentEnhancedHue; + colorHueTransitionState->finalEnhancedHue = hue; + } + else + { + currentHue = static_cast(colorHueTransitionState->currentHue); + colorHueTransitionState->finalHue = static_cast(hue); + } + + // compute shortest direction + if (hue > currentHue) + { + moveUp = (hue - currentHue) < halfWay; + } + else + { + moveUp = (currentHue - hue) > halfWay; + } + + colorHueTransitionState->up = moveUp; + colorHueTransitionState->stepsRemaining = transitionTime; + colorHueTransitionState->stepsTotal = transitionTime; + colorHueTransitionState->endpoint = endpoint; + colorHueTransitionState->repeat = false; + + initSaturationTransitionState(endpoint, colorSaturationTransitionState); + colorSaturationTransitionState->finalValue = saturation; + colorSaturationTransitionState->stepsRemaining = transitionTime; + colorSaturationTransitionState->stepsTotal = transitionTime; + colorSaturationTransitionState->endpoint = endpoint; + colorSaturationTransitionState->lowLimit = MIN_SATURATION_VALUE; + colorSaturationTransitionState->highLimit = MAX_SATURATION_VALUE; + + SetHSVRemainingTime(endpoint); + + // kick off the state machine: + scheduleTimerCallbackMs(configureHSVEventControl(endpoint), TRANSITION_UPDATE_TIME_MS.count()); + + return Status::Success; +} + /** * @brief Executes move Hue Command * @@ -858,8 +1305,8 @@ EmberEventControl * ColorControlServer::configureHSVEventControl(EndpointId endp * @param[in] rate * @param[in] optionsMask * @param[in] optionsOverride - * @param[in] isEnhanced If True, function was called by EnhancedMoveHue command and rate is a uint16 value. If False function was - * called by MoveHue command and rate is a uint8 value + * @param[in] isEnhanced If True, function was called by EnhancedMoveHue command and rate is a uint16 value. If False function + * was called by MoveHue command and rate is a uint8 value * @return true Success * @return false Failed */ @@ -963,8 +1410,8 @@ bool ColorControlServer::moveHueCommand(app::CommandHandler * commandObj, const * @param[in] transitionTime * @param[in] optionsMask * @param[in] optionsOverride - * @param[in] isEnhanced If True, function was called by EnhancedMoveHue command and rate is a uint16 value. If False function was - * called by MoveHue command and rate is a uint8 value + * @param[in] isEnhanced If True, function was called by EnhancedMoveHue command and rate is a uint16 value. If False function + * was called by MoveHue command and rate is a uint8 value * @return true Success * @return false Failed */ @@ -1089,14 +1536,15 @@ bool ColorControlServer::moveToHueCommand(app::CommandHandler * commandObj, cons /** * @brief executes move to hue and saturatioan command * - * @param[in] endpoint + * @param[in] commandObj + * @param[in] commandPath * @param[in] hue * @param[in] saturation * @param[in] transitionTime * @param[in] optionsMask * @param[in] optionsOverride - * @param[in] isEnhanced If True, function was called by EnhancedMoveHue command and rate is a uint16 value. If False function was - * called by MoveHue command and rate is a uint8 value + * @param[in] isEnhanced If True, function was called by EnhancedMoveHue command and rate is a uint16 value. If False function + * was called by MoveHue command and rate is a uint8 value * @return true Success * @return false Failed */ @@ -1105,92 +1553,16 @@ bool ColorControlServer::moveToHueAndSaturationCommand(app::CommandHandler * com uint8_t saturation, uint16_t transitionTime, uint8_t optionsMask, uint8_t optionsOverride, bool isEnhanced) { - EndpointId endpoint = commandPath.mEndpointId; - - Status status = Status::Success; - uint16_t currentHue = 0; - uint16_t halfWay = isEnhanced ? HALF_MAX_UINT16T : HALF_MAX_UINT8T; - bool moveUp; - - Color16uTransitionState * colorSaturationTransitionState = getSaturationTransitionState(endpoint); - ColorHueTransitionState * colorHueTransitionState = getColorHueTransitionState(endpoint); - - VerifyOrExit(colorSaturationTransitionState != nullptr, status = Status::UnsupportedEndpoint); - VerifyOrExit(colorHueTransitionState != nullptr, status = Status::UnsupportedEndpoint); - - // limit checking: hue and saturation are 0..254. Spec dictates we ignore - // this and report a constraint error. - if ((!isEnhanced && hue > MAX_HUE_VALUE) || saturation > MAX_SATURATION_VALUE) - { - commandObj->AddStatus(commandPath, Status::ConstraintError); - return true; - } - - if (!shouldExecuteIfOff(endpoint, optionsMask, optionsOverride)) + if (!shouldExecuteIfOff(commandPath.mEndpointId, optionsMask, optionsOverride)) { commandObj->AddStatus(commandPath, Status::Success); return true; } - - // New command. Need to stop any active transitions. - stopAllColorTransitions(endpoint); - - // Handle color mode transition, if necessary. - if (isEnhanced) - { - handleModeSwitch(endpoint, ColorControlServer::ColorMode::COLOR_MODE_EHSV); - } - else - { - handleModeSwitch(endpoint, ColorControlServer::ColorMode::COLOR_MODE_HSV); - } - - // now, kick off the state machine. - initHueTransitionState(endpoint, colorHueTransitionState, isEnhanced); - - if (isEnhanced) - { - currentHue = colorHueTransitionState->currentEnhancedHue; - colorHueTransitionState->finalEnhancedHue = hue; - } - else - { - currentHue = static_cast(colorHueTransitionState->currentHue); - colorHueTransitionState->finalHue = static_cast(hue); - } - - // compute shortest direction - if (hue > currentHue) - { - moveUp = (hue - currentHue) < halfWay; - } - else - { - moveUp = (currentHue - hue) > halfWay; - } - - colorHueTransitionState->up = moveUp; - colorHueTransitionState->stepsRemaining = max(transitionTime, 1); - colorHueTransitionState->stepsTotal = colorHueTransitionState->stepsRemaining; - colorHueTransitionState->timeRemaining = transitionTime; - colorHueTransitionState->endpoint = endpoint; - colorHueTransitionState->repeat = false; - - initSaturationTransitionState(endpoint, colorSaturationTransitionState); - colorSaturationTransitionState->finalValue = saturation; - colorSaturationTransitionState->stepsRemaining = colorHueTransitionState->stepsRemaining; - colorSaturationTransitionState->stepsTotal = colorHueTransitionState->stepsRemaining; - colorSaturationTransitionState->timeRemaining = transitionTime; - colorSaturationTransitionState->endpoint = endpoint; - colorSaturationTransitionState->lowLimit = MIN_SATURATION_VALUE; - colorSaturationTransitionState->highLimit = MAX_SATURATION_VALUE; - - SetHSVRemainingTime(endpoint); - - // kick off the state machine: - scheduleTimerCallbackMs(configureHSVEventControl(endpoint), transitionTime ? TRANSITION_UPDATE_TIME_MS.count() : 0); - -exit: + Status status = moveToHueAndSaturation(hue, saturation, transitionTime, isEnhanced, commandPath.mEndpointId); +#ifdef EMBER_AF_PLUGIN_SCENES + // the scene has been changed (the value of color temp has changed) + Scenes::ScenesServer::Instance().MakeSceneInvalid(commandPath.mEndpointId); +#endif // EMBER_AF_PLUGIN_SCENES commandObj->AddStatus(commandPath, status); return true; } @@ -1204,8 +1576,8 @@ bool ColorControlServer::moveToHueAndSaturationCommand(app::CommandHandler * com * @param[in] transitionTime * @param[in] optionsMask * @param[in] optionsOverride - * @param[in] isEnhanced If True, function was called by EnhancedMoveHue command and rate is a uint16 value. If False function was - * called by MoveHue command and rate is a uint8 value + * @param[in] isEnhanced If True, function was called by EnhancedMoveHue command and rate is a uint16 value. If False function + * was called by MoveHue command and rate is a uint8 value * @return true Success * @return false Failed */ @@ -1370,62 +1742,25 @@ bool ColorControlServer::moveSaturationCommand(app::CommandHandler * commandObj, /** * @brief executes move to saturation command * - * @param saturation - * @param transitionTime - * @param optionsMask - * @param optionsOverride + * @param commandObj + * @param commandPath + * @param commandData * @return true * @return false */ bool ColorControlServer::moveToSaturationCommand(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, const Commands::MoveToSaturation::DecodableType & commandData) { - uint8_t saturation = commandData.saturation; - uint16_t transitionTime = commandData.transitionTime; - uint8_t optionsMask = commandData.optionsMask; - uint8_t optionsOverride = commandData.optionsOverride; - EndpointId endpoint = commandPath.mEndpointId; - Status status = Status::Success; - - Color16uTransitionState * colorSaturationTransitionState = getSaturationTransitionState(endpoint); - VerifyOrExit(colorSaturationTransitionState != nullptr, status = Status::UnsupportedEndpoint); - - // limit checking: hue and saturation are 0..254. Spec dictates we ignore - // this and report a malformed packet. - if (saturation > MAX_SATURATION_VALUE) - { - commandObj->AddStatus(commandPath, Status::ConstraintError); - return true; - } - - if (!shouldExecuteIfOff(endpoint, optionsMask, optionsOverride)) + if (!shouldExecuteIfOff(commandPath.mEndpointId, commandData.optionsMask, commandData.optionsOverride)) { commandObj->AddStatus(commandPath, Status::Success); return true; } - - // New command. Need to stop any active transitions. - stopAllColorTransitions(endpoint); - - // Handle color mode transition, if necessary. - handleModeSwitch(endpoint, COLOR_MODE_HSV); - - // now, kick off the state machine. - initSaturationTransitionState(endpoint, colorSaturationTransitionState); - colorSaturationTransitionState->finalValue = saturation; - colorSaturationTransitionState->stepsRemaining = max(transitionTime, 1); - colorSaturationTransitionState->stepsTotal = colorSaturationTransitionState->stepsRemaining; - colorSaturationTransitionState->timeRemaining = transitionTime; - colorSaturationTransitionState->endpoint = endpoint; - colorSaturationTransitionState->lowLimit = MIN_SATURATION_VALUE; - colorSaturationTransitionState->highLimit = MAX_SATURATION_VALUE; - - SetHSVRemainingTime(endpoint); - - // kick off the state machine: - scheduleTimerCallbackMs(configureHSVEventControl(endpoint), transitionTime ? TRANSITION_UPDATE_TIME_MS.count() : 0); - -exit: + Status status = moveToSaturation(commandData.saturation, commandData.transitionTime, commandPath.mEndpointId); +#ifdef EMBER_AF_PLUGIN_SCENES + // the scene has been changed (the value of color temp has changed) + Scenes::ScenesServer::Instance().MakeSceneInvalid(commandPath.mEndpointId); +#endif // EMBER_AF_PLUGIN_SCENES commandObj->AddStatus(commandPath, status); return true; } @@ -1607,6 +1942,10 @@ bool ColorControlServer::colorLoopCommand(app::CommandHandler * commandObj, cons } exit: +#ifdef EMBER_AF_PLUGIN_SCENES + // the scene has been changed (the value of color temp has changed) + Scenes::ScenesServer::Instance().MakeSceneInvalid(endpoint); +#endif // EMBER_AF_PLUGIN_SCENES commandObj->AddStatus(commandPath, status); return true; } @@ -1751,34 +2090,28 @@ EmberEventControl * ColorControlServer::configureXYEventControl(EndpointId endpo return controller; } -bool ColorControlServer::moveToColorCommand(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, - const Commands::MoveToColor::DecodableType & commandData) +/** + * @brief executes move to saturation command + * + * @param colorX target X + * @param colorY target Y + * @param transitionTime transition time in 10th of seconds + * @param endpoint target endpoint where to execute move + * @return Status::Success if successful,Status::UnsupportedEndpoint XY is not supported on the endpoint + */ +Status ColorControlServer::moveToColor(uint16_t colorX, uint16_t colorY, uint16_t transitionTime, EndpointId endpoint) { - uint16_t colorX = commandData.colorX; - uint16_t colorY = commandData.colorY; - uint16_t transitionTime = commandData.transitionTime; - uint8_t optionsMask = commandData.optionsMask; - uint8_t optionsOverride = commandData.optionsOverride; - EndpointId endpoint = commandPath.mEndpointId; - Status status = Status::Success; - Color16uTransitionState * colorXTransitionState = getXTransitionState(endpoint); Color16uTransitionState * colorYTransitionState = getYTransitionState(endpoint); - VerifyOrExit(colorXTransitionState != nullptr, status = Status::UnsupportedEndpoint); - VerifyOrExit(colorYTransitionState != nullptr, status = Status::UnsupportedEndpoint); - - if (!shouldExecuteIfOff(endpoint, optionsMask, optionsOverride)) - { - commandObj->AddStatus(commandPath, Status::Success); - return true; - } + VerifyOrReturnError(nullptr != colorXTransitionState, Status::UnsupportedEndpoint); + VerifyOrReturnError(nullptr != colorYTransitionState, Status::UnsupportedEndpoint); // New command. Need to stop any active transitions. stopAllColorTransitions(endpoint); // Handle color mode transition, if necessary. - handleModeSwitch(endpoint, COLOR_MODE_CIE_XY); + handleModeSwitch(endpoint, ColorMode::COLOR_MODE_CIE_XY); // now, kick off the state machine. Attributes::CurrentX::Get(endpoint, &(colorXTransitionState->initialValue)); @@ -1806,7 +2139,23 @@ bool ColorControlServer::moveToColorCommand(app::CommandHandler * commandObj, co // kick off the state machine: scheduleTimerCallbackMs(configureXYEventControl(endpoint), transitionTime ? TRANSITION_UPDATE_TIME_MS.count() : 0); -exit: + return Status::Success; +} + +bool ColorControlServer::moveToColorCommand(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, + const Commands::MoveToColor::DecodableType & commandData) +{ + if (!shouldExecuteIfOff(commandPath.mEndpointId, commandData.optionsMask, commandData.optionsOverride)) + { + commandObj->AddStatus(commandPath, Status::Success); + return true; + } + + Status status = moveToColor(commandData.colorX, commandData.colorY, commandData.transitionTime, commandPath.mEndpointId); +#ifdef EMBER_AF_PLUGIN_SCENES + // the scene has been changed (the value of color temp has changed) + Scenes::ScenesServer::Instance().MakeSceneInvalid(commandPath.mEndpointId); +#endif // EMBER_AF_PLUGIN_SCENES commandObj->AddStatus(commandPath, status); return true; } @@ -2041,13 +2390,14 @@ ColorControlServer::Color16uTransitionState * ColorControlServer::getTempTransit * @param aEndpoint * @param colorTemperature * @param transitionTime + * @return Status::Success if successful, Status::UnsupportedEndpoint if the endpoint doesn't support color temperature */ Status ColorControlServer::moveToColorTemp(EndpointId aEndpoint, uint16_t colorTemperature, uint16_t transitionTime) { EndpointId endpoint = aEndpoint; Color16uTransitionState * colorTempTransitionState = getTempTransitionState(endpoint); - VerifyOrReturnError(colorTempTransitionState != nullptr, Status::UnsupportedEndpoint); + VerifyOrReturnError(nullptr != colorTempTransitionState, Status::UnsupportedEndpoint); uint16_t temperatureMin = MIN_TEMPERATURE_VALUE; Attributes::ColorTempPhysicalMinMireds::Get(endpoint, &temperatureMin); @@ -2323,21 +2673,18 @@ bool ColorControlServer::moveColorTempCommand(app::CommandHandler * commandObj, bool ColorControlServer::moveToColorTempCommand(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, const Commands::MoveToColorTemperature::DecodableType & commandData) { - uint16_t colorTemperature = commandData.colorTemperatureMireds; - uint16_t transitionTime = commandData.transitionTime; - uint8_t optionsMask = commandData.optionsMask; - uint8_t optionsOverride = commandData.optionsOverride; - EndpointId endpoint = commandPath.mEndpointId; - - if (!shouldExecuteIfOff(endpoint, optionsMask, optionsOverride)) + if (!shouldExecuteIfOff(commandPath.mEndpointId, commandData.optionsMask, commandData.optionsOverride)) { commandObj->AddStatus(commandPath, Status::Success); return true; } - Status error = moveToColorTemp(endpoint, colorTemperature, transitionTime); - - commandObj->AddStatus(commandPath, error); + Status status = moveToColorTemp(commandPath.mEndpointId, commandData.colorTemperatureMireds, commandData.transitionTime); +#ifdef EMBER_AF_PLUGIN_SCENES + // the scene has been changed (the value of color temp has changed) + Scenes::ScenesServer::Instance().MakeSceneInvalid(commandPath.mEndpointId); +#endif // EMBER_AF_PLUGIN_SCENES + commandObj->AddStatus(commandPath, status); return true; } @@ -2686,6 +3033,11 @@ void emberAfColorControlClusterServerInitCallback(EndpointId endpoint) #ifdef EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_TEMP ColorControlServer::Instance().startUpColorTempCommand(endpoint); #endif // EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_TEMP +#ifdef EMBER_AF_PLUGIN_SCENES + // Registers Scene handlers for the level control cluster on the server + app::Clusters::Scenes::ScenesServer::Instance().RegisterSceneHandler(endpoint, + ColorControlServer::Instance().GetSceneHandler()); +#endif } void MatterColorControlClusterServerShutdownCallback(EndpointId endpoint) diff --git a/src/app/clusters/color-control-server/color-control-server.h b/src/app/clusters/color-control-server/color-control-server.h index 814bdfd5c1b7f5..2ec0f5e63b4a3f 100644 --- a/src/app/clusters/color-control-server/color-control-server.h +++ b/src/app/clusters/color-control-server/color-control-server.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -133,6 +134,8 @@ class ColorControlServer *********************************************************/ static ColorControlServer & Instance(); + chip::scenes::SceneHandler * GetSceneHandler(); + bool HasFeature(chip::EndpointId endpoint, Feature feature); chip::Protocols::InteractionModel::Status stopAllColorTransitions(chip::EndpointId endpoint); bool stopMoveStepCommand(chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, @@ -207,6 +210,10 @@ class ColorControlServer void cancelEndpointTimerCallback(EmberEventControl * control); #ifdef EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_HSV + chip::Protocols::InteractionModel::Status moveToSaturation(uint8_t saturation, uint16_t transitionTime, + chip::EndpointId endpoint); + chip::Protocols::InteractionModel::Status moveToHueAndSaturation(uint16_t hue, uint8_t saturation, uint16_t transitionTime, + bool isEnhanced, chip::EndpointId endpoint); ColorHueTransitionState * getColorHueTransitionState(chip::EndpointId endpoint); Color16uTransitionState * getSaturationTransitionState(chip::EndpointId endpoint); uint8_t getSaturation(chip::EndpointId endpoint); @@ -225,6 +232,8 @@ class ColorControlServer #endif // EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_HSV #ifdef EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_XY + chip::Protocols::InteractionModel::Status moveToColor(uint16_t colorX, uint16_t colorY, uint16_t transitionTime, + chip::EndpointId endpoint); Color16uTransitionState * getXTransitionState(chip::EndpointId endpoint); Color16uTransitionState * getYTransitionState(chip::EndpointId endpoint); uint16_t findNewColorValueFromStep(uint16_t oldValue, int16_t step); @@ -262,6 +271,8 @@ class ColorControlServer #endif // EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_TEMP EmberEventControl eventControls[kColorControlClusterServerMaxEndpointCount]; + + friend class DefaultColorControlSceneHandler; }; /********************************************************** From 9524929c032e28a16a1d39ef6b88cea662c943bd Mon Sep 17 00:00:00 2001 From: lpbeliveau-silabs Date: Tue, 5 Sep 2023 16:00:02 -0400 Subject: [PATCH 2/5] Reworked the logic when applying scene and added checks on boundaries --- .../color-control-server.cpp | 165 ++++++++++-------- 1 file changed, 93 insertions(+), 72 deletions(-) diff --git a/src/app/clusters/color-control-server/color-control-server.cpp b/src/app/clusters/color-control-server/color-control-server.cpp index d75e059cc718b2..6810ce37789c83 100644 --- a/src/app/clusters/color-control-server/color-control-server.cpp +++ b/src/app/clusters/color-control-server/color-control-server.cpp @@ -38,7 +38,7 @@ using chip::Protocols::InteractionModel::Status; class DefaultColorControlSceneHandler : public scenes::DefaultSceneHandlerImpl { public: - enum ColorControlEFS : uint8_t + enum class ColorControlEFS : uint8_t { kCurrentX = 0, kCurrentY, @@ -49,11 +49,9 @@ class DefaultColorControlSceneHandler : public scenes::DefaultSceneHandlerImpl kColorLoopTime, kColorTemperatureMireds, kEnhancedColorMode, + kColorControlMaxScenableAttributes, }; - // As per spec, 2 attributes are scenable in the level control cluster - static constexpr uint8_t kColorControlMaxScenableAttributes = 9; - DefaultColorControlSceneHandler() = default; ~DefaultColorControlSceneHandler() override {} @@ -67,6 +65,10 @@ class DefaultColorControlSceneHandler : public scenes::DefaultSceneHandlerImpl buffer[0] = ColorControl::Id; clusterBuffer.reduce_size(1); } + else + { + clusterBuffer.reduce_size(0); + } } // Default function for ColorControl cluster, only checks if ColorControl is enabled on the endpoint @@ -84,7 +86,8 @@ class DefaultColorControlSceneHandler : public scenes::DefaultSceneHandlerImpl { using AttributeValuePair = Scenes::Structs::AttributeValuePair::Type; - AttributeValuePair pairs[kColorControlMaxScenableAttributes]; + AttributeValuePair pairs[static_cast(ColorControlEFS::kColorControlMaxScenableAttributes)]; + size_t attributeCount = 0; if (ColorControlServer::Instance().HasFeature(endpoint, ColorControlServer::Feature::kXy)) @@ -157,14 +160,36 @@ class DefaultColorControlSceneHandler : public scenes::DefaultSceneHandlerImpl ReturnErrorOnFailure(DecodeAttributeValueList(serializedBytes, attributeValueList)); size_t attributeCount = 0; - uint32_t attributeBuffer[kColorControlMaxScenableAttributes]; - auto pair_iterator = attributeValueList.begin(); + auto pair_iterator = attributeValueList.begin(); // The color control cluster should have a maximum of 9 scenable attributes ReturnErrorOnFailure(attributeValueList.ComputeSize(&attributeCount)); - VerifyOrReturnError(attributeCount <= kColorControlMaxScenableAttributes, CHIP_ERROR_BUFFER_TOO_SMALL); + VerifyOrReturnError(attributeCount <= static_cast(ColorControlEFS::kColorControlMaxScenableAttributes), + CHIP_ERROR_BUFFER_TOO_SMALL); + // Retrieve the buffers for different modes +#ifdef EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_HSV + ColorControlServer::ColorHueTransitionState * colorHueTransitionState = + ColorControlServer::Instance().getColorHueTransitionState(endpoint); + ColorControlServer::Color16uTransitionState * colorSaturationTransitionState = + ColorControlServer::Instance().getSaturationTransitionState(endpoint); +#endif +#ifdef EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_XY + ColorControlServer::Color16uTransitionState * colorXTransitionState = + ColorControlServer::Instance().getXTransitionState(endpoint); + ColorControlServer::Color16uTransitionState * colorYTransitionState = + ColorControlServer::Instance().getYTransitionState(endpoint); +#endif +#ifdef EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_TEMP + ColorControlServer::Color16uTransitionState * colorTempTransitionState = + ColorControlServer::Instance().getTempTransitionState(endpoint); +#endif + + // Initialize action attributes to default values in case they are not in the scene + uint8_t targetColorMode = 0x00; + uint8_t loopActiveValue = 0x00; + uint8_t loopDirectionValue = 0x00; + uint16_t loopTimeValue = 0x0019; // Default loop time value according to spec - uint8_t attIdx; while (pair_iterator.Next()) { auto & decodePair = pair_iterator.GetValue(); @@ -172,47 +197,66 @@ class DefaultColorControlSceneHandler : public scenes::DefaultSceneHandlerImpl switch (decodePair.attributeID) { case Attributes::CurrentX::Id: - attIdx = ColorControlEFS::kCurrentX; + if (SupportsColorMode(endpoint, EMBER_ZCL_ENHANCED_COLOR_MODE_CURRENT_X_AND_CURRENT_Y)) + { + if (decodePair.attributeValue) + colorXTransitionState->finalValue = + std::min(static_cast(decodePair.attributeValue), colorXTransitionState->highLimit); + } break; case Attributes::CurrentY::Id: - attIdx = ColorControlEFS::kCurrentY; + if (SupportsColorMode(endpoint, EMBER_ZCL_ENHANCED_COLOR_MODE_CURRENT_X_AND_CURRENT_Y)) + { + colorYTransitionState->finalValue = + std::min(static_cast(decodePair.attributeValue), colorYTransitionState->highLimit); + } break; case Attributes::EnhancedCurrentHue::Id: - attIdx = ColorControlEFS::kEnhancedCurrentHue; + if (SupportsColorMode(endpoint, EMBER_ZCL_ENHANCED_COLOR_MODE_ENHANCED_CURRENT_HUE_AND_CURRENT_SATURATION)) + { + colorHueTransitionState->finalEnhancedHue = static_cast(decodePair.attributeValue); + } break; case Attributes::CurrentSaturation::Id: - attIdx = ColorControlEFS::kCurrentSaturation; + if (SupportsColorMode(endpoint, EMBER_ZCL_ENHANCED_COLOR_MODE_CURRENT_HUE_AND_CURRENT_SATURATION)) + { + colorSaturationTransitionState->finalValue = + std::min(static_cast(decodePair.attributeValue), colorSaturationTransitionState->highLimit); + } break; case Attributes::ColorLoopActive::Id: - attIdx = ColorControlEFS::kColorLoopActive; + loopActiveValue = static_cast(decodePair.attributeValue); break; case Attributes::ColorLoopDirection::Id: - attIdx = ColorControlEFS::kColorLoopDirection; + loopDirectionValue = static_cast(decodePair.attributeValue); break; case Attributes::ColorLoopTime::Id: - attIdx = ColorControlEFS::kColorLoopTime; + loopTimeValue = static_cast(decodePair.attributeValue); break; case Attributes::ColorTemperatureMireds::Id: - attIdx = ColorControlEFS::kColorTemperatureMireds; + if (SupportsColorMode(endpoint, EMBER_ZCL_ENHANCED_COLOR_MODE_COLOR_TEMPERATURE)) + { + colorTempTransitionState->finalValue = + std::min(static_cast(decodePair.attributeValue), colorTempTransitionState->highLimit); + } break; case Attributes::EnhancedColorMode::Id: - attIdx = ColorControlEFS::kEnhancedColorMode; + if (decodePair.attributeValue <= static_cast(ColorControlServer::ColorMode::COLOR_MODE_EHSV)) + { + targetColorMode = static_cast(decodePair.attributeValue); + } break; default: return CHIP_ERROR_INVALID_ARGUMENT; break; } - - attributeBuffer[attIdx] = decodePair.attributeValue; } - ReturnErrorOnFailure(pair_iterator.GetStatus()); // Switch to the mode saved in the scene - if (SupportsColorMode(endpoint, static_cast(attributeBuffer[ColorControlEFS::kEnhancedColorMode]))) + if (SupportsColorMode(endpoint, targetColorMode)) { - ColorControlServer::Instance().handleModeSwitch( - endpoint, static_cast(attributeBuffer[ColorControlEFS::kEnhancedColorMode])); + ColorControlServer::Instance().handleModeSwitch(endpoint, targetColorMode); } else { @@ -221,65 +265,42 @@ class DefaultColorControlSceneHandler : public scenes::DefaultSceneHandlerImpl uint16_t transitionTime10th = static_cast(timeMs / 100); -#ifdef EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_HSV - ColorControlServer::ColorHueTransitionState * colorHueTransitionState = - ColorControlServer::Instance().getColorHueTransitionState(endpoint); - ColorControlServer::Color16uTransitionState * colorSaturationTransitionState = - ColorControlServer::Instance().getSaturationTransitionState(endpoint); - colorSaturationTransitionState->finalValue = static_cast(attributeBuffer[ColorControlEFS::kCurrentSaturation]); - colorHueTransitionState->finalEnhancedHue = static_cast(attributeBuffer[ColorControlEFS::kEnhancedCurrentHue]); -#endif -#ifdef EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_XY - ColorControlServer::Color16uTransitionState * colorXTransitionState = - ColorControlServer::Instance().getXTransitionState(endpoint); - ColorControlServer::Color16uTransitionState * colorYTransitionState = - ColorControlServer::Instance().getYTransitionState(endpoint); - colorXTransitionState->finalValue = static_cast(attributeBuffer[ColorControlEFS::kCurrentX]); - colorYTransitionState->finalValue = static_cast(attributeBuffer[ColorControlEFS::kCurrentY]); -#endif -#ifdef EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_TEMP - ColorControlServer::Color16uTransitionState * colorTempTransitionState = - ColorControlServer::Instance().getTempTransitionState(endpoint); - colorTempTransitionState->finalValue = static_cast(attributeBuffer[ColorControlEFS::kColorTemperatureMireds]); -#endif - - // Set Loop Scene Attributes and start loop if scene stored active loop - Attributes::ColorLoopDirection::Set(endpoint, static_cast(attributeBuffer[ColorControlEFS::kColorLoopDirection])); - Attributes::ColorLoopTime::Set(endpoint, static_cast(attributeBuffer[ColorControlEFS::kColorLoopTime])); - if (attributeBuffer[ColorControlEFS::kColorLoopActive] == 1) + if (loopActiveValue == 1 && ColorControlServer::Instance().HasFeature(endpoint, ColorControlServer::Feature::kColorLoop)) { + // Set Loop Scene Attributes and start loop if scene stored active loop + Attributes::ColorLoopDirection::Set(endpoint, loopDirectionValue); + Attributes::ColorLoopTime::Set(endpoint, loopTimeValue); // Tries to applie color control loop ColorControlServer::Instance().startColorLoop(endpoint, true); } else { // Execute movement to value depending on the mode in the saved scene - switch (attributeBuffer[ColorControlEFS::kEnhancedColorMode]) + switch (targetColorMode) { case ColorControlServer::ColorMode::COLOR_MODE_HSV: #ifdef EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_HSV - ColorControlServer::Instance().moveToSaturation( - static_cast(attributeBuffer[ColorControlEFS::kCurrentSaturation]), transitionTime10th, endpoint); + ColorControlServer::Instance().moveToSaturation(static_cast(colorSaturationTransitionState->finalValue), + transitionTime10th, endpoint); #endif // EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_HSV break; case ColorControlServer::ColorMode::COLOR_MODE_CIE_XY: #ifdef EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_XY - ColorControlServer::Instance().moveToColor(static_cast(attributeBuffer[ColorControlEFS::kCurrentX]), - static_cast(attributeBuffer[ColorControlEFS::kCurrentY]), + ColorControlServer::Instance().moveToColor(colorXTransitionState->finalValue, colorYTransitionState->finalValue, transitionTime10th, endpoint); #endif // EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_XY break; case ColorControlServer::ColorMode::COLOR_MODE_TEMPERATURE: #ifdef EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_TEMP ColorControlServer::Instance().moveToColorTemp( - endpoint, static_cast(attributeBuffer[ColorControlEFS::kColorTemperatureMireds]), transitionTime10th); + endpoint, static_cast(colorTempTransitionState->finalValue), transitionTime10th); #endif // EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_TEMP break; case ColorControlServer::ColorMode::COLOR_MODE_EHSV: #ifdef EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_HSV ColorControlServer::Instance().moveToHueAndSaturation( - static_cast(attributeBuffer[ColorControlEFS::kEnhancedCurrentHue]), - static_cast(attributeBuffer[ColorControlEFS::kCurrentSaturation]), transitionTime10th, true, endpoint); + colorHueTransitionState->finalEnhancedHue, static_cast(colorSaturationTransitionState->finalValue), + transitionTime10th, true, endpoint); #endif // EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_HSV break; default: @@ -1166,13 +1187,6 @@ Status ColorControlServer::moveToSaturation(uint8_t saturation, uint16_t transit Color16uTransitionState * colorSaturationTransitionState = getSaturationTransitionState(endpoint); VerifyOrReturnError(nullptr != colorSaturationTransitionState, Status::UnsupportedEndpoint); - // limit checking: hue and saturation are 0..254. Spec dictates we ignore - // this and report a malformed packet. - if (saturation > MAX_SATURATION_VALUE) - { - return Status::ConstraintError; - } - if (transitionTime == 0) { transitionTime++; @@ -1226,13 +1240,6 @@ Status ColorControlServer::moveToHueAndSaturation(uint16_t hue, uint8_t saturati VerifyOrReturnError(nullptr != colorSaturationTransitionState, Status::UnsupportedEndpoint); VerifyOrReturnError(nullptr != colorHueTransitionState, Status::UnsupportedEndpoint); - // limit checking: hue and saturation are 0..254. Spec dictates we ignore - // this and report a constraint error. - if ((!isEnhanced && hue > MAX_HUE_VALUE) || saturation > MAX_SATURATION_VALUE) - { - return Status::ConstraintError; - } - if (transitionTime == 0) { transitionTime++; @@ -1553,6 +1560,14 @@ bool ColorControlServer::moveToHueAndSaturationCommand(app::CommandHandler * com uint8_t saturation, uint16_t transitionTime, uint8_t optionsMask, uint8_t optionsOverride, bool isEnhanced) { + // limit checking: hue and saturation are 0..254. Spec dictates we ignore + // this and report a constraint error. + if ((!isEnhanced && hue > MAX_HUE_VALUE) || saturation > MAX_SATURATION_VALUE) + { + commandObj->AddStatus(commandPath, Status::ConstraintError); + return true; + } + if (!shouldExecuteIfOff(commandPath.mEndpointId, optionsMask, optionsOverride)) { commandObj->AddStatus(commandPath, Status::Success); @@ -1751,6 +1766,12 @@ bool ColorControlServer::moveSaturationCommand(app::CommandHandler * commandObj, bool ColorControlServer::moveToSaturationCommand(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, const Commands::MoveToSaturation::DecodableType & commandData) { + if (commandData.saturation > MAX_SATURATION_VALUE) + { + commandObj->AddStatus(commandPath, Status::ConstraintError); + return true; + } + if (!shouldExecuteIfOff(commandPath.mEndpointId, commandData.optionsMask, commandData.optionsOverride)) { commandObj->AddStatus(commandPath, Status::Success); From 0a7cf3b6f89e6642e70b480dae2b01e945f62e5d Mon Sep 17 00:00:00 2001 From: lpbeliveau-silabs Date: Wed, 6 Sep 2023 13:46:06 -0400 Subject: [PATCH 3/5] Modified SerializeSave to explicitely use default value on Attributes::Get failure --- .../color-control-server.cpp | 56 +++++++++++++------ 1 file changed, 40 insertions(+), 16 deletions(-) diff --git a/src/app/clusters/color-control-server/color-control-server.cpp b/src/app/clusters/color-control-server/color-control-server.cpp index 6810ce37789c83..1545ec09f2f2af 100644 --- a/src/app/clusters/color-control-server/color-control-server.cpp +++ b/src/app/clusters/color-control-server/color-control-server.cpp @@ -92,12 +92,18 @@ class DefaultColorControlSceneHandler : public scenes::DefaultSceneHandlerImpl if (ColorControlServer::Instance().HasFeature(endpoint, ColorControlServer::Feature::kXy)) { - uint16_t xValue = 0x616B; // Default X value according to spec - Attributes::CurrentX::Get(endpoint, &xValue); + uint16_t xValue; + if (EMBER_ZCL_STATUS_SUCCESS != Attributes::CurrentX::Get(endpoint, &xValue)) + { + xValue = 0x616B; // Default X value according to spec + } AddAttributeValuePair(pairs, Attributes::CurrentX::Id, xValue, attributeCount); - uint16_t yValue = 0x607D; // Default Y value according to spec - Attributes::CurrentY::Get(endpoint, &yValue); + uint16_t yValue; + if (EMBER_ZCL_STATUS_SUCCESS != Attributes::CurrentY::Get(endpoint, &yValue)) + { + yValue = 0x607D; // Default Y value according to spec + } AddAttributeValuePair(pairs, Attributes::CurrentY::Id, yValue, attributeCount); } @@ -110,35 +116,53 @@ class DefaultColorControlSceneHandler : public scenes::DefaultSceneHandlerImpl if (ColorControlServer::Instance().HasFeature(endpoint, ColorControlServer::Feature::kHueAndSaturation)) { - uint8_t saturationValue = 0x00; - Attributes::CurrentSaturation::Get(endpoint, &saturationValue); + uint8_t saturationValue; + if (EMBER_ZCL_STATUS_SUCCESS != Attributes::CurrentSaturation::Get(endpoint, &saturationValue)) + { + saturationValue = 0x00; + } AddAttributeValuePair(pairs, Attributes::CurrentSaturation::Id, saturationValue, attributeCount); } if (ColorControlServer::Instance().HasFeature(endpoint, ColorControlServer::Feature::kColorLoop)) { - uint8_t loopActiveValue = 0x00; - Attributes::ColorLoopActive::Get(endpoint, &loopActiveValue); + uint8_t loopActiveValue; + if (EMBER_ZCL_STATUS_SUCCESS != Attributes::ColorLoopActive::Get(endpoint, &loopActiveValue)) + { + loopActiveValue = 0x00; + } AddAttributeValuePair(pairs, Attributes::ColorLoopActive::Id, loopActiveValue, attributeCount); - uint8_t loopDirectionValue = 0x00; - Attributes::ColorLoopDirection::Get(endpoint, &loopDirectionValue); + uint8_t loopDirectionValue; + if (EMBER_ZCL_STATUS_SUCCESS != Attributes::ColorLoopDirection::Get(endpoint, &loopDirectionValue)) + { + loopDirectionValue = 0x00; + } AddAttributeValuePair(pairs, Attributes::ColorLoopDirection::Id, loopDirectionValue, attributeCount); - uint16_t loopTimeValue = 0x0019; // Default loop time value according to spec - Attributes::ColorLoopTime::Get(endpoint, &loopTimeValue); + uint16_t loopTimeValue; + if (EMBER_ZCL_STATUS_SUCCESS != Attributes::ColorLoopTime::Get(endpoint, &loopTimeValue)) + { + loopTimeValue = 0x0019; // Default loop time value according to spec + } AddAttributeValuePair(pairs, Attributes::ColorLoopTime::Id, loopTimeValue, attributeCount); } if (ColorControlServer::Instance().HasFeature(endpoint, ColorControlServer::Feature::kColorTemperature)) { - uint16_t temperatureValue = 0x00FA; - Attributes::ColorTemperatureMireds::Get(endpoint, &temperatureValue); + uint16_t temperatureValue; + if (EMBER_ZCL_STATUS_SUCCESS != Attributes::ColorTemperatureMireds::Get(endpoint, &temperatureValue)) + { + temperatureValue = 0x00FA; // Default temperature value according to spec + } AddAttributeValuePair(pairs, Attributes::ColorTemperatureMireds::Id, temperatureValue, attributeCount); } - uint8_t modeValue = ColorControlServer::ColorMode::COLOR_MODE_CIE_XY; - Attributes::EnhancedColorMode::Get(endpoint, &modeValue); + uint8_t modeValue; + if (EMBER_ZCL_STATUS_SUCCESS != Attributes::EnhancedColorMode::Get(endpoint, &modeValue)) + { + modeValue = ColorControlServer::ColorMode::COLOR_MODE_CIE_XY; // Default mode value according to spec + } AddAttributeValuePair(pairs, Attributes::EnhancedColorMode::Id, modeValue, attributeCount); app::DataModel::List attributeValueList(pairs, attributeCount); From fda4a77671cd09962563a33a14327b5c66d77a56 Mon Sep 17 00:00:00 2001 From: lpbeliveau-silabs Date: Tue, 12 Sep 2023 13:26:25 -0400 Subject: [PATCH 4/5] Removed enum for attribute index as no longer needed, reverted the changes in the move to commands introduced by the helpers --- .../color-control-server.cpp | 52 ++++++------------- 1 file changed, 17 insertions(+), 35 deletions(-) diff --git a/src/app/clusters/color-control-server/color-control-server.cpp b/src/app/clusters/color-control-server/color-control-server.cpp index 1545ec09f2f2af..320f6c230663b9 100644 --- a/src/app/clusters/color-control-server/color-control-server.cpp +++ b/src/app/clusters/color-control-server/color-control-server.cpp @@ -38,19 +38,9 @@ using chip::Protocols::InteractionModel::Status; class DefaultColorControlSceneHandler : public scenes::DefaultSceneHandlerImpl { public: - enum class ColorControlEFS : uint8_t - { - kCurrentX = 0, - kCurrentY, - kEnhancedCurrentHue, - kCurrentSaturation, - kColorLoopActive, - kColorLoopDirection, - kColorLoopTime, - kColorTemperatureMireds, - kEnhancedColorMode, - kColorControlMaxScenableAttributes, - }; + // As per spec, 9 attributes are scenable in the color control cluster, if new scenables attributes are added, this value should + // be updated. + static constexpr uint8_t kColorControlScenableAttributesCount = 9; DefaultColorControlSceneHandler() = default; ~DefaultColorControlSceneHandler() override {} @@ -86,7 +76,7 @@ class DefaultColorControlSceneHandler : public scenes::DefaultSceneHandlerImpl { using AttributeValuePair = Scenes::Structs::AttributeValuePair::Type; - AttributeValuePair pairs[static_cast(ColorControlEFS::kColorControlMaxScenableAttributes)]; + AttributeValuePair pairs[kColorControlScenableAttributesCount]; size_t attributeCount = 0; @@ -188,8 +178,7 @@ class DefaultColorControlSceneHandler : public scenes::DefaultSceneHandlerImpl // The color control cluster should have a maximum of 9 scenable attributes ReturnErrorOnFailure(attributeValueList.ComputeSize(&attributeCount)); - VerifyOrReturnError(attributeCount <= static_cast(ColorControlEFS::kColorControlMaxScenableAttributes), - CHIP_ERROR_BUFFER_TOO_SMALL); + VerifyOrReturnError(attributeCount <= kColorControlScenableAttributesCount, CHIP_ERROR_BUFFER_TOO_SMALL); // Retrieve the buffers for different modes #ifdef EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_HSV ColorControlServer::ColorHueTransitionState * colorHueTransitionState = @@ -294,7 +283,7 @@ class DefaultColorControlSceneHandler : public scenes::DefaultSceneHandlerImpl // Set Loop Scene Attributes and start loop if scene stored active loop Attributes::ColorLoopDirection::Set(endpoint, loopDirectionValue); Attributes::ColorLoopTime::Set(endpoint, loopTimeValue); - // Tries to applie color control loop + // Tries to apply color control loop ColorControlServer::Instance().startColorLoop(endpoint, true); } else @@ -1211,11 +1200,6 @@ Status ColorControlServer::moveToSaturation(uint8_t saturation, uint16_t transit Color16uTransitionState * colorSaturationTransitionState = getSaturationTransitionState(endpoint); VerifyOrReturnError(nullptr != colorSaturationTransitionState, Status::UnsupportedEndpoint); - if (transitionTime == 0) - { - transitionTime++; - } - // New command. Need to stop any active transitions. stopAllColorTransitions(endpoint); @@ -1225,8 +1209,9 @@ Status ColorControlServer::moveToSaturation(uint8_t saturation, uint16_t transit // now, kick off the state machine. initSaturationTransitionState(endpoint, colorSaturationTransitionState); colorSaturationTransitionState->finalValue = saturation; - colorSaturationTransitionState->stepsRemaining = transitionTime; - colorSaturationTransitionState->stepsTotal = transitionTime; + colorSaturationTransitionState->stepsRemaining = max(transitionTime, 1); + colorSaturationTransitionState->stepsTotal = colorSaturationTransitionState->stepsRemaining; + colorSaturationTransitionState->timeRemaining = transitionTime; colorSaturationTransitionState->endpoint = endpoint; colorSaturationTransitionState->lowLimit = MIN_SATURATION_VALUE; colorSaturationTransitionState->highLimit = MAX_SATURATION_VALUE; @@ -1234,7 +1219,7 @@ Status ColorControlServer::moveToSaturation(uint8_t saturation, uint16_t transit SetHSVRemainingTime(endpoint); // kick off the state machine: - scheduleTimerCallbackMs(configureHSVEventControl(endpoint), TRANSITION_UPDATE_TIME_MS.count()); + scheduleTimerCallbackMs(configureHSVEventControl(endpoint), transitionTime ? TRANSITION_UPDATE_TIME_MS.count() : 0); return Status::Success; } @@ -1264,11 +1249,6 @@ Status ColorControlServer::moveToHueAndSaturation(uint16_t hue, uint8_t saturati VerifyOrReturnError(nullptr != colorSaturationTransitionState, Status::UnsupportedEndpoint); VerifyOrReturnError(nullptr != colorHueTransitionState, Status::UnsupportedEndpoint); - if (transitionTime == 0) - { - transitionTime++; - } - // New command. Need to stop any active transitions. stopAllColorTransitions(endpoint); @@ -1307,15 +1287,17 @@ Status ColorControlServer::moveToHueAndSaturation(uint16_t hue, uint8_t saturati } colorHueTransitionState->up = moveUp; - colorHueTransitionState->stepsRemaining = transitionTime; - colorHueTransitionState->stepsTotal = transitionTime; + colorHueTransitionState->stepsRemaining = max(transitionTime, 1); + colorHueTransitionState->stepsTotal = colorHueTransitionState->stepsRemaining; + colorHueTransitionState->timeRemaining = transitionTime; colorHueTransitionState->endpoint = endpoint; colorHueTransitionState->repeat = false; initSaturationTransitionState(endpoint, colorSaturationTransitionState); colorSaturationTransitionState->finalValue = saturation; - colorSaturationTransitionState->stepsRemaining = transitionTime; - colorSaturationTransitionState->stepsTotal = transitionTime; + colorSaturationTransitionState->stepsRemaining = colorHueTransitionState->stepsRemaining; + colorSaturationTransitionState->stepsTotal = colorHueTransitionState->stepsRemaining; + colorSaturationTransitionState->timeRemaining = transitionTime; colorSaturationTransitionState->endpoint = endpoint; colorSaturationTransitionState->lowLimit = MIN_SATURATION_VALUE; colorSaturationTransitionState->highLimit = MAX_SATURATION_VALUE; @@ -1323,7 +1305,7 @@ Status ColorControlServer::moveToHueAndSaturation(uint16_t hue, uint8_t saturati SetHSVRemainingTime(endpoint); // kick off the state machine: - scheduleTimerCallbackMs(configureHSVEventControl(endpoint), TRANSITION_UPDATE_TIME_MS.count()); + scheduleTimerCallbackMs(configureHSVEventControl(endpoint), transitionTime ? TRANSITION_UPDATE_TIME_MS.count() : 0); return Status::Success; } From e8dd4b402235ca0fd26d0e6ba8b6882ae094db59 Mon Sep 17 00:00:00 2001 From: lpbeliveau-silabs Date: Tue, 12 Sep 2023 14:30:37 -0400 Subject: [PATCH 5/5] Fix commenting --- .../color-control-server/color-control-server.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/app/clusters/color-control-server/color-control-server.cpp b/src/app/clusters/color-control-server/color-control-server.cpp index 320f6c230663b9..cc8c9ab935550b 100644 --- a/src/app/clusters/color-control-server/color-control-server.cpp +++ b/src/app/clusters/color-control-server/color-control-server.cpp @@ -1581,7 +1581,6 @@ bool ColorControlServer::moveToHueAndSaturationCommand(app::CommandHandler * com } Status status = moveToHueAndSaturation(hue, saturation, transitionTime, isEnhanced, commandPath.mEndpointId); #ifdef EMBER_AF_PLUGIN_SCENES - // the scene has been changed (the value of color temp has changed) Scenes::ScenesServer::Instance().MakeSceneInvalid(commandPath.mEndpointId); #endif // EMBER_AF_PLUGIN_SCENES commandObj->AddStatus(commandPath, status); @@ -1772,6 +1771,8 @@ bool ColorControlServer::moveSaturationCommand(app::CommandHandler * commandObj, bool ColorControlServer::moveToSaturationCommand(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, const Commands::MoveToSaturation::DecodableType & commandData) { + // limit checking: saturation is 0..254. Spec dictates we ignore + // this and report a malformed packet. if (commandData.saturation > MAX_SATURATION_VALUE) { commandObj->AddStatus(commandPath, Status::ConstraintError); @@ -1785,7 +1786,6 @@ bool ColorControlServer::moveToSaturationCommand(app::CommandHandler * commandOb } Status status = moveToSaturation(commandData.saturation, commandData.transitionTime, commandPath.mEndpointId); #ifdef EMBER_AF_PLUGIN_SCENES - // the scene has been changed (the value of color temp has changed) Scenes::ScenesServer::Instance().MakeSceneInvalid(commandPath.mEndpointId); #endif // EMBER_AF_PLUGIN_SCENES commandObj->AddStatus(commandPath, status); @@ -1970,7 +1970,6 @@ bool ColorControlServer::colorLoopCommand(app::CommandHandler * commandObj, cons exit: #ifdef EMBER_AF_PLUGIN_SCENES - // the scene has been changed (the value of color temp has changed) Scenes::ScenesServer::Instance().MakeSceneInvalid(endpoint); #endif // EMBER_AF_PLUGIN_SCENES commandObj->AddStatus(commandPath, status); @@ -2180,7 +2179,6 @@ bool ColorControlServer::moveToColorCommand(app::CommandHandler * commandObj, co Status status = moveToColor(commandData.colorX, commandData.colorY, commandData.transitionTime, commandPath.mEndpointId); #ifdef EMBER_AF_PLUGIN_SCENES - // the scene has been changed (the value of color temp has changed) Scenes::ScenesServer::Instance().MakeSceneInvalid(commandPath.mEndpointId); #endif // EMBER_AF_PLUGIN_SCENES commandObj->AddStatus(commandPath, status); @@ -2708,7 +2706,6 @@ bool ColorControlServer::moveToColorTempCommand(app::CommandHandler * commandObj Status status = moveToColorTemp(commandPath.mEndpointId, commandData.colorTemperatureMireds, commandData.transitionTime); #ifdef EMBER_AF_PLUGIN_SCENES - // the scene has been changed (the value of color temp has changed) Scenes::ScenesServer::Instance().MakeSceneInvalid(commandPath.mEndpointId); #endif // EMBER_AF_PLUGIN_SCENES commandObj->AddStatus(commandPath, status); @@ -3061,7 +3058,7 @@ void emberAfColorControlClusterServerInitCallback(EndpointId endpoint) ColorControlServer::Instance().startUpColorTempCommand(endpoint); #endif // EMBER_AF_PLUGIN_COLOR_CONTROL_SERVER_TEMP #ifdef EMBER_AF_PLUGIN_SCENES - // Registers Scene handlers for the level control cluster on the server + // Registers Scene handlers for the color control cluster on the server app::Clusters::Scenes::ScenesServer::Instance().RegisterSceneHandler(endpoint, ColorControlServer::Instance().GetSceneHandler()); #endif