diff --git a/CMakeLists.txt b/CMakeLists.txt
index 59035e50774..e4991d558d8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -174,6 +174,7 @@ set(ARDUINO_LIBRARY_Matter_SRCS
   libraries/Matter/src/MatterEndpoints/MatterColorTemperatureLight.cpp
   libraries/Matter/src/MatterEndpoints/MatterColorLight.cpp
   libraries/Matter/src/MatterEndpoints/MatterEnhancedColorLight.cpp
+  libraries/Matter/src/MatterEndpoints/MatterFan.cpp
   libraries/Matter/src/Matter.cpp)
 
 set(ARDUINO_LIBRARY_PPP_SRCS
diff --git a/libraries/Matter/examples/MatterFan/MatterFan.ino b/libraries/Matter/examples/MatterFan/MatterFan.ino
new file mode 100644
index 00000000000..ac26550f2b6
--- /dev/null
+++ b/libraries/Matter/examples/MatterFan/MatterFan.ino
@@ -0,0 +1,202 @@
+// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
+//
+// 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.
+
+// Matter Manager
+#include <Matter.h>
+#include <WiFi.h>
+
+// List of Matter Endpoints for this Node
+// Fan Endpoint - On/Off control + Speed Percent Control + Fan Modes
+MatterFan Fan;
+
+// set your board USER BUTTON pin here - used for toggling On/Off
+const uint8_t buttonPin = 0;  // Set your pin here. Using BOOT Button. C6/C3 use GPIO9.
+
+// set your board Analog Pin here - used for changing the Fan speed
+const uint8_t analogPin = A0;  // Analog Pin depends on each board
+
+// set your board PWM Pin here - used for controlling the Fan speed (DC motor example)
+// for this example, it will use the builtin board RGB LED to simulate the Fan DC motor using its brightness
+#ifdef RGB_BUILTIN
+const uint8_t dcMotorPin = RGB_BUILTIN;
+#else
+const uint8_t dcMotorPin = 2;  // Set your pin here if your board has not defined LED_BUILTIN
+#warning "Do not forget to set the RGB LED pin"
+#endif
+
+// WiFi is manually set and started
+const char *ssid = "your-ssid";          // Change this to your WiFi SSID
+const char *password = "your-password";  // Change this to your WiFi password
+
+void fanDCMotorDrive(bool fanState, uint8_t speedPercent) {
+  // drive the Fan DC motor
+  if (fanState == false) {
+    // turn off the Fan
+    digitalWrite(dcMotorPin, LOW);
+  } else {
+    // set the Fan speed
+    uint8_t fanDCMotorPWM = map(speedPercent, 0, 100, 0, 255);
+#ifdef RGB_BUILTIN
+    rgbLedWrite(dcMotorPin, fanDCMotorPWM, fanDCMotorPWM, fanDCMotorPWM);
+#else
+    analogWrite(dcMotorPin, fanDCMotorPWM);
+#endif
+  }
+}
+
+void setup() {
+  // Initialize the USER BUTTON (Boot button) GPIO that will toggle the Fan (On/Off)
+  pinMode(buttonPin, INPUT_PULLUP);
+  // Initialize the Analog Pin A0 used to read input voltage and to set the Fan speed accordingly
+  pinMode(analogPin, INPUT);
+  analogReadResolution(10);  // 10 bits resolution reading 0..1023
+  // Initialize the PWM output pin for a Fan DC motor
+  pinMode(dcMotorPin, OUTPUT);
+
+  Serial.begin(115200);
+  while (!Serial) {
+    delay(100);
+  }
+
+  // We start by connecting to a WiFi network
+  Serial.print("Connecting to ");
+  Serial.println(ssid);
+  // enable IPv6
+  WiFi.enableIPv6(true);
+  // Manually connect to WiFi
+  WiFi.begin(ssid, password);
+  // Wait for connection
+  while (WiFi.status() != WL_CONNECTED) {
+    delay(500);
+    Serial.print(".");
+  }
+  Serial.println("\r\nWiFi connected");
+  Serial.println("IP address: ");
+  Serial.println(WiFi.localIP());
+  delay(500);
+
+  // On Boot or Reset, Fan is set at 0% speed, OFF, changing between OFF, ON, SMART and HIGH
+  Fan.begin(0, MatterFan::FAN_MODE_OFF, MatterFan::FAN_MODE_SEQ_OFF_HIGH);
+
+  // callback functions would control Fan motor
+  // the Matter Controller will send new data whenever the User APP or Automation request
+
+  // single feature callbacks take place before the generic (all features) callback
+  // This callback will be executed whenever the speed percent matter attribute is updated
+  Fan.onChangeSpeedPercent([](uint8_t speedPercent) {
+    // setting speed to Zero, while the Fan is ON, shall turn the Fan OFF
+    if (speedPercent == MatterFan::OFF_SPEED && Fan.getMode() != MatterFan::FAN_MODE_OFF) {
+      // ATTR_SET do not update the attribute, just SET it to avoid infinite loop
+      return Fan.setOnOff(false, Fan.ATTR_SET);
+    }
+    // changing the speed to higher than Zero, while the Fan is OFF, shall turn the Fan ON
+    if (speedPercent > MatterFan::OFF_SPEED && Fan.getMode() == MatterFan::FAN_MODE_OFF) {
+      // ATTR_SET do not update the attribute, just SET it to avoid infinite loop
+      return Fan.setOnOff(true, Fan.ATTR_SET);
+    }
+    // for other case, just return true
+    return true;
+  });
+
+  // This callback will be executed whenever the fan mode matter attribute is updated
+  // This will take action when user APP starts the Fan by changing the mode
+  Fan.onChangeMode([](MatterFan::FanMode_t fanMode) {
+    // when the Fan is turned ON using Mode Selection, while it is OFF, shall start it by setting the speed to 50%
+    if (Fan.getSpeedPercent() == MatterFan::OFF_SPEED && fanMode != MatterFan::FAN_MODE_OFF) {
+      Serial.printf("Fan set to %s mode -- speed percentage will go to 50%%\r\n", Fan.getFanModeString(fanMode));
+      // ATTR_SET do not update the attribute, just SET it to avoid infinite loop
+      return Fan.setSpeedPercent(50, Fan.ATTR_SET);
+    }
+    return true;
+  });
+
+  // Generic callback will be executed as soon as a single feature callback is done
+  // In this example, it will just print status messages
+  Fan.onChange([](MatterFan::FanMode_t fanMode, uint8_t speedPercent) {
+    // just report state
+    Serial.printf("Fan State: Mode %s | %d%% speed.\r\n", Fan.getFanModeString(fanMode), speedPercent);
+    // drive the Fan DC motor
+    fanDCMotorDrive(fanMode != MatterFan::FAN_MODE_OFF, speedPercent);
+    // returns success
+    return true;
+  });
+
+  // Matter beginning - Last step, after all EndPoints are initialized
+  Matter.begin();
+  // This may be a restart of a already commissioned Matter accessory
+  if (Matter.isDeviceCommissioned()) {
+    Serial.println("Matter Node is commissioned and connected to Wi-Fi. Ready for use.");
+  }
+}
+
+// Builtin Button control
+uint32_t button_time_stamp = 0;                 // debouncing control
+bool button_state = false;                      // false = released | true = pressed
+const uint32_t debouceTime = 250;               // button debouncing time (ms)
+const uint32_t decommissioningTimeout = 10000;  // keep the button pressed for 10s to decommission the Matter Fabric
+
+void loop() {
+  // Check Matter Accessory Commissioning state, which may change during execution of loop()
+  if (!Matter.isDeviceCommissioned()) {
+    Serial.println("");
+    Serial.println("Matter Node is not commissioned yet.");
+    Serial.println("Initiate the device discovery in your Matter environment.");
+    Serial.println("Commission it to your Matter hub with the manual pairing code or QR code");
+    Serial.printf("Manual pairing code: %s\r\n", Matter.getManualPairingCode().c_str());
+    Serial.printf("QR code URL: %s\r\n", Matter.getOnboardingQRCodeUrl().c_str());
+    // waits for Matter Generic Switch Commissioning.
+    uint32_t timeCount = 0;
+    while (!Matter.isDeviceCommissioned()) {
+      delay(100);
+      if ((timeCount++ % 50) == 0) {  // 50*100ms = 5 sec
+        Serial.println("Matter Node not commissioned yet. Waiting for commissioning.");
+      }
+    }
+    Serial.println("Matter Node is commissioned and connected to Wi-Fi. Ready for use.");
+  }
+
+  // A builtin button is used to trigger and send a command to the Matter Controller
+  // Check if the button has been pressed
+  if (digitalRead(buttonPin) == LOW && !button_state) {
+    // deals with button debouncing
+    button_time_stamp = millis();  // record the time while the button is pressed.
+    button_state = true;           // pressed.
+  }
+
+  // Onboard User Button is used as a smart button or to decommission it
+  uint32_t time_diff = millis() - button_time_stamp;
+  if (button_state && time_diff > debouceTime && digitalRead(buttonPin) == HIGH) {
+    button_state = false;  // released
+    // button is released - toggle Fan On/Off
+    Fan.toggle();
+    Serial.printf("User button released. Setting the Fan %s.\r\n", Fan > 0 ? "ON" : "OFF");
+
+    // Factory reset is triggered if the button is pressed longer than 10 seconds
+    if (time_diff > decommissioningTimeout) {
+      Serial.println("Decommissioning the Generic Switch Matter Accessory. It shall be commissioned again.");
+      Matter.decommission();
+    }
+  }
+
+  // checks Analog pin and adjust the speed only if it has changed
+  static int lastRead = 0;
+  // analog values (0..1023) / 103 => mapped into 10 steps (0..9)
+  int anaVal = analogRead(analogPin) / 103;
+  if (lastRead != anaVal) {
+    // speed percent moves in steps of 10. Range is 10..100
+    if (Fan.setSpeedPercent((anaVal + 1) * 10)) {
+      lastRead = anaVal;
+    }
+  }
+}
diff --git a/libraries/Matter/examples/MatterFan/ci.json b/libraries/Matter/examples/MatterFan/ci.json
new file mode 100644
index 00000000000..0665800b12b
--- /dev/null
+++ b/libraries/Matter/examples/MatterFan/ci.json
@@ -0,0 +1,7 @@
+{
+    "fqbn_append": "PartitionScheme=huge_app",
+    "requires": [
+      "CONFIG_SOC_WIFI_SUPPORTED=y",
+      "CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y"
+    ]
+}
diff --git a/libraries/Matter/keywords.txt b/libraries/Matter/keywords.txt
index 597bbac657b..4668054d12e 100644
--- a/libraries/Matter/keywords.txt
+++ b/libraries/Matter/keywords.txt
@@ -15,6 +15,9 @@ MatterColorTemperatureLight	KEYWORD1
 MatterColorLight	KEYWORD1
 MatterEnhancedColorLight	KEYWORD1
 MatterEndPoint	KEYWORD1
+MatterFan	KEYWORD1
+FanMode_t	KEYWORD1
+FanModeSequence_t	KEYWORD1
 
 #######################################
 # Methods and Functions (KEYWORD2)
@@ -32,6 +35,7 @@ decommission	KEYWORD2
 attributeChangeCB	KEYWORD2
 setOnOff	KEYWORD2
 getOnOff	KEYWORD2
+toggle	KEYWORD2
 setBrightness	KEYWORD2
 getBrightness	KEYWORD2
 setColorTemperature	KEYWORD2
@@ -40,7 +44,6 @@ setColorRGB	KEYWORD2
 getColorRGB	KEYWORD2
 setColorHSV	KEYWORD2
 getColorHSV	KEYWORD2
-toggle	KEYWORD2
 updateAccessory	KEYWORD2
 onChange	KEYWORD2
 onChangeOnOff	KEYWORD2
@@ -48,6 +51,17 @@ onChangeBrightness	KEYWORD2
 onChangeColorTemperature	KEYWORD2
 onChangeColorHSV	KEYWORD2
 click	KEYWORD2
+getAttribute	KEYWORD2
+getAttributeVal	KEYWORD2
+setAttributeVal	KEYWORD2
+updateAttributeVal	KEYWORD2
+getFanModeString	KEYWORD2
+setSpeedPercent	KEYWORD2
+getSpeedPercent	KEYWORD2
+setMode	KEYWORD2
+getMode	KEYWORD2
+onChangeMode	KEYWORD2
+onChangeSpeedPercent	KEYWORD2
 
 #######################################
 # Constants (LITERAL1)
@@ -56,3 +70,21 @@ click	KEYWORD2
 MAX_BRIGHTNESS	LITERAL1
 MAX_COLOR_TEMPERATURE	LITERAL1
 MIN_COLOR_TEMPERATURE	LITERAL1
+ATTR_SET	LITERAL1
+ATTR_UPDATE	LITERAL1
+MAX_SPEED	LITERAL1
+MIN_SPEED	LITERAL1
+OFF_SPEED	LITERAL1
+FAN_MODE_OFF	LITERAL1
+FAN_MODE_LOW	LITERAL1
+FAN_MODE_MEDIUM	LITERAL1
+FAN_MODE_HIGH	LITERAL1
+FAN_MODE_ON	LITERAL1
+FAN_MODE_AUTO	LITERAL1
+FAN_MODE_SMART	LITERAL1
+FAN_MODE_SEQ_OFF_LOW_MED_HIGH	LITERAL1
+FAN_MODE_SEQ_OFF_LOW_HIGH	LITERAL1
+FAN_MODE_SEQ_OFF_LOW_MED_HIGH_AUTO	LITERAL1
+FAN_MODE_SEQ_OFF_LOW_HIGH_AUTO	LITERAL1
+FAN_MODE_SEQ_OFF_HIGH_AUTO	LITERAL1
+FAN_MODE_SEQ_OFF_HIGH	LITERAL1
diff --git a/libraries/Matter/src/Matter.h b/libraries/Matter/src/Matter.h
index 9136eead048..06edf32288f 100644
--- a/libraries/Matter/src/Matter.h
+++ b/libraries/Matter/src/Matter.h
@@ -25,6 +25,7 @@
 #include <MatterEndpoints/MatterColorTemperatureLight.h>
 #include <MatterEndpoints/MatterColorLight.h>
 #include <MatterEndpoints/MatterEnhancedColorLight.h>
+#include <MatterEndpoints/MatterFan.h>
 
 using namespace esp_matter;
 
@@ -56,6 +57,7 @@ class ArduinoMatter {
   friend class MatterColorTemperatureLight;
   friend class MatterColorLight;
   friend class MatterEnhancedColorLight;
+  friend class MatterFan;
 
 protected:
   static void _init();
diff --git a/libraries/Matter/src/MatterEndPoint.h b/libraries/Matter/src/MatterEndPoint.h
index 02577957e8e..99bff8470d3 100644
--- a/libraries/Matter/src/MatterEndPoint.h
+++ b/libraries/Matter/src/MatterEndPoint.h
@@ -19,15 +19,86 @@
 #include <Matter.h>
 #include <functional>
 
+using namespace esp_matter;
+
 // Matter Endpoint Base Class. Controls the endpoint ID and allows the child class to overwrite attribute change call
 class MatterEndPoint {
 public:
+  enum attrOperation_t {
+    ATTR_SET = false,
+    ATTR_UPDATE = true
+  };
+
   uint16_t getEndPointId() {
     return endpoint_id;
   }
+
   void setEndPointId(uint16_t ep) {
     endpoint_id = ep;
   }
+
+  // helper functions for attribute manipulation
+  attribute_t *getAttribute(uint32_t cluster_id, uint32_t attribute_id) {
+    if (endpoint_id == 0) {
+      log_e("Endpoint ID is not set");
+      return NULL;
+    }
+    endpoint_t *endpoint = endpoint::get(node::get(), endpoint_id);
+    if (endpoint == NULL) {
+      log_e("Endpoint [%d] not found", endpoint_id);
+      return NULL;
+    }
+    cluster_t *cluster = cluster::get(endpoint, cluster_id);
+    if (cluster == NULL) {
+      log_e("Cluster [%d] not found", cluster_id);
+      return NULL;
+    }
+    attribute_t *attribute = attribute::get(cluster, attribute_id);
+    if (attribute == NULL) {
+      log_e("Attribute [%d] not found", attribute_id);
+      return NULL;
+    }
+    return attribute;
+  }
+
+  // get the value of an attribute from its cluster id and attribute it
+  bool getAttributeVal(uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *attrVal) {
+    attribute_t *attribute = getAttribute(cluster_id, attribute_id);
+    if (attribute == NULL) {
+      return false;
+    }
+    if (attribute::get_val(attribute, attrVal) == ESP_OK) {
+      log_v("GET_VAL Success for cluster %d, attribute %d with value %d", cluster_id, attribute_id, attrVal->val.u32);
+      return true;
+    }
+    log_e("GET_VAL FAILED! for cluster %d, attribute %d with value %d", cluster_id, attribute_id, attrVal->val.u32);
+    return false;
+  }
+
+  // set the value of an attribute from its cluster id and attribute it
+  bool setAttributeVal(uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *attrVal) {
+    attribute_t *attribute = getAttribute(cluster_id, attribute_id);
+    if (attribute == NULL) {
+      return false;
+    }
+    if (attribute::set_val(attribute, attrVal) == ESP_OK) {
+      log_v("SET_VAL Success for cluster %d, attribute %d with value %d", cluster_id, attribute_id, attrVal->val.u32);
+      return true;
+    }
+    log_e("SET_VAL FAILED! for cluster %d, attribute %d with value %d", cluster_id, attribute_id, attrVal->val.u32);
+    return false;
+  }
+
+  // update the value of an attribute from its cluster id and attribute it
+  bool updateAttributeVal(uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *attrVal) {
+    if (attribute::update(endpoint_id, cluster_id, attribute_id, attrVal) == ESP_OK) {
+      log_v("Update Success for cluster %d, attribute %d with value %d", cluster_id, attribute_id, attrVal->val.u32);
+      return true;
+    }
+    log_e("Update FAILED! for cluster %d, attribute %d with value %d", cluster_id, attribute_id, attrVal->val.u32);
+    return false;
+  }
+
   // this function is called by Matter internal event processor. It could be overwritten by the application, if necessary.
   virtual bool attributeChangeCB(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *val) = 0;
 
diff --git a/libraries/Matter/src/MatterEndpoints/MatterFan.cpp b/libraries/Matter/src/MatterEndpoints/MatterFan.cpp
new file mode 100644
index 00000000000..8db6a317ead
--- /dev/null
+++ b/libraries/Matter/src/MatterEndpoints/MatterFan.cpp
@@ -0,0 +1,230 @@
+// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
+//
+// 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 <sdkconfig.h>
+#ifdef CONFIG_ESP_MATTER_ENABLE_DATA_MODEL
+
+#include <Matter.h>
+#include <MatterEndpoints/MatterFan.h>
+
+using namespace esp_matter;
+using namespace esp_matter::endpoint;
+using namespace esp_matter::cluster;
+using namespace chip::app::Clusters;
+
+// string helper for the FAN MODE
+const char *MatterFan::fanModeString[7] = {"OFF", "LOW", "MEDIUM", "HIGH", "ON", "AUTO", "SMART"};
+// bitmap for valid Fan Modes based on order defined in Zap Generated Cluster Enums
+const uint8_t MatterFan::fanModeSequence[6] = {fanSeqModeOffLowMedHigh,  fanSeqModeOffLowHigh,  fanSeqModeOffLowMedHighAuto,
+                                               fanSeqModeOffLowHighAuto, fanSeqModeOffHighAuto, fanSeqModeOffHigh};
+
+// Constructor and Method Definitions
+MatterFan::MatterFan() {}
+
+MatterFan::~MatterFan() {
+  end();
+}
+
+bool MatterFan::attributeChangeCB(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *val) {
+  bool ret = true;
+
+  if (!started) {
+    log_e("Matter Fan device has not begun.");
+    return false;
+  }
+
+  log_d("Fan Attr update callback: endpoint: %u, cluster: %u, attribute: %u, val: %u", endpoint_id, cluster_id, attribute_id, val->val.u32);
+
+  if (endpoint_id == getEndPointId() && cluster_id == FanControl::Id) {
+    switch (attribute_id) {
+      case FanControl::Attributes::FanMode::Id:
+        log_v("FanControl Fan Mode changed to %s (%x)", val->val.u8 < 7 ? fanModeString[val->val.u8] : "Unknown", val->val.u8);
+        if (_onChangeModeCB != NULL) {
+          ret &= _onChangeModeCB((FanMode_t)val->val.u8);
+        }
+        if (_onChangeCB != NULL) {
+          ret &= _onChangeCB((FanMode_t)val->val.u8, currentPercent);
+        }
+        if (ret == true) {
+          currentFanMode = (FanMode_t)val->val.u8;
+        }
+        break;
+      case FanControl::Attributes::PercentSetting::Id:
+      case FanControl::Attributes::PercentCurrent::Id:
+        log_v("FanControl Percent %s changed to %d", attribute_id == FanControl::Attributes::PercentSetting::Id ? "SETTING" : "CURRENT", val->val.u8);
+        if (_onChangeSpeedCB != NULL) {
+          ret &= _onChangeSpeedCB(val->val.u8);
+        }
+        if (_onChangeCB != NULL) {
+          ret &= _onChangeCB(currentFanMode, val->val.u8);
+        }
+        if (ret == true) {
+          // change setting speed percent
+          currentPercent = val->val.u8;
+          setAttributeVal(FanControl::Id, FanControl::Attributes::PercentSetting::Id, val);
+          setAttributeVal(FanControl::Id, FanControl::Attributes::PercentCurrent::Id, val);
+        }
+        break;
+    }
+  }
+
+  return ret;
+}
+
+bool MatterFan::begin(uint8_t percent, FanMode_t fanMode, FanModeSequence_t fanModeSeq) {
+  ArduinoMatter::_init();
+
+  // endpoint handles can be used to add/modify clusters.
+  fan::config_t fan_config;
+  fan_config.fan_control.fan_mode = fanMode;
+  fan_config.fan_control.percent_current = percent;
+  fan_config.fan_control.percent_setting = percent;
+  fan_config.fan_control.fan_mode_sequence = fanModeSeq;
+  validFanModes = fanModeSequence[fanModeSeq];
+
+  endpoint_t *endpoint = fan::create(node::get(), &fan_config, ENDPOINT_FLAG_NONE, (void *)this);
+
+  if (endpoint == nullptr) {
+    log_e("Failed to create Fan endpoint");
+    return false;
+  }
+
+  currentFanMode = fanMode;
+  currentPercent = percent;
+
+  setEndPointId(endpoint::get_id(endpoint));
+  log_i("Fan created with endpoint_id %d", getEndPointId());
+  started = true;
+  return true;
+}
+
+void MatterFan::end() {
+  started = false;
+}
+
+bool MatterFan::setMode(FanMode_t newMode, bool performUpdate) {
+  if (!started) {
+    log_w("Matter Fan device has not begun.");
+    return false;
+  }
+  // avoid processing the a "no-change"
+  if (currentFanMode == newMode) {
+    return true;
+  }
+
+  // check if the mode is valid based on the sequence used in its creation
+  if (!(validFanModes & (1 << newMode))) {
+    log_e("Invalid Fan Mode %s for the current Fan Mode Sequence.", fanModeString[newMode]);
+    return false;
+  }
+
+  esp_matter_attr_val_t modeVal = esp_matter_invalid(NULL);
+  if (!getAttributeVal(FanControl::Id, FanControl::Attributes::FanMode::Id, &modeVal)) {
+    log_e("Failed to get Fan Mode Attribute.");
+    return false;
+  }
+  if (modeVal.val.u8 != (uint8_t)newMode) {
+    modeVal.val.u8 = (uint8_t)newMode;
+    bool ret;
+    if (performUpdate) {
+      ret = updateAttributeVal(FanControl::Id, FanControl::Attributes::FanMode::Id, &modeVal);
+    } else {
+      ret = setAttributeVal(FanControl::Id, FanControl::Attributes::FanMode::Id, &modeVal);
+    }
+    if (!ret) {
+      log_e("Failed to %s Fan Mode Attribute.", performUpdate ? "update" : "set");
+      return false;
+    }
+  }
+  currentFanMode = newMode;
+  log_v("Fan Mode %s to %s ==> onOffState[%s]", performUpdate ? "updated" : "set", fanModeString[currentFanMode], getOnOff() ? "ON" : "OFF");
+  return true;
+}
+
+// this function will change the Fan Speed by calling the user application callback
+// it is up to the application to decide to turn on, off or change the speed of the fan
+bool MatterFan::setSpeedPercent(uint8_t newPercent, bool performUpdate) {
+  if (!started) {
+    log_w("Matter Fan device has not begun.");
+    return false;
+  }
+  // avoid processing the a "no-change"
+  if (currentPercent == newPercent) {
+    return true;
+  }
+
+  esp_matter_attr_val_t speedVal = esp_matter_invalid(NULL);
+  if (!getAttributeVal(FanControl::Id, FanControl::Attributes::PercentSetting::Id, &speedVal)) {
+    log_e("Failed to get Fan Speed Percent Attribute.");
+    return false;
+  }
+  if (speedVal.val.u8 != newPercent) {
+    speedVal.val.u8 = newPercent;
+    bool ret;
+    if (performUpdate) {
+      ret = updateAttributeVal(FanControl::Id, FanControl::Attributes::PercentSetting::Id, &speedVal);
+    } else {
+      ret = setAttributeVal(FanControl::Id, FanControl::Attributes::PercentSetting::Id, &speedVal);
+      ret = setAttributeVal(FanControl::Id, FanControl::Attributes::PercentCurrent::Id, &speedVal);
+    }
+    if (!ret) {
+      log_e("Failed to %s Fan Speed Percent Attribute.", performUpdate ? "update" : "set");
+      return false;
+    }
+  }
+  currentPercent = newPercent;
+  log_v("Fan Speed %s to %d ==> onOffState[%s]", performUpdate ? "updated" : "set", currentPercent, getOnOff() ? "ON" : "OFF");
+  return true;
+}
+
+bool MatterFan::setOnOff(bool newState, bool performUpdate) {
+  if (!started) {
+    log_w("Matter Fan device has not begun.");
+    return false;
+  }
+  // avoid processing the a "no-change"
+  if (getOnOff() == newState) {
+    return true;
+  }
+
+  esp_matter_attr_val_t modeVal = esp_matter_invalid(NULL);
+  if (!getAttributeVal(FanControl::Id, FanControl::Attributes::FanMode::Id, &modeVal)) {
+    log_e("Failed to get Fan Mode Attribute.");
+    return false;
+  }
+  if (modeVal.val.u8 != (uint8_t)newState) {
+    FanMode_t newMode = newState ? FAN_MODE_ON : FAN_MODE_OFF;
+    if (!setMode(newMode, performUpdate)) {
+      return false;
+    }
+  }
+  log_v(
+    "Fan State %s to %s :: Mode[%s]|Speed[%d]", performUpdate ? "updated" : "set", getOnOff() ? "ON" : "OFF", fanModeString[currentFanMode], currentPercent
+  );
+  return true;
+}
+
+bool MatterFan::getOnOff() {
+  return currentFanMode == FAN_MODE_OFF ? false : true;
+}
+
+bool MatterFan::toggle(bool performUpdate) {
+  if (getOnOff() == true) {
+    return setOnOff(false, performUpdate);
+  } else {
+    return setOnOff(true, performUpdate);
+  }
+}
+
+#endif /* CONFIG_ESP_MATTER_ENABLE_DATA_MODEL */
diff --git a/libraries/Matter/src/MatterEndpoints/MatterFan.h b/libraries/Matter/src/MatterEndpoints/MatterFan.h
new file mode 100644
index 00000000000..232577b7bef
--- /dev/null
+++ b/libraries/Matter/src/MatterEndpoints/MatterFan.h
@@ -0,0 +1,163 @@
+// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
+//
+// 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 <sdkconfig.h>
+#ifdef CONFIG_ESP_MATTER_ENABLE_DATA_MODEL
+
+#include <Matter.h>
+#include <MatterEndPoint.h>
+#include <app-common/zap-generated/cluster-objects.h>
+
+using namespace chip::app::Clusters::FanControl;
+
+// Matter Fan endpoint with On/Off, Mode and Speed control
+
+class MatterFan : public MatterEndPoint {
+public:
+  // Fan feature constants
+  static const uint8_t MAX_SPEED = 100;  // maximum High speed
+  static const uint8_t MIN_SPEED = 1;    // minimum Low speed
+  static const uint8_t OFF_SPEED = 0;    // speed set by Matter when FAN_MODE_OFF
+
+  // Default Fan Modes: ON, SMART, HIGH and OFF
+
+  // Other mode will depend on what is the configured Fan Mode Sequence
+  enum FanMode_t {
+    FAN_MODE_OFF = (uint8_t)FanModeEnum::kOff,
+    FAN_MODE_LOW = (uint8_t)FanModeEnum::kLow,
+    FAN_MODE_MEDIUM = (uint8_t)FanModeEnum::kMedium,
+    FAN_MODE_HIGH = (uint8_t)FanModeEnum::kHigh,
+    FAN_MODE_ON = (uint8_t)FanModeEnum::kOn,
+    FAN_MODE_AUTO = (uint8_t)FanModeEnum::kAuto,
+    FAN_MODE_SMART = (uint8_t)FanModeEnum::kSmart
+  };
+
+  // Menu will always have ON, OFF, HIGH and SMART.
+  // AUTO will show up only when a AUTO SEQ is CONFIGURED
+  // LOW and MEDIUM depend on the SEQ MODE configuration
+  enum FanModeSequence_t {
+    FAN_MODE_SEQ_OFF_LOW_MED_HIGH = (uint8_t)FanModeSequenceEnum::kOffLowMedHigh,
+    FAN_MODE_SEQ_OFF_LOW_HIGH = (uint8_t)FanModeSequenceEnum::kOffLowHigh,
+    FAN_MODE_SEQ_OFF_LOW_MED_HIGH_AUTO = (uint8_t)FanModeSequenceEnum::kOffLowMedHighAuto,
+    FAN_MODE_SEQ_OFF_LOW_HIGH_AUTO = (uint8_t)FanModeSequenceEnum::kOffLowHighAuto,
+    FAN_MODE_SEQ_OFF_HIGH_AUTO = (uint8_t)FanModeSequenceEnum::kOffHighAuto,
+    FAN_MODE_SEQ_OFF_HIGH = (uint8_t)FanModeSequenceEnum::kOffHigh
+  };
+
+  MatterFan();
+  ~MatterFan();
+  virtual bool begin(uint8_t percent = 0, FanMode_t fanMode = FAN_MODE_OFF, FanModeSequence_t fanModeSeq = FAN_MODE_SEQ_OFF_HIGH);
+  void end();  // this will just stop processing Matter events
+
+  // returns a friendly string for the Fan Mode
+  static const char *getFanModeString(uint8_t mode) {
+    return fanModeString[mode];
+  }
+
+  // Fan Control of current On/Off state
+
+  bool setOnOff(bool newState, bool performUpdate = true);  // sets Fan On/Off state
+  bool getOnOff();                                          // returns current Fan state
+  bool toggle(bool performUpdate = true);                   // toggle Fun On/Off state
+
+  // Fan Control of current speed percent
+
+  bool setSpeedPercent(uint8_t newPercent, bool performUpdate = true);  // returns true if successful
+  uint8_t getSpeedPercent() {                                           // returns current Fan Speed Percent
+    return currentPercent;
+  }
+
+  // Fan Control of current Fan Mode
+
+  bool setMode(FanMode_t newMode, bool performUpdate = true);  // returns true if successful
+  FanMode_t getMode() {                                        // returns current Fan Mode
+    return currentFanMode;
+  }
+  // used to update the state of the Fan using the current Matter Fan internal state
+  // It is necessary to set a user callback function using onChange() to handle the physical Fan motor state
+
+  void updateAccessory() {
+    if (_onChangeCB != NULL) {
+      _onChangeCB(currentFanMode, currentPercent);
+    }
+  }
+
+  // returns current Fan speed percent
+  operator uint8_t() {
+    return getSpeedPercent();
+  }
+  // sets Fan speed percent
+  void operator=(uint8_t speedPercent) {
+    setSpeedPercent(speedPercent);
+  }
+
+  // this function is called by Matter internal event processor. It could be overwritten by the application, if necessary.
+  bool attributeChangeCB(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *val);
+
+  // User Callback for whenever the Fan Mode (state) is changed by the Matter Controller
+  using EndPointModeCB = std::function<bool(FanMode_t)>;
+  void onChangeMode(EndPointModeCB onChangeCB) {
+    _onChangeModeCB = onChangeCB;
+  }
+
+  // User Callback for whenever the Fan Speed Percentage value [0..100] is changed by the Matter Controller
+  using EndPointSpeedCB = std::function<bool(uint8_t)>;
+  void onChangeSpeedPercent(EndPointSpeedCB onChangeCB) {
+    _onChangeSpeedCB = onChangeCB;
+  }
+
+  // User Callback for whenever any parameter is changed by the Matter Controller
+  using EndPointCB = std::function<bool(FanMode_t, uint8_t)>;
+  void onChange(EndPointCB onChangeCB) {
+    _onChangeCB = onChangeCB;
+  }
+
+protected:
+  bool started = false;
+  uint8_t validFanModes = 0;  // bitmap for valid Fan Modes - index of fanModeSequence[]
+
+  uint8_t currentPercent = 0;               // current speed percent
+  FanMode_t currentFanMode = FAN_MODE_OFF;  // current Fan Mode
+  EndPointModeCB _onChangeModeCB = NULL;
+  EndPointSpeedCB _onChangeSpeedCB = NULL;
+  EndPointCB _onChangeCB = NULL;
+
+  // bitmap for Fan Sequence Modes (OFF, LOW, MEDIUM, HIGH, AUTO)
+  static const uint8_t fanSeqModeOff = 0x01;
+  static const uint8_t fanSeqModeLow = 0x02;
+  static const uint8_t fanSeqModeMedium = 0x04;
+  static const uint8_t fanSeqModeHigh = 0x08;
+  static const uint8_t fanSeqModeOn = 0x10;
+  static const uint8_t fanSeqModeAuto = 0x20;
+  static const uint8_t fanSeqModeSmart = 0x40;
+
+  // bitmap for common modes: ON, OFF, HIGH and SMART
+  static const uint8_t fanSeqCommonModes = fanSeqModeOff | fanSeqModeOn | fanSeqModeHigh | fanSeqModeSmart;
+
+  static const uint8_t fanSeqModeOffLowMedHigh = fanSeqCommonModes | fanSeqModeLow | fanSeqModeMedium;
+  static const uint8_t fanSeqModeOffLowHigh = fanSeqCommonModes | fanSeqModeLow;
+  static const uint8_t fanSeqModeOffLowMedHighAuto = fanSeqCommonModes | fanSeqModeLow | fanSeqModeMedium | fanSeqModeAuto;
+  static const uint8_t fanSeqModeOffLowHighAuto = fanSeqCommonModes | fanSeqModeLow | fanSeqModeAuto;
+  static const uint8_t fanSeqModeOffHighAuto = fanSeqCommonModes | fanSeqModeAuto;
+  static const uint8_t fanSeqModeOffHigh = fanSeqCommonModes;
+
+  // bitmap for valid Fan Modes based on order defined in Zap Generated Cluster Enums
+  static const uint8_t fanModeSequence[6];
+
+  // string helper for the FAN MODE
+  static const char *fanModeString[7];
+};
+
+#endif /* CONFIG_ESP_MATTER_ENABLE_DATA_MODEL */