Adding Media subscriptions support to Linux tv-casting-app (#22709)

diff --git a/examples/tv-casting-app/linux/CastingUtils.cpp b/examples/tv-casting-app/linux/CastingUtils.cpp
index 0e6fc87..6f5ea53 100644
--- a/examples/tv-casting-app/linux/CastingUtils.cpp
+++ b/examples/tv-casting-app/linux/CastingUtils.cpp
@@ -26,6 +26,7 @@
 // TODO: Accept these values over CLI
 const char * kContentUrl        = "https://www.test.com/videoid";
 const char * kContentDisplayStr = "Test video";
+int gInitialContextVal          = 121212;
 
 CHIP_ERROR DiscoverCommissioners()
 {
@@ -114,14 +115,69 @@
     ChipLogProgress(AppServer, "LaunchURLResponseCallback called with %" CHIP_ERROR_FORMAT, err.Format());
 }
 
+void OnCurrentStateReadResponseSuccess(
+    void * context, chip::app::Clusters::MediaPlayback::Attributes::CurrentState::TypeInfo::DecodableArgType responseData)
+{
+    ChipLogProgress(AppServer, "OnCurrentStateReadResponseSuccess called with responseData: %d", static_cast<int>(responseData));
+    switch (responseData)
+    {
+    case chip::app::Clusters::MediaPlayback::PlaybackStateEnum::kPlaying:
+        ChipLogProgress(AppServer, "OnCurrentStateReadResponseSuccess CurrentState: Playing");
+        break;
+    case chip::app::Clusters::MediaPlayback::PlaybackStateEnum::kPaused:
+        ChipLogProgress(AppServer, "OnCurrentStateReadResponseSuccess CurrentState: Paused");
+        break;
+    case chip::app::Clusters::MediaPlayback::PlaybackStateEnum::kNotPlaying:
+        ChipLogProgress(AppServer, "OnCurrentStateReadResponseSuccess CurrentState: Not Playing");
+        break;
+    case chip::app::Clusters::MediaPlayback::PlaybackStateEnum::kBuffering:
+        ChipLogProgress(AppServer, "OnCurrentStateReadResponseSuccess CurrentState: Buffering");
+        break;
+    default:
+        ChipLogError(AppServer, "OnCurrentStateReadResponseSuccess Invalid CurrentState!");
+        break;
+    }
+
+    if (context != nullptr)
+    {
+        ChipLogProgress(AppServer, "OnCurrentStateReadResponseSuccess context value: %d", *(static_cast<int *>(context)));
+    }
+}
+
+void OnCurrentStateReadResponseFailure(void * context, CHIP_ERROR err)
+{
+    ChipLogProgress(AppServer, "OnCurrentStateReadResponseFailure called with %" CHIP_ERROR_FORMAT, err.Format());
+}
+
+void OnCurrentStateSubscriptionEstablished(void * context)
+{
+    ChipLogProgress(AppServer, "OnCurrentStateSubscriptionEstablished called");
+    if (context != nullptr)
+    {
+        ChipLogProgress(AppServer, "OnCurrentStateSubscriptionEstablished context value: %d", *(static_cast<int *>(context)));
+    }
+}
+
 void HandleCommissioningCompleteCallback(CHIP_ERROR err)
 {
     ChipLogProgress(AppServer, "HandleCommissioningCompleteCallback called with %" CHIP_ERROR_FORMAT, err.Format());
     if (err == CHIP_NO_ERROR)
     {
-        ReturnOnFailure(
-            CastingServer::GetInstance()->ContentLauncherLaunchURL(kContentUrl, kContentDisplayStr, LaunchURLResponseCallback));
-        ChipLogProgress(AppServer, "ContentLauncherLaunchURL called successfully");
+        // Subscribe to a media attribute
+        err = CastingServer::GetInstance()->MediaPlayback_SubscribeToCurrentState(
+            static_cast<void *>(&gInitialContextVal), OnCurrentStateReadResponseSuccess, OnCurrentStateReadResponseFailure, 0, 4000,
+            OnCurrentStateSubscriptionEstablished);
+        if (err != CHIP_NO_ERROR)
+        {
+            ChipLogError(AppServer, "MediaPlayback_SubscribeToCurrentState call failed!");
+        }
+
+        // Send a media command
+        err = CastingServer::GetInstance()->ContentLauncherLaunchURL(kContentUrl, kContentDisplayStr, LaunchURLResponseCallback);
+        if (err != CHIP_NO_ERROR)
+        {
+            ChipLogError(AppServer, "ContentLauncherLaunchURL call failed!");
+        }
     }
 }
 
diff --git a/examples/tv-casting-app/linux/CastingUtils.h b/examples/tv-casting-app/linux/CastingUtils.h
index 177bedf..172b018 100644
--- a/examples/tv-casting-app/linux/CastingUtils.h
+++ b/examples/tv-casting-app/linux/CastingUtils.h
@@ -42,6 +42,13 @@
 
 void LaunchURLResponseCallback(CHIP_ERROR err);
 
+void OnCurrentStateReadResponseSuccess(
+    void * context, chip::app::Clusters::MediaPlayback::Attributes::CurrentState::TypeInfo::DecodableArgType responseData);
+
+void OnCurrentStateReadResponseFailure(void * context, CHIP_ERROR err);
+
+void OnCurrentStateSubscriptionEstablished(void * context);
+
 #if CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT
 void HandleUDCSendExpiration(chip::System::Layer * aSystemLayer, void * context);
 #endif // CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT
diff --git a/examples/tv-casting-app/tv-casting-common/BUILD.gn b/examples/tv-casting-app/tv-casting-common/BUILD.gn
index b421fad..dd3644a 100644
--- a/examples/tv-casting-app/tv-casting-common/BUILD.gn
+++ b/examples/tv-casting-app/tv-casting-common/BUILD.gn
@@ -49,16 +49,20 @@
     "commands/common/CHIPCommand.cpp",
     "include/ApplicationLauncher.h",
     "include/CastingServer.h",
+    "include/Channel.h",
     "include/ContentLauncher.h",
     "include/KeypadInput.h",
     "include/LevelControl.h",
+    "include/MediaBase.h",
     "include/MediaCommandBase.h",
     "include/MediaPlayback.h",
+    "include/MediaSubscriptionBase.h",
     "include/TargetEndpointInfo.h",
     "include/TargetNavigator.h",
     "include/TargetVideoPlayerInfo.h",
     "src/ApplicationLauncher.cpp",
     "src/CastingServer.cpp",
+    "src/Channel.cpp",
     "src/ContentLauncher.cpp",
     "src/KeypadInput.cpp",
     "src/LevelControl.cpp",
diff --git a/examples/tv-casting-app/tv-casting-common/include/ApplicationLauncher.h b/examples/tv-casting-app/tv-casting-common/include/ApplicationLauncher.h
index d3365be..3444955 100644
--- a/examples/tv-casting-app/tv-casting-common/include/ApplicationLauncher.h
+++ b/examples/tv-casting-app/tv-casting-common/include/ApplicationLauncher.h
@@ -17,10 +17,12 @@
  */
 
 #include "MediaCommandBase.h"
+#include "MediaSubscriptionBase.h"
 
 #include <functional>
 #include <zap-generated/CHIPClusters.h>
 
+// COMMAND CLASSES
 class LaunchAppCommand
     : public MediaCommandBase<chip::app::Clusters::ApplicationLauncher::Commands::LaunchApp::Type,
                               chip::app::Clusters::ApplicationLauncher::Commands::LauncherResponse::DecodableType>
@@ -51,3 +53,11 @@
     CHIP_ERROR Invoke(chip::app::Clusters::ApplicationLauncher::Structs::Application::Type application,
                       std::function<void(CHIP_ERROR)> responseCallback);
 };
