diff --git a/examples/tv-app/android/include/messages/MessagesManager.cpp b/examples/tv-app/android/include/messages/MessagesManager.cpp new file mode 100644 index 00000000000000..dcd48f028b961e --- /dev/null +++ b/examples/tv-app/android/include/messages/MessagesManager.cpp @@ -0,0 +1,54 @@ +/** + * + * Copyright (c) 2024 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MessagesManager.h" + +using namespace std; +using namespace chip::app; +using namespace chip::app::Clusters::Messages; + +// Commands +void MessagesManager::HandlePresentMessagesRequest( + const chip::ByteSpan & messageId, const MessagePriorityEnum & priority, + const chip::BitMask & messageControl, const chip::app::DataModel::Nullable & startTime, + const chip::app::DataModel::Nullable & duration, const chip::CharSpan & messageText, + const chip::Optional> & responses) +{ + // TODO: Present Message +} + +void MessagesManager::HandleCancelMessagesRequest(const chip::app::DataModel::DecodableList & messageIds) +{ + // TODO: Cancel Message +} + +// Attributes +CHIP_ERROR MessagesManager::HandleGetMessages(chip::app::AttributeValueEncoder & aEncoder) +{ + return aEncoder.EncodeEmptyList(); +} + +CHIP_ERROR MessagesManager::HandleGetActiveMessageIds(chip::app::AttributeValueEncoder & aEncoder) +{ + return aEncoder.EncodeEmptyList(); +} + +// Global Attributes +uint32_t MessagesManager::GetFeatureMap(chip::EndpointId endpoint) +{ + return 1; +} diff --git a/examples/tv-app/android/include/messages/MessagesManager.h b/examples/tv-app/android/include/messages/MessagesManager.h new file mode 100644 index 00000000000000..5f06d253846c4f --- /dev/null +++ b/examples/tv-app/android/include/messages/MessagesManager.h @@ -0,0 +1,45 @@ +/** + * + * Copyright (c) 2024 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +class MessagesManager : public chip::app::Clusters::Messages::Delegate +{ +public: + // Commands + void HandlePresentMessagesRequest( + const chip::ByteSpan & messageId, const chip::app::Clusters::Messages::MessagePriorityEnum & priority, + const chip::BitMask & messageControl, + const chip::app::DataModel::Nullable & startTime, const chip::app::DataModel::Nullable & duration, + const chip::CharSpan & messageText, + const chip::Optional> & responses) override; + void HandleCancelMessagesRequest(const chip::app::DataModel::DecodableList & messageIds) override; + + // Attributes + CHIP_ERROR HandleGetMessages(chip::app::AttributeValueEncoder & aEncoder) override; + CHIP_ERROR HandleGetActiveMessageIds(chip::app::AttributeValueEncoder & aEncoder) override; + + // Global Attributes + uint32_t GetFeatureMap(chip::EndpointId endpoint) override; + +protected: + std::list mMessages; +}; diff --git a/examples/tv-app/tv-common/BUILD.gn b/examples/tv-app/tv-common/BUILD.gn index c522709c0a8e85..699a13a20ec6e0 100644 --- a/examples/tv-app/tv-common/BUILD.gn +++ b/examples/tv-app/tv-common/BUILD.gn @@ -64,6 +64,8 @@ source_set("tv-common-sources") { "clusters/media-input/MediaInputManager.h", "clusters/media-playback/MediaPlaybackManager.cpp", "clusters/media-playback/MediaPlaybackManager.h", + "clusters/messages/MessagesManager.cpp", + "clusters/messages/MessagesManager.h", "clusters/target-navigator/TargetNavigatorManager.cpp", "clusters/target-navigator/TargetNavigatorManager.h", "clusters/wake-on-lan/WakeOnLanManager.cpp", diff --git a/examples/tv-app/tv-common/clusters/messages/MessagesManager.cpp b/examples/tv-app/tv-common/clusters/messages/MessagesManager.cpp new file mode 100644 index 00000000000000..11068dc1d720c8 --- /dev/null +++ b/examples/tv-app/tv-common/clusters/messages/MessagesManager.cpp @@ -0,0 +1,81 @@ +/** + * + * Copyright (c) 2024 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MessagesManager.h" + +#include + +using namespace std; +using namespace chip::app; +using namespace chip::app::Clusters::Messages; +using Message = chip::app::Clusters::Messages::Structs::MessageStruct::Type; + +// Commands +void MessagesManager::HandlePresentMessagesRequest( + const chip::ByteSpan & messageId, const MessagePriorityEnum & priority, + const chip::BitMask & messageControl, const chip::app::DataModel::Nullable & startTime, + const chip::app::DataModel::Nullable & duration, const chip::CharSpan & messageText, + const chip::Optional> & responses) +{ + Message message{ + // TODO: Enable id + chip::ByteSpan(), priority, messageControl, startTime, duration, + // TODO: Enable text + chip::CharSpan() + // TODO: Convert responses to Optional> message.responses = responses; + }; + + mMessages.push_back(message); + // Add your code to present Message +} + +void MessagesManager::HandleCancelMessagesRequest(const chip::app::DataModel::DecodableList & messageIds) +{ + // TODO: Cancel Message +} + +// Attributes +CHIP_ERROR MessagesManager::HandleGetMessages(chip::app::AttributeValueEncoder & aEncoder) +{ + return aEncoder.EncodeList([this](const auto & encoder) -> CHIP_ERROR { + for (Message & entry : mMessages) + { + ReturnErrorOnFailure(encoder.Encode(entry)); + } + return CHIP_NO_ERROR; + }); +} + +CHIP_ERROR MessagesManager::HandleGetActiveMessageIds(chip::app::AttributeValueEncoder & aEncoder) +{ + return aEncoder.EncodeList([this](const auto & encoder) -> CHIP_ERROR { + for (Message & entry : mMessages) + { + ReturnErrorOnFailure(encoder.Encode(entry.messageID)); + } + return CHIP_NO_ERROR; + }); +} + +// Global Attributes +uint32_t MessagesManager::GetFeatureMap(chip::EndpointId endpoint) +{ + uint32_t featureMap = 0; + Attributes::FeatureMap::Get(endpoint, &featureMap); + return featureMap; +} diff --git a/examples/tv-app/tv-common/clusters/messages/MessagesManager.h b/examples/tv-app/tv-common/clusters/messages/MessagesManager.h new file mode 100644 index 00000000000000..97ff5096b8ef7d --- /dev/null +++ b/examples/tv-app/tv-common/clusters/messages/MessagesManager.h @@ -0,0 +1,48 @@ +/** + * + * Copyright (c) 2024 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include +#include + +class MessagesManager : public chip::app::Clusters::Messages::Delegate +{ +public: + // Commands + void HandlePresentMessagesRequest( + const chip::ByteSpan & messageId, const chip::app::Clusters::Messages::MessagePriorityEnum & priority, + const chip::BitMask & messageControl, + const chip::app::DataModel::Nullable & startTime, const chip::app::DataModel::Nullable & duration, + const chip::CharSpan & messageText, + const chip::Optional< + chip::app::DataModel::DecodableList> & + responses) override; + void HandleCancelMessagesRequest(const chip::app::DataModel::DecodableList & messageIds) override; + + // Attributes + CHIP_ERROR HandleGetMessages(chip::app::AttributeValueEncoder & aEncoder) override; + CHIP_ERROR HandleGetActiveMessageIds(chip::app::AttributeValueEncoder & aEncoder) override; + + // Global Attributes + uint32_t GetFeatureMap(chip::EndpointId endpoint) override; + +protected: + std::list mMessages; +}; diff --git a/examples/tv-app/tv-common/src/ZCLCallbacks.cpp b/examples/tv-app/tv-common/src/ZCLCallbacks.cpp index c56a3f73e76afc..cffb427473a6b0 100644 --- a/examples/tv-app/tv-common/src/ZCLCallbacks.cpp +++ b/examples/tv-app/tv-common/src/ZCLCallbacks.cpp @@ -37,6 +37,7 @@ #include "low-power/LowPowerManager.h" #include "media-input/MediaInputManager.h" #include "media-playback/MediaPlaybackManager.h" +#include "messages/MessagesManager.h" #include "target-navigator/TargetNavigatorManager.h" #include "wake-on-lan/WakeOnLanManager.h" @@ -56,6 +57,7 @@ static KeypadInputManager keypadInputManager; static LowPowerManager lowPowerManager; static MediaInputManager mediaInputManager; static MediaPlaybackManager mediaPlaybackManager; +static MessagesManager messagesManager; static TargetNavigatorManager targetNavigatorManager; static WakeOnLanManager wakeOnLanManager; } // namespace @@ -170,6 +172,12 @@ void emberAfMediaPlaybackClusterInitCallback(EndpointId endpoint) MediaPlayback::SetDefaultDelegate(endpoint, &mediaPlaybackManager); } +void emberAfMessagesClusterInitCallback(EndpointId endpoint) +{ + ChipLogProgress(Zcl, "TV Linux App: Messages::SetDefaultDelegate"); + Messages::SetDefaultDelegate(endpoint, &messagesManager); +} + void emberAfTargetNavigatorClusterInitCallback(EndpointId endpoint) { ChipLogProgress(Zcl, "TV Linux App: TargetNavigator::SetDefaultDelegate"); diff --git a/examples/tv-app/tv-common/tv-app.cmake b/examples/tv-app/tv-common/tv-app.cmake index bfa554d0e77ec5..fd9822347e9bfb 100644 --- a/examples/tv-app/tv-common/tv-app.cmake +++ b/examples/tv-app/tv-common/tv-app.cmake @@ -62,6 +62,7 @@ macro(chip_add_tv_app_common target) ${CHIP_TV_COMMON_BASE_DIR}/clusters/low-power/LowPowerManager.cpp ${CHIP_TV_COMMON_BASE_DIR}/clusters/media-input/MediaInputManager.cpp ${CHIP_TV_COMMON_BASE_DIR}/clusters/media-playback/MediaPlaybackManager.cpp + ${CHIP_TV_COMMON_BASE_DIR}/clusters/messages/MessagesManager.cpp ${CHIP_TV_COMMON_BASE_DIR}/clusters/target-navigator/TargetNavigatorManager.cpp ${CHIP_TV_COMMON_BASE_DIR}/clusters/wake-on-lan/WakeOnLanManager.cpp diff --git a/src/app/clusters/messages-server/messages-delegate.h b/src/app/clusters/messages-server/messages-delegate.h new file mode 100644 index 00000000000000..b836a6b34c3ab2 --- /dev/null +++ b/src/app/clusters/messages-server/messages-delegate.h @@ -0,0 +1,60 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include +#include +#include + +namespace chip { +namespace app { +namespace Clusters { +namespace Messages { + +using MessageResponseOption = chip::app::Clusters::Messages::Structs::MessageResponseOptionStruct::Type; + +class Delegate +{ +public: + // Commands + virtual void + HandlePresentMessagesRequest(const ByteSpan & messageId, const MessagePriorityEnum & priority, + const chip::BitMask & messageControl, + const DataModel::Nullable & startTime, const DataModel::Nullable & duration, + const CharSpan & messageText, + const chip::Optional> & responses) = 0; + virtual void HandleCancelMessagesRequest(const DataModel::DecodableList & messageIds) = 0; + + // Attributes + virtual CHIP_ERROR HandleGetMessages(app::AttributeValueEncoder & aEncoder) = 0; + virtual CHIP_ERROR HandleGetActiveMessageIds(app::AttributeValueEncoder & aEncoder) = 0; + + // Global Attributes + bool HasFeature(chip::EndpointId endpoint, Feature feature); + virtual uint32_t GetFeatureMap(chip::EndpointId endpoint) = 0; + + virtual ~Delegate() = default; +}; + +} // namespace Messages +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/src/app/clusters/messages-server/messages-server.cpp b/src/app/clusters/messages-server/messages-server.cpp index 2cca0913eb4320..33a403e64b3d86 100644 --- a/src/app/clusters/messages-server/messages-server.cpp +++ b/src/app/clusters/messages-server/messages-server.cpp @@ -1,29 +1,199 @@ -#include "messages-server.h" +/** + * + * Copyright (c) 2024 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include #include -#include -#include -#include -#include +#include #include #include - -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include using namespace chip; using namespace chip::app; +using namespace chip::app::Clusters; using namespace chip::app::Clusters::Messages; +using chip::app::LogEvent; using chip::Protocols::InteractionModel::Status; +static constexpr size_t kMessagesDelegateTableSize = + MATTER_DM_MESSAGES_CLUSTER_SERVER_ENDPOINT_COUNT + CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT; +static_assert(kMessagesDelegateTableSize <= kEmberInvalidEndpointIndex, "Messages Delegate table size error"); + +// ----------------------------------------------------------------------------- +// Delegate Implementation + +namespace { + +Delegate * gDelegateTable[kMessagesDelegateTableSize] = { nullptr }; + +Delegate * GetDelegate(EndpointId endpoint) +{ + ChipLogProgress(Zcl, "MessagesCluster NOT returning delegate for endpoint:%u", endpoint); + + uint16_t ep = emberAfGetClusterServerEndpointIndex(endpoint, Messages::Id, MATTER_DM_MESSAGES_CLUSTER_SERVER_ENDPOINT_COUNT); + return (ep >= kMessagesDelegateTableSize ? nullptr : gDelegateTable[ep]); +} + +bool isDelegateNull(Delegate * delegate, EndpointId endpoint) +{ + if (delegate == nullptr) + { + ChipLogProgress(Zcl, "Message Cluster has no delegate set for endpoint:%u", endpoint); + return true; + } + return false; +} +} // namespace + +namespace chip { +namespace app { +namespace Clusters { +namespace Messages { + +void SetDefaultDelegate(EndpointId endpoint, Delegate * delegate) +{ + uint16_t ep = emberAfGetClusterServerEndpointIndex(endpoint, Messages::Id, MATTER_DM_MESSAGES_CLUSTER_SERVER_ENDPOINT_COUNT); + // if endpoint is found + if (ep < kMessagesDelegateTableSize) + { + gDelegateTable[ep] = delegate; + } +} + +bool Delegate::HasFeature(chip::EndpointId endpoint, Feature feature) +{ + uint32_t featureMap = GetFeatureMap(endpoint); + return (featureMap & chip::to_underlying(feature)); +} + +} // namespace Messages +} // namespace Clusters +} // namespace app +} // namespace chip + +// ----------------------------------------------------------------------------- +// Attribute Accessor Implementation + +namespace { + +class MessagesAttrAccess : public app::AttributeAccessInterface +{ +public: + MessagesAttrAccess() : app::AttributeAccessInterface(Optional::Missing(), Messages::Id) {} + + CHIP_ERROR Read(const app::ConcreteReadAttributePath & aPath, app::AttributeValueEncoder & aEncoder) override; + +private: + CHIP_ERROR ReadMessages(app::AttributeValueEncoder & aEncoder, Delegate * delegate); + CHIP_ERROR ReadActiveMessageIds(app::AttributeValueEncoder & aEncoder, Delegate * delegate); + CHIP_ERROR ReadFeatureFlagAttribute(EndpointId endpoint, app::AttributeValueEncoder & aEncoder, Delegate * delegate); +}; + +MessagesAttrAccess gMessagesAttrAccess; + +CHIP_ERROR MessagesAttrAccess::Read(const app::ConcreteReadAttributePath & aPath, app::AttributeValueEncoder & aEncoder) +{ + EndpointId endpoint = aPath.mEndpointId; + Delegate * delegate = GetDelegate(endpoint); + + switch (aPath.mAttributeId) + { + case app::Clusters::Messages::Attributes::Messages::Id: + if (isDelegateNull(delegate, endpoint)) + { + return aEncoder.EncodeEmptyList(); + } + + return ReadMessages(aEncoder, delegate); + case app::Clusters::Messages::Attributes::ActiveMessageIDs::Id: + if (isDelegateNull(delegate, endpoint)) + { + return aEncoder.EncodeEmptyList(); + } + + return ReadActiveMessageIds(aEncoder, delegate); + case app::Clusters::Messages::Attributes::FeatureMap::Id: + if (isDelegateNull(delegate, endpoint)) + { + return CHIP_NO_ERROR; + } + + return ReadFeatureFlagAttribute(endpoint, aEncoder, delegate); + default: + break; + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR MessagesAttrAccess::ReadFeatureFlagAttribute(EndpointId endpoint, app::AttributeValueEncoder & aEncoder, + Delegate * delegate) +{ + uint32_t featureFlag = delegate->GetFeatureMap(endpoint); + return aEncoder.Encode(featureFlag); +} + +CHIP_ERROR MessagesAttrAccess::ReadMessages(app::AttributeValueEncoder & aEncoder, Delegate * delegate) +{ + return delegate->HandleGetMessages(aEncoder); +} + +CHIP_ERROR MessagesAttrAccess::ReadActiveMessageIds(app::AttributeValueEncoder & aEncoder, Delegate * delegate) +{ + return delegate->HandleGetActiveMessageIds(aEncoder); +} + +} // anonymous namespace + bool emberAfMessagesClusterPresentMessagesRequestCallback( chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, const chip::app::Clusters::Messages::Commands::PresentMessagesRequest::DecodableType & commandData) { + CHIP_ERROR err = CHIP_NO_ERROR; + EndpointId endpoint = commandPath.mEndpointId; + Status status = Status::Success; + + auto & messageId = commandData.messageID; + auto & priority = commandData.priority; + auto & messageControl = commandData.messageControl; + auto & startTime = commandData.startTime; + auto & duration = commandData.duration; + auto & messageText = commandData.messageText; + auto & responses = commandData.responses; + + Delegate * delegate = GetDelegate(endpoint); + VerifyOrExit(isDelegateNull(delegate, endpoint) != true, err = CHIP_ERROR_INCORRECT_STATE); + + delegate->HandlePresentMessagesRequest(messageId, priority, messageControl, startTime, duration, messageText, responses); + +exit: + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "emberAfMessagesClusterPresentMessagesRequestCallback error: %s", err.AsString()); + status = Status::Failure; + } + + commandObj->AddStatus(commandPath, status); return true; } @@ -35,7 +205,29 @@ bool emberAfMessagesClusterCancelMessagesRequestCallback( chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, const chip::app::Clusters::Messages::Commands::CancelMessagesRequest::DecodableType & commandData) { + CHIP_ERROR err = CHIP_NO_ERROR; + EndpointId endpoint = commandPath.mEndpointId; + Status status = Status::Success; + + auto & messageIds = commandData.messageIDs; + + Delegate * delegate = GetDelegate(endpoint); + VerifyOrExit(isDelegateNull(delegate, endpoint) != true, err = CHIP_ERROR_INCORRECT_STATE); + + delegate->HandleCancelMessagesRequest(messageIds); + +exit: + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "emberAfMessagesClusterCancelMessagesRequestCallback error: %s", err.AsString()); + status = Status::Failure; + } + + commandObj->AddStatus(commandPath, status); return true; } -void MatterMessagesPluginServerInitCallback() {} +void MatterMessagesPluginServerInitCallback() +{ + registerAttributeAccessOverride(&gMessagesAttrAccess); +} diff --git a/src/app/clusters/messages-server/messages-server.h b/src/app/clusters/messages-server/messages-server.h index 767534f8f880b8..a1ae6c03c4b5d6 100644 --- a/src/app/clusters/messages-server/messages-server.h +++ b/src/app/clusters/messages-server/messages-server.h @@ -17,13 +17,18 @@ #pragma once +#include "messages-delegate.h" #include #include namespace chip { namespace app { namespace Clusters { -namespace Messaging {} // namespace Messaging +namespace Messages { + +void SetDefaultDelegate(EndpointId endpoint, Delegate * delegate); + +} // namespace Messages } // namespace Clusters } // namespace app } // namespace chip