+
+// SUBSCRIBER CLASSES
+class CurrentAppSubscriber
+    : public MediaSubscriptionBase<chip::app::Clusters::ApplicationLauncher::Attributes::CurrentApp::TypeInfo>
+{
+public:
+    CurrentAppSubscriber() : MediaSubscriptionBase(chip::app::Clusters::ApplicationLauncher::Id) {}
+};
diff --git a/examples/tv-casting-app/tv-casting-common/include/CastingServer.h b/examples/tv-casting-app/tv-casting-common/include/CastingServer.h
index b29f82e..b91a0e0 100644
--- a/examples/tv-casting-app/tv-casting-common/include/CastingServer.h
+++ b/examples/tv-casting-app/tv-casting-common/include/CastingServer.h
@@ -19,6 +19,7 @@
 #pragma once
 
 #include "ApplicationLauncher.h"
+#include "Channel.h"
 #include "ContentLauncher.h"
 #include "KeypadInput.h"
 #include "LevelControl.h"
@@ -27,13 +28,15 @@
 #include "TargetNavigator.h"
 #include "TargetVideoPlayerInfo.h"
 
+#include <app-common/zap-generated/cluster-objects.h>
 #include <app/server/Server.h>
 #include <controller/CHIPCommissionableNodeController.h>
 #include <functional>
+#include <tv-casting-app/zap-generated/CHIPClientCallbacks.h>
 #include <zap-generated/CHIPClusters.h>
 
 constexpr chip::System::Clock::Seconds16 kCommissioningWindowTimeout = chip::System::Clock::Seconds16(3 * 60);
-constexpr chip::EndpointId kTvEndpoint                               = 1;
+constexpr chip::EndpointId kTvEndpoint                               = 4;
 
 /**
  * @brief Represents a TV Casting server that can get the casting app commissioned
@@ -73,6 +76,9 @@
     chip::FabricIndex CurrentFabricIndex() { return mTargetVideoPlayerInfo.GetFabricIndex(); }
     void SetDefaultFabricIndex();
 
+    /**
+     * @brief Content Launcher cluster
+     */
     CHIP_ERROR ContentLauncher_LaunchURL(
         const char * contentUrl, const char * contentDisplayStr,
         chip::Optional<chip::app::Clusters::ContentLauncher::Structs::BrandingInformation::Type> brandingInformation,
@@ -80,10 +86,43 @@
     CHIP_ERROR ContentLauncher_LaunchContent(chip::app::Clusters::ContentLauncher::Structs::ContentSearch::Type search,
                                              bool autoPlay, chip::Optional<chip::CharSpan> data,
                                              std::function<void(CHIP_ERROR)> responseCallback);
+
+    /**
+     * @brief Level Control cluster
+     */
     CHIP_ERROR LevelControl_Step(chip::app::Clusters::LevelControl::StepMode stepMode, uint8_t stepSize, uint16_t transitionTime,
                                  uint8_t optionMask, uint8_t optionOverride, std::function<void(CHIP_ERROR)> responseCallback);
     CHIP_ERROR LevelControl_MoveToLevel(uint8_t level, uint16_t transitionTime, uint8_t optionMask, uint8_t optionOverride,
                                         std::function<void(CHIP_ERROR)> responseCallback);
+
+    CHIP_ERROR
+    LevelControl_SubscribeToCurrentLevel(
+        void * context,
+        chip::Controller::ReadResponseSuccessCallback<
+            chip::app::Clusters::LevelControl::Attributes::CurrentLevel::TypeInfo::DecodableArgType>
+            successFn,
+        chip::Controller::ReadResponseFailureCallback failureFn, uint16_t minInterval, uint16_t maxInterval,
+        chip::Controller::SubscriptionEstablishedCallback onSubscriptionEstablished);
+    CHIP_ERROR
+    LevelControl_SubscribeToMinLevel(void * context,
+                                     chip::Controller::ReadResponseSuccessCallback<
+                                         chip::app::Clusters::LevelControl::Attributes::MinLevel::TypeInfo::DecodableArgType>
+                                         successFn,
+                                     chip::Controller::ReadResponseFailureCallback failureFn, uint16_t minInterval,
+                                     uint16_t maxInterval,
+                                     chip::Controller::SubscriptionEstablishedCallback onSubscriptionEstablished);
+    CHIP_ERROR
+    LevelControl_SubscribeToMaxLevel(void * context,
+                                     chip::Controller::ReadResponseSuccessCallback<
+                                         chip::app::Clusters::LevelControl::Attributes::MaxLevel::TypeInfo::DecodableArgType>
+                                         successFn,
+                                     chip::Controller::ReadResponseFailureCallback failureFn, uint16_t minInterval,
+                                     uint16_t maxInterval,
+                                     chip::Controller::SubscriptionEstablishedCallback onSubscriptionEstablished);
+
+    /**
+     * @brief Media Playback cluster
+     */
     CHIP_ERROR MediaPlayback_Play(std::function<void(CHIP_ERROR)> responseCallback);
     CHIP_ERROR MediaPlayback_Pause(std::function<void(CHIP_ERROR)> responseCallback);
     CHIP_ERROR MediaPlayback_StopPlayback(std::function<void(CHIP_ERROR)> responseCallback);
@@ -91,17 +130,116 @@
     CHIP_ERROR MediaPlayback_Seek(uint64_t position, std::function<void(CHIP_ERROR)> responseCallback);
     CHIP_ERROR MediaPlayback_SkipForward(uint64_t deltaPositionMilliseconds, std::function<void(CHIP_ERROR)> responseCallback);
     CHIP_ERROR MediaPlayback_SkipBackward(uint64_t deltaPositionMilliseconds, std::function<void(CHIP_ERROR)> responseCallback);
+
+    CHIP_ERROR MediaPlayback_SubscribeToCurrentState(
+        void * context,
+        chip::Controller::ReadResponseSuccessCallback<
+            chip::app::Clusters::MediaPlayback::Attributes::CurrentState::TypeInfo::DecodableArgType>
+            successFn,
+        chip::Controller::ReadResponseFailureCallback failureFn, uint16_t minInterval, uint16_t maxInterval,
+        chip::Controller::SubscriptionEstablishedCallback onSubscriptionEstablished);
+    CHIP_ERROR
+    MediaPlayback_SubscribeToStartTime(void * context,
+                                       chip::Controller::ReadResponseSuccessCallback<
+                                           chip::app::Clusters::MediaPlayback::Attributes::StartTime::TypeInfo::DecodableArgType>
+                                           successFn,
+                                       chip::Controller::ReadResponseFailureCallback failureFn, uint16_t minInterval,
+                                       uint16_t maxInterval,
+                                       chip::Controller::SubscriptionEstablishedCallback onSubscriptionEstablished);
+    CHIP_ERROR
+    MediaPlayback_SubscribeToDuration(void * context,
+                                      chip::Controller::ReadResponseSuccessCallback<
+                                          chip::app::Clusters::MediaPlayback::Attributes::Duration::TypeInfo::DecodableArgType>
+                                          successFn,
+                                      chip::Controller::ReadResponseFailureCallback failureFn, uint16_t minInterval,
+                                      uint16_t maxInterval,
+                                      chip::Controller::SubscriptionEstablishedCallback onSubscriptionEstablished);
+    CHIP_ERROR MediaPlayback_SubscribeToSampledPosition(
+        void * context,
+        chip::Controller::ReadResponseSuccessCallback<
+            chip::app::Clusters::MediaPlayback::Attributes::SampledPosition::TypeInfo::DecodableArgType>
+            successFn,
+        chip::Controller::ReadResponseFailureCallback failureFn, uint16_t minInterval, uint16_t maxInterval,
+        chip::Controller::SubscriptionEstablishedCallback onSubscriptionEstablished);
+    CHIP_ERROR MediaPlayback_SubscribeToPlaybackSpeed(
+        void * context,
+        chip::Controller::ReadResponseSuccessCallback<
+            chip::app::Clusters::MediaPlayback::Attributes::PlaybackSpeed::TypeInfo::DecodableArgType>
+            successFn,
+        chip::Controller::ReadResponseFailureCallback failureFn, uint16_t minInterval, uint16_t maxInterval,
+        chip::Controller::SubscriptionEstablishedCallback onSubscriptionEstablished);
+    CHIP_ERROR MediaPlayback_SubscribeToSeekRangeEnd(
+        void * context,
+        chip::Controller::ReadResponseSuccessCallback<
+            chip::app::Clusters::MediaPlayback::Attributes::SeekRangeEnd::TypeInfo::DecodableArgType>
+            successFn,
+        chip::Controller::ReadResponseFailureCallback failureFn, uint16_t minInterval, uint16_t maxInterval,
+        chip::Controller::SubscriptionEstablishedCallback onSubscriptionEstablished);
+    CHIP_ERROR MediaPlayback_SubscribeToSeekRangeStart(
+        void * context,
+        chip::Controller::ReadResponseSuccessCallback<
+            chip::app::Clusters::MediaPlayback::Attributes::SeekRangeStart::TypeInfo::DecodableArgType>
+            successFn,
+        chip::Controller::ReadResponseFailureCallback failureFn, uint16_t minInterval, uint16_t maxInterval,
+        chip::Controller::SubscriptionEstablishedCallback onSubscriptionEstablished);
+
+    /**
+     * @brief Application Launcher cluster
+     */
     CHIP_ERROR ApplicationLauncher_LaunchApp(chip::app::Clusters::ApplicationLauncher::Structs::Application::Type application,
                                              chip::Optional<chip::ByteSpan> data, std::function<void(CHIP_ERROR)> responseCallback);
     CHIP_ERROR ApplicationLauncher_StopApp(chip::app::Clusters::ApplicationLauncher::Structs::Application::Type application,
                                            std::function<void(CHIP_ERROR)> responseCallback);
     CHIP_ERROR ApplicationLauncher_HideApp(chip::app::Clusters::ApplicationLauncher::Structs::Application::Type application,
                                            std::function<void(CHIP_ERROR)> responseCallback);
+
+    CHIP_ERROR
+    ApplicationLauncher_SubscribeToCurrentApp(
+        void * context,
+        chip::Controller::ReadResponseSuccessCallback<
+            chip::app::Clusters::ApplicationLauncher::Attributes::CurrentApp::TypeInfo::DecodableArgType>
+            successFn,
+        chip::Controller::ReadResponseFailureCallback failureFn, uint16_t minInterval, uint16_t maxInterval,
+        chip::Controller::SubscriptionEstablishedCallback onSubscriptionEstablished);
+
+    /**
+     * @brief Target Navigator cluster
+     */
     CHIP_ERROR TargetNavigator_NavigateTarget(const uint8_t target, const chip::Optional<chip::CharSpan> data,
                                               std::function<void(CHIP_ERROR)> responseCallback);
+
+    CHIP_ERROR TargetNavigator_SubscribeToTargetList(
+        void * context,
+        chip::Controller::ReadResponseSuccessCallback<
+            chip::app::Clusters::TargetNavigator::Attributes::TargetList::TypeInfo::DecodableArgType>
+            successFn,
+        chip::Controller::ReadResponseFailureCallback failureFn, uint16_t minInterval, uint16_t maxInterval,
+        chip::Controller::SubscriptionEstablishedCallback onSubscriptionEstablished);
+    CHIP_ERROR TargetNavigator_SubscribeToCurrentTarget(
+        void * context,
+        chip::Controller::ReadResponseSuccessCallback<
+            chip::app::Clusters::TargetNavigator::Attributes::CurrentTarget::TypeInfo::DecodableArgType>
+            successFn,
+        chip::Controller::ReadResponseFailureCallback failureFn, uint16_t minInterval, uint16_t maxInterval,
+        chip::Controller::SubscriptionEstablishedCallback onSubscriptionEstablished);
+
+    /**
+     * @brief Keypad Input cluster
+     */
     CHIP_ERROR KeypadInput_SendKey(const chip::app::Clusters::KeypadInput::CecKeyCode keyCode,
                                    std::function<void(CHIP_ERROR)> responseCallback);
 
+    /**
+     * @brief Channel cluster
+     */
+    CHIP_ERROR Channel_ChangeChannelCommand(const chip::CharSpan & match, std::function<void(CHIP_ERROR)> responseCallback);
+    CHIP_ERROR Channel_SubscribeToLineup(
+        void * context,
+        chip::Controller::ReadResponseSuccessCallback<chip::app::Clusters::Channel::Attributes::Lineup::TypeInfo::DecodableArgType>
+            successFn,
+        chip::Controller::ReadResponseFailureCallback failureFn, uint16_t minInterval, uint16_t maxInterval,
+        chip::Controller::SubscriptionEstablishedCallback onSubscriptionEstablished);
+
 private:
     CHIP_ERROR InitBindingHandlers();
     static void DeviceEventCallback(const chip::DeviceLayer::ChipDeviceEvent * event, intptr_t arg);
@@ -127,6 +265,10 @@
     StepCommand mStepCommand;
     MoveToLevelCommand mMoveToLevelCommand;
 
+    CurrentLevelSubscriber mCurrentLevelSubscriber;
+    MinLevelSubscriber mMinLevelSubscriber;
+    MaxLevelSubscriber mMaxLevelSubscriber;
+
     /**
      * @brief Media Playback cluster
      */
@@ -138,6 +280,14 @@
     SkipForwardCommand mSkipForwardCommand;
     SkipBackwardCommand mSkipBackwardCommand;
 
+    CurrentStateSubscriber mCurrentStateSubscriber;
+    StartTimeSubscriber mStartTimeSubscriber;
+    DurationSubscriber mDurationSubscriber;
+    SampledPositionSubscriber mSampledPositionSubscriber;
+    PlaybackSpeedSubscriber mPlaybackSpeedSubscriber;
+    SeekRangeEndSubscriber mSeekRangeEndSubscriber;
+    SeekRangeStartSubscriber mSeekRangeStartSubscriber;
+
     /**
      * @brief Application Launcher cluster
      */
@@ -145,13 +295,25 @@
     StopAppCommand mStopAppCommand;
     HideAppCommand mHideAppCommand;
 
+    CurrentAppSubscriber mCurrentAppSubscriber;
+
     /**
      * @brief Target Navigator cluster
      */
     NavigateTargetCommand mNavigateTargetCommand;
 
+    TargetListSubscriber mTargetListSubscriber;
+    CurrentTargetSubscriber mCurrentTargetSubscriber;
+
     /**
      * @brief Keypad Input cluster
      */
     SendKeyCommand mSendKeyCommand;
+
+    /**
+     * @brief Channel cluster
+     */
+    ChangeChannelCommand mChangeChannelCommand;
+
+    LineupSubscriber mLineupSubscriber;
 };
diff --git a/examples/tv-casting-app/tv-casting-common/include/Channel.h b/examples/tv-casting-app/tv-casting-common/include/Channel.h
new file mode 100644
index 0000000..1a0a18e
--- /dev/null
+++ b/examples/tv-casting-app/tv-casting-common/include/Channel.h
@@ -0,0 +1,40 @@
+/*
+ *
+ *    Copyright (c) 2022 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.
+ */
+
+#include "MediaCommandBase.h"
+#include "MediaSubscriptionBase.h"
+
+#include <functional>
+#include <zap-generated/CHIPClusters.h>
+
+// COMMAND CLASSES
+class ChangeChannelCommand : public MediaCommandBase<chip::app::Clusters::Channel::Commands::ChangeChannel::Type,
+                                                     chip::app::Clusters::Channel::Commands::ChangeChannelResponse::DecodableType>
+{
+public:
+    ChangeChannelCommand() : MediaCommandBase(chip::app::Clusters::Channel::Id) {}
+
+    CHIP_ERROR Invoke(const chip::CharSpan & match, std::function<void(CHIP_ERROR)> responseCallback);
+};
+
+// SUBSCRIBER CLASSES
+class LineupSubscriber : public MediaSubscriptionBase<chip::app::Clusters::Channel::Attributes::Lineup::TypeInfo>
+{
+public:
+    LineupSubscriber() : MediaSubscriptionBase(chip::app::Clusters::Channel::Id) {}
+};
diff --git a/examples/tv-casting-app/tv-casting-common/include/LevelControl.h b/examples/tv-casting-app/tv-casting-common/include/LevelControl.h
index d431e3b..a1fd172 100644
--- a/examples/tv-casting-app/tv-casting-common/include/LevelControl.h
+++ b/examples/tv-casting-app/tv-casting-common/include/LevelControl.h
@@ -17,10 +17,12 @@
  */
 
 #include "MediaCommandBase.h"
+#include "MediaSubscriptionBase.h"
 
 #include <functional>
 #include <zap-generated/CHIPClusters.h>
 
+// COMMAND CLASSES
 class StepCommand
     : public MediaCommandBase<chip::app::Clusters::LevelControl::Commands::Step::Type, chip::app::DataModel::NullObjectType>
 {
@@ -41,3 +43,22 @@
     CHIP_ERROR Invoke(uint8_t level, chip::app::DataModel::Nullable<uint16_t> transitionTime, uint8_t optionMask,
                       uint8_t optionOverride, std::function<void(CHIP_ERROR)> responseCallback);
 };
+
+// SUBSCRIBER CLASSES
+class CurrentLevelSubscriber : public MediaSubscriptionBase<chip::app::Clusters::LevelControl::Attributes::CurrentLevel::TypeInfo>
+{
+public:
+    CurrentLevelSubscriber() : MediaSubscriptionBase(chip::app::Clusters::LevelControl::Id) {}
+};
+
+class MinLevelSubscriber : public MediaSubscriptionBase<chip::app::Clusters::LevelControl::Attributes::MinLevel::TypeInfo>
+{
+public:
+    MinLevelSubscriber() : MediaSubscriptionBase(chip::app::Clusters::LevelControl::Id) {}
+};
+
+class MaxLevelSubscriber : public MediaSubscriptionBase<chip::app::Clusters::LevelControl::Attributes::MaxLevel::TypeInfo>
+{
+public:
+    MaxLevelSubscriber() : MediaSubscriptionBase(chip::app::Clusters::LevelControl::Id) {}
+};
diff --git a/examples/tv-casting-app/tv-casting-common/include/MediaBase.h b/examples/tv-casting-app/tv-casting-common/include/MediaBase.h
new file mode 100644
index 0000000..3686161
--- /dev/null
+++ b/examples/tv-casting-app/tv-casting-common/include/MediaBase.h
@@ -0,0 +1,55 @@
+/*
+ *
+ *    Copyright (c) 2022 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 "TargetVideoPlayerInfo.h"
+
+#include <controller/CHIPCluster.h>
+
+class MediaBase
+{
+public:
+    MediaBase(chip::ClusterId clusterId) { mClusterId = clusterId; }
+
+    CHIP_ERROR SetTarget(TargetVideoPlayerInfo & targetVideoPlayerInfo, chip::EndpointId tvEndpoint)
+    {
+        auto deviceProxy = targetVideoPlayerInfo.GetOperationalDeviceProxy();
+        if (deviceProxy == nullptr)
+        {
+            ChipLogError(AppServer, "Failed in getting an instance of OperationalDeviceProxy");
+            return CHIP_ERROR_PEER_NODE_NOT_FOUND;
+        }
+        mTargetVideoPlayerInfo = &targetVideoPlayerInfo;
+        mTvEndpoint            = tvEndpoint;
+        return CHIP_NO_ERROR;
+    }
+
+    class MediaClusterBase : public chip::Controller::ClusterBase
+    {
+    public:
+        MediaClusterBase(chip::Messaging::ExchangeManager & exchangeManager, const chip::SessionHandle & session,
+                         chip::ClusterId cluster, chip::EndpointId endpoint) :
+            ClusterBase(exchangeManager, session, cluster, endpoint)
+        {}
+    };
+
+protected:
+    chip::ClusterId mClusterId;
+    TargetVideoPlayerInfo * mTargetVideoPlayerInfo = nullptr;
+    chip::EndpointId mTvEndpoint;
+};
diff --git a/examples/tv-casting-app/tv-casting-common/include/MediaCommandBase.h b/examples/tv-casting-app/tv-casting-common/include/MediaCommandBase.h
index a125f18..1b9c3d2 100644
--- a/examples/tv-casting-app/tv-casting-common/include/MediaCommandBase.h
+++ b/examples/tv-casting-app/tv-casting-common/include/MediaCommandBase.h
@@ -17,29 +17,15 @@
  */
 #pragma once
 
-#include "TargetVideoPlayerInfo.h"
+#include "MediaBase.h"
 
-#include <controller/CHIPCluster.h>
 #include <functional>
 
 template <typename RequestType, typename ResponseType>
-class MediaCommandBase
+class MediaCommandBase : public MediaBase
 {
 public:
-    MediaCommandBase(chip::ClusterId clusterId) { mClusterId = clusterId; }
-
-    CHIP_ERROR SetTarget(TargetVideoPlayerInfo & targetVideoPlayerInfo, chip::EndpointId tvEndpoint)
-    {
-        auto deviceProxy = targetVideoPlayerInfo.GetOperationalDeviceProxy();
-        if (deviceProxy == nullptr)
-        {
-            ChipLogError(AppServer, "Failed in getting an instance of OperationalDeviceProxy");
-            return CHIP_ERROR_PEER_NODE_NOT_FOUND;
-        }
-        mTargetVideoPlayerInfo = &targetVideoPlayerInfo;
-        mTvEndpoint            = tvEndpoint;
-        return CHIP_NO_ERROR;
-    }
+    MediaCommandBase(chip::ClusterId clusterId) : MediaBase(clusterId) {}
 
     CHIP_ERROR Invoke(RequestType request, std::function<void(CHIP_ERROR)> responseCallback)
     {
@@ -50,15 +36,6 @@
 
         sResponseCallback = responseCallback;
 
-        class MediaClusterBase : public chip::Controller::ClusterBase
-        {
-        public:
-            MediaClusterBase(chip::Messaging::ExchangeManager & exchangeManager, const chip::SessionHandle & session,
-                             chip::ClusterId cluster, chip::EndpointId endpoint) :
-                ClusterBase(exchangeManager, session, cluster, endpoint)
-            {}
-        };
-
         MediaClusterBase cluster(*deviceProxy->GetExchangeManager(), deviceProxy->GetSecureSession().Value(), mClusterId,
                                  mTvEndpoint);
         return cluster.InvokeCommand(request, nullptr, OnSuccess, OnFailure);
@@ -69,9 +46,6 @@
     static void OnFailure(void * context, CHIP_ERROR error) { sResponseCallback(error); }
 
 protected:
-    chip::ClusterId mClusterId;
-    TargetVideoPlayerInfo * mTargetVideoPlayerInfo = nullptr;
-    chip::EndpointId mTvEndpoint;
     static std::function<void(CHIP_ERROR)> sResponseCallback;
 };
 
diff --git a/examples/tv-casting-app/tv-casting-common/include/MediaPlayback.h b/examples/tv-casting-app/tv-casting-common/include/MediaPlayback.h
index ca551bd..c4d540f 100644
--- a/examples/tv-casting-app/tv-casting-common/include/MediaPlayback.h
+++ b/examples/tv-casting-app/tv-casting-common/include/MediaPlayback.h
@@ -17,10 +17,12 @@
  */
 
 #include "MediaCommandBase.h"
+#include "MediaSubscriptionBase.h"
 
 #include <functional>
 #include <zap-generated/CHIPClusters.h>
 
+// COMMAND CLASSES
 class PlayCommand : public MediaCommandBase<chip::app::Clusters::MediaPlayback::Commands::Play::Type,
                                             chip::app::Clusters::MediaPlayback::Commands::PlaybackResponse::DecodableType>
 {
@@ -83,3 +85,49 @@
 
     CHIP_ERROR Invoke(uint64_t deltaPositionMilliseconds, std::function<void(CHIP_ERROR)> responseCallback);
 };
+
+// SUBSCRIBER CLASSES
+class CurrentStateSubscriber : public MediaSubscriptionBase<chip::app::Clusters::MediaPlayback::Attributes::CurrentState::TypeInfo>
+{
+public:
+    CurrentStateSubscriber() : MediaSubscriptionBase(chip::app::Clusters::MediaPlayback::Id) {}
+};
+
+class StartTimeSubscriber : public MediaSubscriptionBase<chip::app::Clusters::MediaPlayback::Attributes::StartTime::TypeInfo>
+{
+public:
+    StartTimeSubscriber() : MediaSubscriptionBase(chip::app::Clusters::MediaPlayback::Id) {}
+};
+
+class DurationSubscriber : public MediaSubscriptionBase<chip::app::Clusters::MediaPlayback::Attributes::Duration::TypeInfo>
+{
+public:
+    DurationSubscriber() : MediaSubscriptionBase(chip::app::Clusters::MediaPlayback::Id) {}
+};
+
+class SampledPositionSubscriber
+    : public MediaSubscriptionBase<chip::app::Clusters::MediaPlayback::Attributes::SampledPosition::TypeInfo>
+{
+public:
+    SampledPositionSubscriber() : MediaSubscriptionBase(chip::app::Clusters::MediaPlayback::Id) {}
+};
+
+class PlaybackSpeedSubscriber
+    : public MediaSubscriptionBase<chip::app::Clusters::MediaPlayback::Attributes::PlaybackSpeed::TypeInfo>
+{
+public:
+    PlaybackSpeedSubscriber() : MediaSubscriptionBase(chip::app::Clusters::MediaPlayback::Id) {}
+};
+
+class SeekRangeEndSubscriber : public MediaSubscriptionBase<chip::app::Clusters::MediaPlayback::Attributes::SeekRangeEnd::TypeInfo>
+{
+public:
+    SeekRangeEndSubscriber() : MediaSubscriptionBase(chip::app::Clusters::MediaPlayback::Id) {}
+};
+
+class SeekRangeStartSubscriber
+    : public MediaSubscriptionBase<chip::app::Clusters::MediaPlayback::Attributes::SeekRangeStart::TypeInfo>
+{
+public:
+    SeekRangeStartSubscriber() : MediaSubscriptionBase(chip::app::Clusters::MediaPlayback::Id) {}
+};
diff --git a/examples/tv-casting-app/tv-casting-common/include/MediaSubscriptionBase.h b/examples/tv-casting-app/tv-casting-common/include/MediaSubscriptionBase.h
new file mode 100644
index 0000000..e2a065e
--- /dev/null
+++ b/examples/tv-casting-app/tv-casting-common/include/MediaSubscriptionBase.h
@@ -0,0 +1,44 @@
+/*
+ *
+ *    Copyright (c) 2022 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 "MediaBase.h"
+
+template <typename TypeInfo>
+class MediaSubscriptionBase : public MediaBase
+{
+public:
+    MediaSubscriptionBase(chip::ClusterId clusterId) : MediaBase(clusterId) {}
+
+    CHIP_ERROR SubscribeAttribute(void * context,
+                                  chip::Controller::ReadResponseSuccessCallback<typename TypeInfo::DecodableArgType> successFn,
+                                  chip::Controller::ReadResponseFailureCallback failureFn, uint16_t minInterval,
+                                  uint16_t maxInterval, chip::Controller::SubscriptionEstablishedCallback onSubscriptionEstablished)
+    {
+        VerifyOrDieWithMsg(mTargetVideoPlayerInfo != nullptr, AppServer, "Target unknown");
+
+        auto deviceProxy = mTargetVideoPlayerInfo->GetOperationalDeviceProxy();
+        ReturnErrorCodeIf(deviceProxy == nullptr || !deviceProxy->ConnectionReady(), CHIP_ERROR_PEER_NODE_NOT_FOUND);
+
+        MediaClusterBase cluster(*deviceProxy->GetExchangeManager(), deviceProxy->GetSecureSession().Value(), mClusterId,
+                                 mTvEndpoint);
+
+        return cluster.template SubscribeAttribute<TypeInfo>(context, successFn, failureFn, minInterval, maxInterval,
+                                                             onSubscriptionEstablished);
+    }
+};
diff --git a/examples/tv-casting-app/tv-casting-common/include/TargetNavigator.h b/examples/tv-casting-app/tv-casting-common/include/TargetNavigator.h
index 731dfb7..b42d168 100644
--- a/examples/tv-casting-app/tv-casting-common/include/TargetNavigator.h
+++ b/examples/tv-casting-app/tv-casting-common/include/TargetNavigator.h
@@ -17,10 +17,12 @@
  */
 
 #include "MediaCommandBase.h"
+#include "MediaSubscriptionBase.h"
 
 #include <functional>
 #include <zap-generated/CHIPClusters.h>
 
+// COMMAND CLASSES
 class NavigateTargetCommand
     : public MediaCommandBase<chip::app::Clusters::TargetNavigator::Commands::NavigateTarget::Type,
                               chip::app::Clusters::TargetNavigator::Commands::NavigateTargetResponse::DecodableType>
@@ -31,3 +33,17 @@
     CHIP_ERROR Invoke(const uint8_t target, const chip::Optional<chip::CharSpan> data,
                       std::function<void(CHIP_ERROR)> responseCallback);
 };
+
+// SUBSCRIBER CLASSES
+class TargetListSubscriber : public MediaSubscriptionBase<chip::app::Clusters::TargetNavigator::Attributes::TargetList::TypeInfo>
+{
+public:
+    TargetListSubscriber() : MediaSubscriptionBase(chip::app::Clusters::TargetNavigator::Id) {}
+};
+
+class CurrentTargetSubscriber
+    : public MediaSubscriptionBase<chip::app::Clusters::TargetNavigator::Attributes::CurrentTarget::TypeInfo>
+{
+public:
+    CurrentTargetSubscriber() : MediaSubscriptionBase(chip::app::Clusters::TargetNavigator::Id) {}
+};
diff --git a/examples/tv-casting-app/tv-casting-common/src/CastingServer.cpp b/examples/tv-casting-app/tv-casting-common/src/CastingServer.cpp
index 67c5f2f..1acc685 100644
--- a/examples/tv-casting-app/tv-casting-common/src/CastingServer.cpp
+++ b/examples/tv-casting-app/tv-casting-common/src/CastingServer.cpp
@@ -282,6 +282,9 @@
     ChipLogError(AppServer, " -- No initialized fabrics with video players");
 }
 
+/**
+ * @brief Content Launcher cluster
+ */
 CHIP_ERROR CastingServer::ContentLauncher_LaunchURL(
     const char * contentUrl, const char * contentDisplayStr,
     chip::Optional<chip::app::Clusters::ContentLauncher::Structs::BrandingInformation::Type> brandingInformation,
@@ -299,6 +302,9 @@
     return mLaunchContentCommand.Invoke(search, autoPlay, data, responseCallback);
 }
 
+/**
+ * @brief Level Control cluster
+ */
 CHIP_ERROR CastingServer::LevelControl_Step(chip::app::Clusters::LevelControl::StepMode stepMode, uint8_t stepSize,
                                             uint16_t transitionTime, uint8_t optionMask, uint8_t optionOverride,
                                             std::function<void(CHIP_ERROR)> responseCallback)
@@ -322,6 +328,42 @@
     return mMoveToLevelCommand.Invoke(level, nullableTransitionTime, optionMask, optionOverride, responseCallback);
 }
 
+CHIP_ERROR CastingServer::LevelControl_SubscribeToCurrentLevel(
+    void * context,
+    ReadResponseSuccessCallback<chip::app::Clusters::LevelControl::Attributes::CurrentLevel::TypeInfo::DecodableArgType> successFn,
+    ReadResponseFailureCallback failureFn, uint16_t minInterval, uint16_t maxInterval,
+    SubscriptionEstablishedCallback onSubscriptionEstablished)
+{
+    ReturnErrorOnFailure(mCurrentLevelSubscriber.SetTarget(mTargetVideoPlayerInfo, kTvEndpoint));
+    return mCurrentLevelSubscriber.SubscribeAttribute(context, successFn, failureFn, minInterval, maxInterval,
+                                                      onSubscriptionEstablished);
+}
+
+CHIP_ERROR CastingServer::LevelControl_SubscribeToMinLevel(
+    void * context,
+    ReadResponseSuccessCallback<chip::app::Clusters::LevelControl::Attributes::MinLevel::TypeInfo::DecodableArgType> successFn,
+    ReadResponseFailureCallback failureFn, uint16_t minInterval, uint16_t maxInterval,
+    SubscriptionEstablishedCallback onSubscriptionEstablished)
+{
+    ReturnErrorOnFailure(mMinLevelSubscriber.SetTarget(mTargetVideoPlayerInfo, kTvEndpoint));
+    return mMinLevelSubscriber.SubscribeAttribute(context, successFn, failureFn, minInterval, maxInterval,
+                                                  onSubscriptionEstablished);
+}
+
+CHIP_ERROR CastingServer::LevelControl_SubscribeToMaxLevel(
+    void * context,
+    ReadResponseSuccessCallback<chip::app::Clusters::LevelControl::Attributes::MaxLevel::TypeInfo::DecodableArgType> successFn,
+    ReadResponseFailureCallback failureFn, uint16_t minInterval, uint16_t maxInterval,
+    SubscriptionEstablishedCallback onSubscriptionEstablished)
+{
+    ReturnErrorOnFailure(mMaxLevelSubscriber.SetTarget(mTargetVideoPlayerInfo, kTvEndpoint));
+    return mMaxLevelSubscriber.SubscribeAttribute(context, successFn, failureFn, minInterval, maxInterval,
+                                                  onSubscriptionEstablished);
+}
+
+/**
+ * @brief Media Playback cluster
+ */
 CHIP_ERROR CastingServer::MediaPlayback_Play(std::function<void(CHIP_ERROR)> responseCallback)
 {
     ReturnErrorOnFailure(mPlayCommand.SetTarget(mTargetVideoPlayerInfo, kTvEndpoint));
@@ -366,6 +408,89 @@
     return mSkipBackwardCommand.Invoke(deltaPositionMilliseconds, responseCallback);
 }
 
+CHIP_ERROR CastingServer::MediaPlayback_SubscribeToCurrentState(
+    void * context,
+    ReadResponseSuccessCallback<chip::app::Clusters::MediaPlayback::Attributes::CurrentState::TypeInfo::DecodableArgType> successFn,
+    ReadResponseFailureCallback failureFn, uint16_t minInterval, uint16_t maxInterval,
+    SubscriptionEstablishedCallback onSubscriptionEstablished)
+{
+    ReturnErrorOnFailure(mCurrentStateSubscriber.SetTarget(mTargetVideoPlayerInfo, kTvEndpoint));
+    return mCurrentStateSubscriber.SubscribeAttribute(context, successFn, failureFn, minInterval, maxInterval,
+                                                      onSubscriptionEstablished);
+}
+
+CHIP_ERROR CastingServer::MediaPlayback_SubscribeToStartTime(
+    void * context,
+    ReadResponseSuccessCallback<chip::app::Clusters::MediaPlayback::Attributes::StartTime::TypeInfo::DecodableArgType> successFn,
+    ReadResponseFailureCallback failureFn, uint16_t minInterval, uint16_t maxInterval,
+    SubscriptionEstablishedCallback onSubscriptionEstablished)
+{
+    ReturnErrorOnFailure(mStartTimeSubscriber.SetTarget(mTargetVideoPlayerInfo, kTvEndpoint));
+    return mStartTimeSubscriber.SubscribeAttribute(context, successFn, failureFn, minInterval, maxInterval,
+                                                   onSubscriptionEstablished);
+}
+
+CHIP_ERROR CastingServer::MediaPlayback_SubscribeToDuration(
+    void * context,
+    ReadResponseSuccessCallback<chip::app::Clusters::MediaPlayback::Attributes::Duration::TypeInfo::DecodableArgType> successFn,
+    ReadResponseFailureCallback failureFn, uint16_t minInterval, uint16_t maxInterval,
+    SubscriptionEstablishedCallback onSubscriptionEstablished)
+{
+    ReturnErrorOnFailure(mDurationSubscriber.SetTarget(mTargetVideoPlayerInfo, kTvEndpoint));
+    return mDurationSubscriber.SubscribeAttribute(context, successFn, failureFn, minInterval, maxInterval,
+                                                  onSubscriptionEstablished);
+}
+
+CHIP_ERROR CastingServer::MediaPlayback_SubscribeToSampledPosition(
+    void * context,
+    ReadResponseSuccessCallback<chip::app::Clusters::MediaPlayback::Attributes::SampledPosition::TypeInfo::DecodableArgType>
+        successFn,
+    ReadResponseFailureCallback failureFn, uint16_t minInterval, uint16_t maxInterval,
+    SubscriptionEstablishedCallback onSubscriptionEstablished)
+{
+    ReturnErrorOnFailure(mSampledPositionSubscriber.SetTarget(mTargetVideoPlayerInfo, kTvEndpoint));
+    return mSampledPositionSubscriber.SubscribeAttribute(context, successFn, failureFn, minInterval, maxInterval,
+                                                         onSubscriptionEstablished);
+}
+
+CHIP_ERROR CastingServer::MediaPlayback_SubscribeToPlaybackSpeed(
+    void * context,
+    ReadResponseSuccessCallback<chip::app::Clusters::MediaPlayback::Attributes::PlaybackSpeed::TypeInfo::DecodableArgType>
+        successFn,
+    ReadResponseFailureCallback failureFn, uint16_t minInterval, uint16_t maxInterval,
+    SubscriptionEstablishedCallback onSubscriptionEstablished)
+{
+    ReturnErrorOnFailure(mPlaybackSpeedSubscriber.SetTarget(mTargetVideoPlayerInfo, kTvEndpoint));
+    return mPlaybackSpeedSubscriber.SubscribeAttribute(context, successFn, failureFn, minInterval, maxInterval,
+                                                       onSubscriptionEstablished);
+}
+
+CHIP_ERROR CastingServer::MediaPlayback_SubscribeToSeekRangeEnd(
+    void * context,
+    ReadResponseSuccessCallback<chip::app::Clusters::MediaPlayback::Attributes::SeekRangeEnd::TypeInfo::DecodableArgType> successFn,
+    ReadResponseFailureCallback failureFn, uint16_t minInterval, uint16_t maxInterval,
+    SubscriptionEstablishedCallback onSubscriptionEstablished)
+{
+    ReturnErrorOnFailure(mSeekRangeEndSubscriber.SetTarget(mTargetVideoPlayerInfo, kTvEndpoint));
+    return mSeekRangeEndSubscriber.SubscribeAttribute(context, successFn, failureFn, minInterval, maxInterval,
+                                                      onSubscriptionEstablished);
+}
+
+CHIP_ERROR CastingServer::MediaPlayback_SubscribeToSeekRangeStart(
+    void * context,
+    ReadResponseSuccessCallback<chip::app::Clusters::MediaPlayback::Attributes::SeekRangeStart::TypeInfo::DecodableArgType>
+        successFn,
+    ReadResponseFailureCallback failureFn, uint16_t minInterval, uint16_t maxInterval,
+    SubscriptionEstablishedCallback onSubscriptionEstablished)
+{
+    ReturnErrorOnFailure(mSeekRangeStartSubscriber.SetTarget(mTargetVideoPlayerInfo, kTvEndpoint));
+    return mSeekRangeStartSubscriber.SubscribeAttribute(context, successFn, failureFn, minInterval, maxInterval,
+                                                        onSubscriptionEstablished);
+}
+
+/**
+ * @brief Application Launcher cluster
+ */
 CHIP_ERROR
 CastingServer::ApplicationLauncher_LaunchApp(chip::app::Clusters::ApplicationLauncher::Structs::Application::Type application,
                                              chip::Optional<chip::ByteSpan> data, std::function<void(CHIP_ERROR)> responseCallback)
@@ -390,6 +515,21 @@
     return mHideAppCommand.Invoke(application, responseCallback);
 }
 
+CHIP_ERROR CastingServer::ApplicationLauncher_SubscribeToCurrentApp(
+    void * context,
+    ReadResponseSuccessCallback<chip::app::Clusters::ApplicationLauncher::Attributes::CurrentApp::TypeInfo::DecodableArgType>
+        successFn,
+    ReadResponseFailureCallback failureFn, uint16_t minInterval, uint16_t maxInterval,
+    SubscriptionEstablishedCallback onSubscriptionEstablished)
+{
+    ReturnErrorOnFailure(mCurrentAppSubscriber.SetTarget(mTargetVideoPlayerInfo, kTvEndpoint));
+    return mCurrentAppSubscriber.SubscribeAttribute(context, successFn, failureFn, minInterval, maxInterval,
+                                                    onSubscriptionEstablished);
+}
+
+/**
+ * @brief Target Navigator cluster
+ */
 CHIP_ERROR CastingServer::TargetNavigator_NavigateTarget(const uint8_t target, const chip::Optional<chip::CharSpan> data,
                                                          std::function<void(CHIP_ERROR)> responseCallback)
 {
@@ -397,9 +537,56 @@
     return mNavigateTargetCommand.Invoke(target, data, responseCallback);
 }
 
+CHIP_ERROR CastingServer::TargetNavigator_SubscribeToTargetList(
+    void * context,
+    ReadResponseSuccessCallback<chip::app::Clusters::TargetNavigator::Attributes::TargetList::TypeInfo::DecodableArgType> successFn,
+    ReadResponseFailureCallback failureFn, uint16_t minInterval, uint16_t maxInterval,
+    SubscriptionEstablishedCallback onSubscriptionEstablished)
+{
+    ReturnErrorOnFailure(mTargetListSubscriber.SetTarget(mTargetVideoPlayerInfo, kTvEndpoint));
+    return mTargetListSubscriber.SubscribeAttribute(context, successFn, failureFn, minInterval, maxInterval,
+                                                    onSubscriptionEstablished);
+}
+
+CHIP_ERROR CastingServer::TargetNavigator_SubscribeToCurrentTarget(
+    void * context,
+    ReadResponseSuccessCallback<chip::app::Clusters::TargetNavigator::Attributes::CurrentTarget::TypeInfo::DecodableArgType>
+        successFn,
+    ReadResponseFailureCallback failureFn, uint16_t minInterval, uint16_t maxInterval,
+    SubscriptionEstablishedCallback onSubscriptionEstablished)
+{
+    ReturnErrorOnFailure(mCurrentTargetSubscriber.SetTarget(mTargetVideoPlayerInfo, kTvEndpoint));
+    return mCurrentTargetSubscriber.SubscribeAttribute(context, successFn, failureFn, minInterval, maxInterval,
+                                                       onSubscriptionEstablished);
+}
+
+/**
+ * @brief Keypad Input cluster
+ */
 CHIP_ERROR CastingServer::KeypadInput_SendKey(const chip::app::Clusters::KeypadInput::CecKeyCode keyCode,
                                               std::function<void(CHIP_ERROR)> responseCallback)
 {
     ReturnErrorOnFailure(mSendKeyCommand.SetTarget(mTargetVideoPlayerInfo, kTvEndpoint));
     return mSendKeyCommand.Invoke(keyCode, responseCallback);
 }
+
+/**
+ * @brief Channel cluster
+ */
+CHIP_ERROR CastingServer::Channel_ChangeChannelCommand(const chip::CharSpan & match,
+                                                       std::function<void(CHIP_ERROR)> responseCallback)
+{
+    ReturnErrorOnFailure(mChangeChannelCommand.SetTarget(mTargetVideoPlayerInfo, kTvEndpoint));
+    return mChangeChannelCommand.Invoke(match, responseCallback);
+}
+
+CHIP_ERROR CastingServer::Channel_SubscribeToLineup(
+    void * context,
+    chip::Controller::ReadResponseSuccessCallback<chip::app::Clusters::Channel::Attributes::Lineup::TypeInfo::DecodableArgType>
+        successFn,
+    chip::Controller::ReadResponseFailureCallback failureFn, uint16_t minInterval, uint16_t maxInterval,
+    chip::Controller::SubscriptionEstablishedCallback onSubscriptionEstablished)
+{
+    ReturnErrorOnFailure(mLineupSubscriber.SetTarget(mTargetVideoPlayerInfo, kTvEndpoint));
+    return mLineupSubscriber.SubscribeAttribute(context, successFn, failureFn, minInterval, maxInterval, onSubscriptionEstablished);
+}
diff --git a/examples/tv-casting-app/tv-casting-common/src/Channel.cpp b/examples/tv-casting-app/tv-casting-common/src/Channel.cpp
new file mode 100644
index 0000000..2f11367
--- /dev/null
+++ b/examples/tv-casting-app/tv-casting-common/src/Channel.cpp
@@ -0,0 +1,29 @@
+/*
+ *
+ *    Copyright (c) 2022 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.
+ */
+
+#include "Channel.h"
+
+using namespace chip;
+using namespace chip::app::Clusters;
+
+CHIP_ERROR ChangeChannelCommand::Invoke(const CharSpan & match, std::function<void(CHIP_ERROR)> responseCallback)
+{
+    Channel::Commands::ChangeChannel::Type request;
+    request.match = match;
+    return MediaCommandBase::Invoke(request, responseCallback);
+}