diff --git a/api/proto/api/device_profile.proto b/api/proto/api/device_profile.proto
index bc1e7b74..fd555240 100644
--- a/api/proto/api/device_profile.proto
+++ b/api/proto/api/device_profile.proto
@@ -104,6 +104,9 @@ enum Ts003Version {
 
   // v1.0.0.
   TS003_V100 = 1;
+
+  // v2.0.0
+  TS003_v200 = 2;
 }
 
 enum Ts004Version {
@@ -112,6 +115,9 @@ enum Ts004Version {
 
   // v1.0.0.
   TS004_V100 = 1;
+
+  // v2.0.0
+  TS004_V200 = 2;
 }
 
 enum Ts005Version {
@@ -120,6 +126,9 @@ enum Ts005Version {
 
   // v1.0.0.
   TS005_V100 = 1;
+
+  // v2.0.0
+  TS005_V200 = 2;
 }
 
 // DeviceProfileService is the service providing API methods for managing
diff --git a/api/rust/proto/chirpstack/api/device_profile.proto b/api/rust/proto/chirpstack/api/device_profile.proto
index bc1e7b74..fd555240 100644
--- a/api/rust/proto/chirpstack/api/device_profile.proto
+++ b/api/rust/proto/chirpstack/api/device_profile.proto
@@ -104,6 +104,9 @@ enum Ts003Version {
 
   // v1.0.0.
   TS003_V100 = 1;
+
+  // v2.0.0
+  TS003_v200 = 2;
 }
 
 enum Ts004Version {
@@ -112,6 +115,9 @@ enum Ts004Version {
 
   // v1.0.0.
   TS004_V100 = 1;
+
+  // v2.0.0
+  TS004_V200 = 2;
 }
 
 enum Ts005Version {
@@ -120,6 +126,9 @@ enum Ts005Version {
 
   // v1.0.0.
   TS005_V100 = 1;
+
+  // v2.0.0
+  TS005_V200 = 2;
 }
 
 // DeviceProfileService is the service providing API methods for managing
diff --git a/chirpstack/src/api/helpers.rs b/chirpstack/src/api/helpers.rs
index 90d35274..96243c01 100644
--- a/chirpstack/src/api/helpers.rs
+++ b/chirpstack/src/api/helpers.rs
@@ -292,6 +292,7 @@ impl ToProto<api::Ts003Version> for Option<fields::device_profile::Ts003Version>
         match self {
             None => api::Ts003Version::Ts003NotImplemented,
             Some(fields::device_profile::Ts003Version::V100) => api::Ts003Version::Ts003V100,
+            Some(fields::device_profile::Ts003Version::V200) => api::Ts003Version::Ts003V200,
         }
     }
 }
@@ -301,6 +302,7 @@ impl FromProto<Option<fields::device_profile::Ts003Version>> for api::Ts003Versi
         match self {
             api::Ts003Version::Ts003NotImplemented => None,
             api::Ts003Version::Ts003V100 => Some(fields::device_profile::Ts003Version::V100),
+            api::Ts003Version::Ts003V200 => Some(fields::device_profile::Ts003Version::V200),
         }
     }
 }
@@ -310,6 +312,7 @@ impl ToProto<api::Ts004Version> for Option<fields::device_profile::Ts004Version>
         match self {
             None => api::Ts004Version::Ts004NotImplemented,
             Some(fields::device_profile::Ts004Version::V100) => api::Ts004Version::Ts004V100,
+            Some(fields::device_profile::Ts004Version::V200) => api::Ts004Version::Ts004V200,
         }
     }
 }
@@ -319,6 +322,7 @@ impl FromProto<Option<fields::device_profile::Ts004Version>> for api::Ts004Versi
         match self {
             api::Ts004Version::Ts004NotImplemented => None,
             api::Ts004Version::Ts004V100 => Some(fields::device_profile::Ts004Version::V100),
+            api::Ts004Version::Ts004V200 => Some(fields::device_profile::Ts004Version::V200),
         }
     }
 }
@@ -328,6 +332,7 @@ impl ToProto<api::Ts005Version> for Option<fields::device_profile::Ts005Version>
         match self {
             None => api::Ts005Version::Ts005NotImplemented,
             Some(fields::device_profile::Ts005Version::V100) => api::Ts005Version::Ts005V100,
+            Some(fields::device_profile::Ts005Version::V200) => api::Ts005Version::Ts005V200,
         }
     }
 }
@@ -337,6 +342,7 @@ impl FromProto<Option<fields::device_profile::Ts005Version>> for api::Ts005Versi
         match self {
             api::Ts005Version::Ts005NotImplemented => None,
             api::Ts005Version::Ts005V100 => Some(fields::device_profile::Ts005Version::V100),
+            api::Ts005Version::Ts005V200 => Some(fields::device_profile::Ts005Version::V200),
         }
     }
 }
diff --git a/chirpstack/src/applayer/clocksync.rs b/chirpstack/src/applayer/clocksync.rs
index cc400a80..94717293 100644
--- a/chirpstack/src/applayer/clocksync.rs
+++ b/chirpstack/src/applayer/clocksync.rs
@@ -21,6 +21,7 @@ pub async fn handle_uplink(
 
     match version {
         Ts003Version::V100 => handle_uplink_v100(dev, dp, rx_info, data).await,
+        Ts003Version::V200 => handle_uplink_v200(dev, dp, rx_info, data).await,
     }
 }
 
@@ -42,6 +43,24 @@ async fn handle_uplink_v100(
     Ok(())
 }
 
+async fn handle_uplink_v200(
+    dev: &device::Device,
+    dp: &device_profile::DeviceProfile,
+    rx_info: &[gw::UplinkRxInfo],
+    data: &[u8],
+) -> Result<()> {
+    let pl = clocksync::v2::Payload::from_slice(true, data)?;
+
+    match pl {
+        clocksync::v2::Payload::AppTimeReq(pl) => {
+            handle_v2_app_time_req(dev, dp, rx_info, pl).await?
+        }
+        _ => {}
+    }
+
+    Ok(())
+}
+
 async fn handle_v1_app_time_req(
     dev: &device::Device,
     dp: &device_profile::DeviceProfile,
@@ -91,6 +110,55 @@ async fn handle_v1_app_time_req(
     Ok(())
 }
 
+async fn handle_v2_app_time_req(
+    dev: &device::Device,
+    dp: &device_profile::DeviceProfile,
+    rx_info: &[gw::UplinkRxInfo],
+    pl: clocksync::v2::AppTimeReqPayload,
+) -> Result<()> {
+    info!("Handling AppTimeReq");
+
+    let now_time_since_gps = if let Some(t) = helpers::get_time_since_gps_epoch(rx_info) {
+        chrono::Duration::from_std(t)?
+    } else {
+        helpers::get_rx_timestamp_chrono(rx_info).to_gps_time()
+    };
+    let dev_time_since_gps = chrono::Duration::seconds(pl.device_time.into());
+
+    let time_diff = (now_time_since_gps - dev_time_since_gps).num_seconds();
+    let time_correction: i32 = if time_diff < 0 {
+        time_diff.try_into().unwrap_or(i32::MIN)
+    } else {
+        time_diff.try_into().unwrap_or(i32::MAX)
+    };
+
+    if time_diff == 0 && !pl.param.ans_required {
+        return Ok(());
+    }
+
+    info!(
+        time_correcrtion = time_correction,
+        "Responding with AppTimeAns"
+    );
+
+    let ans = clocksync::v2::Payload::AppTimeAns(clocksync::v2::AppTimeAnsPayload {
+        time_correction,
+        param: clocksync::v2::AppTimeAnsPayloadParam {
+            token_ans: pl.param.token_req,
+        },
+    });
+
+    device_queue::enqueue_item(device_queue::DeviceQueueItem {
+        dev_eui: dev.dev_eui,
+        f_port: dp.app_layer_params.ts003_f_port.into(),
+        data: ans.to_vec()?,
+        ..Default::default()
+    })
+    .await?;
+
+    Ok(())
+}
+
 #[cfg(test)]
 mod test {
     use super::*;
@@ -248,4 +316,153 @@ mod test {
             }
         }
     }
+
+    #[tokio::test]
+    async fn test_handle_v2_app_time_req() {
+        struct Test {
+            name: String,
+            rx_info: gw::UplinkRxInfo,
+            req: clocksync::v2::AppTimeReqPayload,
+            expected: Option<clocksync::v2::AppTimeAnsPayload>,
+        }
+
+        let tests = vec![
+            Test {
+                name: "device synced".into(),
+                rx_info: gw::UplinkRxInfo {
+                    time_since_gps_epoch: Some(Duration::from_secs(1234).try_into().unwrap()),
+                    ..Default::default()
+                },
+                req: clocksync::v2::AppTimeReqPayload {
+                    device_time: 1234,
+                    param: clocksync::v2::AppTimeReqPayloadParam {
+                        token_req: 8,
+                        ans_required: false,
+                    },
+                },
+                expected: None,
+            },
+            Test {
+                name: "device synced - ans required".into(),
+                rx_info: gw::UplinkRxInfo {
+                    time_since_gps_epoch: Some(Duration::from_secs(1234).try_into().unwrap()),
+                    ..Default::default()
+                },
+                req: clocksync::v2::AppTimeReqPayload {
+                    device_time: 1234,
+                    param: clocksync::v2::AppTimeReqPayloadParam {
+                        token_req: 8,
+                        ans_required: true,
+                    },
+                },
+                expected: Some(clocksync::v2::AppTimeAnsPayload {
+                    time_correction: 0,
+                    param: clocksync::v2::AppTimeAnsPayloadParam { token_ans: 8 },
+                }),
+            },
+            Test {
+                name: "device not synced (positive correction)".into(),
+                rx_info: gw::UplinkRxInfo {
+                    time_since_gps_epoch: Some(Duration::from_secs(1234).try_into().unwrap()),
+                    ..Default::default()
+                },
+                req: clocksync::v2::AppTimeReqPayload {
+                    device_time: 1200,
+                    param: clocksync::v2::AppTimeReqPayloadParam {
+                        token_req: 8,
+                        ans_required: false,
+                    },
+                },
+                expected: Some(clocksync::v2::AppTimeAnsPayload {
+                    time_correction: 34,
+                    param: clocksync::v2::AppTimeAnsPayloadParam { token_ans: 8 },
+                }),
+            },
+            Test {
+                name: "device not synced (negative correction)".into(),
+                rx_info: gw::UplinkRxInfo {
+                    time_since_gps_epoch: Some(Duration::from_secs(1200).try_into().unwrap()),
+                    ..Default::default()
+                },
+                req: clocksync::v2::AppTimeReqPayload {
+                    device_time: 1234,
+                    param: clocksync::v2::AppTimeReqPayloadParam {
+                        token_req: 8,
+                        ans_required: false,
+                    },
+                },
+                expected: Some(clocksync::v2::AppTimeAnsPayload {
+                    time_correction: -34,
+                    param: clocksync::v2::AppTimeAnsPayloadParam { token_ans: 8 },
+                }),
+            },
+        ];
+
+        let _guard = test::prepare().await;
+        let t = tenant::create(tenant::Tenant {
+            name: "test-tenant".into(),
+            ..Default::default()
+        })
+        .await
+        .unwrap();
+
+        let app = application::create(application::Application {
+            name: "test-app".into(),
+            tenant_id: t.id,
+            ..Default::default()
+        })
+        .await
+        .unwrap();
+
+        let dp = device_profile::create(device_profile::DeviceProfile {
+            name: "test-dp".into(),
+            tenant_id: t.id,
+            app_layer_params: fields::AppLayerParams {
+                ts003_version: Some(Ts003Version::V200),
+                ..Default::default()
+            },
+            ..Default::default()
+        })
+        .await
+        .unwrap();
+
+        let d = device::create(device::Device {
+            name: "test-dev".into(),
+            dev_eui: EUI64::from_be_bytes([1, 2, 3, 4, 5, 6, 7, 8]),
+            application_id: app.id,
+            device_profile_id: dp.id,
+            ..Default::default()
+        })
+        .await
+        .unwrap();
+
+        for tst in &tests {
+            println!("> {}", tst.name);
+            device_queue::flush_for_dev_eui(&d.dev_eui).await.unwrap();
+            let pl = clocksync::v2::Payload::AppTimeReq(tst.req.clone());
+
+            handle_uplink(
+                &d,
+                &dp,
+                &[tst.rx_info.clone()],
+                dp.app_layer_params.ts003_f_port,
+                &pl.to_vec().unwrap(),
+            )
+            .await;
+
+            let queue_items = device_queue::get_for_dev_eui(&d.dev_eui).await.unwrap();
+            if let Some(expected_pl) = &tst.expected {
+                assert_eq!(1, queue_items.len());
+                let qi = queue_items.first().unwrap();
+                assert_eq!(dp.app_layer_params.ts003_f_port as i16, qi.f_port);
+
+                let qi_pl = clocksync::v2::Payload::from_slice(false, &qi.data).unwrap();
+                let expected_pl = clocksync::v2::Payload::AppTimeAns(expected_pl.clone());
+
+                assert_eq!(expected_pl, qi_pl);
+            } else {
+                assert!(queue_items.is_empty());
+            }
+        }
+    }
 }
diff --git a/chirpstack/src/applayer/fragmentation.rs b/chirpstack/src/applayer/fragmentation.rs
index 92af2263..2ced414b 100644
--- a/chirpstack/src/applayer/fragmentation.rs
+++ b/chirpstack/src/applayer/fragmentation.rs
@@ -18,6 +18,7 @@ pub async fn handle_uplink(
 
     match version {
         Ts004Version::V100 => handle_uplink_v100(dev, data).await,
+        Ts004Version::V200 => handle_uplink_v200(dev, data).await,
     }
 }
 
@@ -37,6 +38,22 @@ async fn handle_uplink_v100(dev: &device::Device, data: &[u8]) -> Result<()> {
     Ok(())
 }
 
+async fn handle_uplink_v200(dev: &device::Device, data: &[u8]) -> Result<()> {
+    let pl = fragmentation::v2::Payload::from_slice(true, data)?;
+
+    match pl {
+        fragmentation::v2::Payload::FragSessionSetupAns(pl) => {
+            handle_v2_frag_session_setup_ans(dev, pl).await?
+        }
+        fragmentation::v2::Payload::FragSessionStatusAns(pl) => {
+            handle_v2_frag_session_status_ans(dev, pl).await?
+        }
+        _ => {}
+    }
+
+    Ok(())
+}
+
 async fn handle_v1_frag_session_setup_ans(
     dev: &device::Device,
     pl: fragmentation::v1::FragSessionSetupAnsPayload,
@@ -68,6 +85,39 @@ async fn handle_v1_frag_session_setup_ans(
     Ok(())
 }
 
+async fn handle_v2_frag_session_setup_ans(
+    dev: &device::Device,
+    pl: fragmentation::v2::FragSessionSetupAnsPayload,
+) -> Result<()> {
+    info!("Handling FragSessionSetupAns");
+
+    let mut fuota_dev = fuota::get_latest_device_by_dev_eui(dev.dev_eui).await?;
+
+    if pl.frag_algo_unsupported
+        | pl.not_enough_memory
+        | pl.frag_index_unsupported
+        | pl.wrong_descriptor
+        | pl.session_cnt_replay
+    {
+        warn!(
+            frag_index = pl.frag_index,
+            frag_algo_unsupported = pl.frag_algo_unsupported,
+            not_enough_memory = pl.not_enough_memory,
+            frag_index_unsupported = pl.frag_index_unsupported,
+            wrong_descriptor = pl.wrong_descriptor,
+            session_cnt_replay = pl.session_cnt_replay,
+            "FragSessionAns contains errors"
+        );
+        fuota_dev.error_msg = format!("Error: FragSessionAns response frag_algo_unsupported={}, not_enough_memory={}, frag_index_unsupported={}, wrong_descriptor={}, session_cnt_replay={}", pl.frag_algo_unsupported, pl.not_enough_memory, pl.frag_index_unsupported, pl.wrong_descriptor, pl.session_cnt_replay);
+    } else {
+        fuota_dev.frag_session_setup_completed_at = Some(Utc::now());
+    }
+
+    let _ = fuota::update_device(fuota_dev).await?;
+
+    Ok(())
+}
+
 async fn handle_v1_frag_session_status_ans(
     dev: &device::Device,
     pl: fragmentation::v1::FragSessionStatusAnsPayload,
@@ -94,3 +144,36 @@ async fn handle_v1_frag_session_status_ans(
 
     Ok(())
 }
+
+async fn handle_v2_frag_session_status_ans(
+    dev: &device::Device,
+    pl: fragmentation::v2::FragSessionStatusAnsPayload,
+) -> Result<()> {
+    info!("Handling FragSessionStatusAnsPayload");
+
+    let mut fuota_dev = fuota::get_latest_device_by_dev_eui(dev.dev_eui).await?;
+
+    if pl.missing_frag != 0
+        || pl.status.memory_error
+        || pl.status.mic_error
+        || pl.status.session_does_not_exist
+    {
+        warn!(
+            frag_index = pl.received_and_index.frag_index,
+            nb_frag_received = pl.received_and_index.nb_frag_received,
+            missing_frag = pl.missing_frag,
+            memory_error = pl.status.memory_error,
+            mic_error = pl.status.mic_error,
+            session_does_not_exist = pl.status.session_does_not_exist,
+            "FragSessionStatusAns contains errors"
+        );
+
+        fuota_dev.error_msg = format!("Error: FragSessionStatusAns response nb_frag_received={}, missing_frag={}, memory_error={}, mic_error={}, session_does_not_exist={}", pl.received_and_index.nb_frag_received, pl.missing_frag, pl.status.memory_error, pl.status.mic_error, pl.status.session_does_not_exist);
+    } else {
+        fuota_dev.frag_status_completed_at = Some(Utc::now());
+    }
+
+    let _ = fuota::update_device(fuota_dev).await?;
+
+    Ok(())
+}
diff --git a/chirpstack/src/applayer/fuota/flow.rs b/chirpstack/src/applayer/fuota/flow.rs
index c4a57460..35984175 100644
--- a/chirpstack/src/applayer/fuota/flow.rs
+++ b/chirpstack/src/applayer/fuota/flow.rs
@@ -11,7 +11,10 @@ use lrwn::region::MacVersion;
 use crate::config;
 use crate::downlink;
 use crate::gpstime::ToGpsTime;
-use crate::storage::fields::{FuotaJob, RequestFragmentationSessionStatus};
+use crate::storage::fields::{
+    device_profile::Ts004Version, device_profile::Ts005Version, FuotaJob,
+    RequestFragmentationSessionStatus,
+};
 use crate::storage::{device, device_keys, device_profile, device_queue, fuota, multicast};
 
 pub struct Flow {
@@ -109,14 +112,30 @@ impl Flow {
         self.job.attempt_count += 1;
 
         // Get McAppSKey + McNwkSKey.
-        let mc_app_s_key = multicastsetup::v1::get_mc_app_s_key(
-            self.fuota_deployment.multicast_key,
-            self.fuota_deployment.multicast_addr,
-        )?;
-        let mc_nwk_s_key = multicastsetup::v1::get_mc_net_s_key(
-            self.fuota_deployment.multicast_key,
-            self.fuota_deployment.multicast_addr,
-        )?;
+        let (mc_app_s_key, mc_nwk_s_key) = match self.device_profile.app_layer_params.ts005_version
+        {
+            Some(Ts005Version::V100) => (
+                multicastsetup::v1::get_mc_app_s_key(
+                    self.fuota_deployment.multicast_key,
+                    self.fuota_deployment.multicast_addr,
+                )?,
+                multicastsetup::v1::get_mc_net_s_key(
+                    self.fuota_deployment.multicast_key,
+                    self.fuota_deployment.multicast_addr,
+                )?,
+            ),
+            Some(Ts005Version::V200) => (
+                multicastsetup::v2::get_mc_app_s_key(
+                    self.fuota_deployment.multicast_key,
+                    self.fuota_deployment.multicast_addr,
+                )?,
+                multicastsetup::v2::get_mc_net_s_key(
+                    self.fuota_deployment.multicast_key,
+                    self.fuota_deployment.multicast_addr,
+                )?,
+            ),
+            None => return Err(anyhow!("Device-profile does not support TS005")),
+        };
 
         let _ = multicast::create(multicast::MulticastGroup {
             id: self.fuota_deployment.id,
@@ -201,38 +220,85 @@ impl Flow {
 
         for fuota_dev in &fuota_devices {
             let dev_keys = device_keys::get(&fuota_dev.dev_eui).await?;
-            let mc_root_key = match self.device_profile.mac_version {
-                MacVersion::LORAWAN_1_0_0
-                | MacVersion::LORAWAN_1_0_1
-                | MacVersion::LORAWAN_1_0_2
-                | MacVersion::LORAWAN_1_0_3
-                | MacVersion::LORAWAN_1_0_4 => {
-                    multicastsetup::v1::get_mc_root_key_for_gen_app_key(dev_keys.gen_app_key)?
+
+            let pl = match self.device_profile.app_layer_params.ts005_version {
+                Some(Ts005Version::V100) => {
+                    let mc_root_key = match self.device_profile.mac_version {
+                        MacVersion::LORAWAN_1_0_0
+                        | MacVersion::LORAWAN_1_0_1
+                        | MacVersion::LORAWAN_1_0_2
+                        | MacVersion::LORAWAN_1_0_3
+                        | MacVersion::LORAWAN_1_0_4 => {
+                            multicastsetup::v1::get_mc_root_key_for_gen_app_key(
+                                dev_keys.gen_app_key,
+                            )?
+                        }
+                        MacVersion::LORAWAN_1_1_0 | MacVersion::Latest => {
+                            multicastsetup::v1::get_mc_root_key_for_app_key(dev_keys.app_key)?
+                        }
+                    };
+                    let mc_ke_key = multicastsetup::v1::get_mc_ke_key(mc_root_key)?;
+                    let mc_key_encrypted = multicastsetup::v1::encrypt_mc_key(
+                        mc_ke_key,
+                        self.fuota_deployment.multicast_key,
+                    );
+
+                    multicastsetup::v1::Payload::McGroupSetupReq(
+                        multicastsetup::v1::McGroupSetupReqPayload {
+                            mc_group_id_header:
+                                multicastsetup::v1::McGroupSetupReqPayloadMcGroupIdHeader {
+                                    mc_group_id: 0,
+                                },
+                            mc_addr: self.fuota_deployment.multicast_addr,
+                            mc_key_encrypted,
+                            min_mc_f_count: 0,
+                            max_mc_f_count: u32::MAX,
+                        },
+                    )
+                    .to_vec()?
                 }
-                MacVersion::LORAWAN_1_1_0 | MacVersion::Latest => {
-                    multicastsetup::v1::get_mc_root_key_for_app_key(dev_keys.app_key)?
+                Some(Ts005Version::V200) => {
+                    let mc_root_key = match self.device_profile.mac_version {
+                        MacVersion::LORAWAN_1_0_0
+                        | MacVersion::LORAWAN_1_0_1
+                        | MacVersion::LORAWAN_1_0_2
+                        | MacVersion::LORAWAN_1_0_3
+                        | MacVersion::LORAWAN_1_0_4 => {
+                            multicastsetup::v2::get_mc_root_key_for_gen_app_key(
+                                dev_keys.gen_app_key,
+                            )?
+                        }
+                        MacVersion::LORAWAN_1_1_0 | MacVersion::Latest => {
+                            multicastsetup::v2::get_mc_root_key_for_app_key(dev_keys.app_key)?
+                        }
+                    };
+                    let mc_ke_key = multicastsetup::v2::get_mc_ke_key(mc_root_key)?;
+                    let mc_key_encrypted = multicastsetup::v2::encrypt_mc_key(
+                        mc_ke_key,
+                        self.fuota_deployment.multicast_key,
+                    );
+
+                    multicastsetup::v2::Payload::McGroupSetupReq(
+                        multicastsetup::v2::McGroupSetupReqPayload {
+                            mc_group_id_header:
+                                multicastsetup::v2::McGroupSetupReqPayloadMcGroupIdHeader {
+                                    mc_group_id: 0,
+                                },
+                            mc_addr: self.fuota_deployment.multicast_addr,
+                            mc_key_encrypted,
+                            min_mc_f_count: 0,
+                            max_mc_f_count: u32::MAX,
+                        },
+                    )
+                    .to_vec()?
                 }
+                None => return Err(anyhow!("Device-profile does not support TS005")),
             };
-            let mc_ke_key = multicastsetup::v1::get_mc_ke_key(mc_root_key)?;
-            let mc_key_encrypted =
-                multicastsetup::v1::encrypt_mc_key(mc_ke_key, self.fuota_deployment.multicast_key);
 
-            let pl = multicastsetup::v1::Payload::McGroupSetupReq(
-                multicastsetup::v1::McGroupSetupReqPayload {
-                    mc_group_id_header: multicastsetup::v1::McGroupSetupReqPayloadMcGroupIdHeader {
-                        mc_group_id: 0,
-                    },
-                    mc_addr: self.fuota_deployment.multicast_addr,
-                    mc_key_encrypted,
-                    min_mc_f_count: 0,
-                    max_mc_f_count: u32::MAX,
-                },
-            );
-
-            device_queue::enqueue_item(device_queue::DeviceQueueItem {
+            let _ = device_queue::enqueue_item(device_queue::DeviceQueueItem {
                 dev_eui: fuota_dev.dev_eui,
                 f_port: self.device_profile.app_layer_params.ts005_f_port.into(),
-                data: pl.to_vec()?,
+                data: pl,
                 ..Default::default()
             })
             .await?;
@@ -285,27 +351,74 @@ impl Flow {
         }
 
         for fuota_dev in &fuota_devices {
-            let pl = fragmentation::v1::Payload::FragSessionSetupReq(
-                fragmentation::v1::FragSessionSetupReqPayload {
-                    frag_session: fragmentation::v1::FragSessionSetuReqPayloadFragSession {
-                        mc_group_bit_mask: [true, false, false, false],
-                        frag_index: 0,
+            let pl = match self.device_profile.app_layer_params.ts004_version {
+                Some(Ts004Version::V100) => fragmentation::v1::Payload::FragSessionSetupReq(
+                    fragmentation::v1::FragSessionSetupReqPayload {
+                        frag_session: fragmentation::v1::FragSessionSetuReqPayloadFragSession {
+                            mc_group_bit_mask: [true, false, false, false],
+                            frag_index: 0,
+                        },
+                        nb_frag: fragments as u16,
+                        frag_size: fragment_size as u8,
+                        padding: padding as u8,
+                        control: fragmentation::v1::FragSessionSetuReqPayloadControl {
+                            block_ack_delay: 0,
+                            fragmentation_matrix: 0,
+                        },
+                        descriptor: [0, 0, 0, 0],
                     },
-                    nb_frag: fragments as u16,
-                    frag_size: fragment_size as u8,
-                    padding: padding as u8,
-                    control: fragmentation::v1::FragSessionSetuReqPayloadControl {
-                        block_ack_delay: 0,
-                        fragmentation_matrix: 0,
-                    },
-                    descriptor: [0, 0, 0, 0],
-                },
-            );
+                )
+                .to_vec()?,
+                Some(Ts004Version::V200) => {
+                    let dev_keys = device_keys::get(&fuota_dev.dev_eui).await?;
+                    let data_block_int_key = match self.device_profile.mac_version {
+                        MacVersion::LORAWAN_1_0_0
+                        | MacVersion::LORAWAN_1_0_1
+                        | MacVersion::LORAWAN_1_0_2
+                        | MacVersion::LORAWAN_1_0_3
+                        | MacVersion::LORAWAN_1_0_4 => {
+                            fragmentation::v2::get_data_block_int_key(dev_keys.gen_app_key)?
+                        }
+                        MacVersion::LORAWAN_1_1_0 | MacVersion::Latest => {
+                            fragmentation::v2::get_data_block_int_key(dev_keys.app_key)?
+                        }
+                    };
+                    let mic = fragmentation::v2::calculate_mic(
+                        data_block_int_key,
+                        0,
+                        0,
+                        [0, 0, 0, 0],
+                        &self.fuota_deployment.payload,
+                    )?;
 
-            device_queue::enqueue_item(device_queue::DeviceQueueItem {
+                    fragmentation::v2::Payload::FragSessionSetupReq(
+                        fragmentation::v2::FragSessionSetupReqPayload {
+                            frag_session: fragmentation::v2::FragSessionSetuReqPayloadFragSession {
+                                mc_group_bit_mask: [true, false, false, false],
+                                frag_index: 0,
+                            },
+                            nb_frag: fragments as u16,
+                            frag_size: fragment_size as u8,
+                            padding: padding as u8,
+                            control: fragmentation::v2::FragSessionSetuReqPayloadControl {
+                                block_ack_delay: 0,
+                                frag_algo: 0,
+                                ack_reception: false,
+                            },
+                            descriptor: [0, 0, 0, 0],
+                            mic,
+                            session_cnt: 0,
+                        },
+                    )
+                    .to_vec()?
+                }
+                None => return Err(anyhow!("Device-profile does not support TS004")),
+            };
+
+            let _ = device_queue::enqueue_item(device_queue::DeviceQueueItem {
                 dev_eui: fuota_dev.dev_eui,
                 f_port: self.device_profile.app_layer_params.ts004_f_port.into(),
-                data: pl.to_vec()?,
+                data: pl,
                 ..Default::default()
             })
             .await?;
@@ -362,51 +475,98 @@ impl Flow {
             .num_seconds()
                 % (1 << 32);
 
-            let pl = match self.fuota_deployment.multicast_group_type.as_ref() {
-                "B" => multicastsetup::v1::Payload::McClassBSessionReq(
-                    multicastsetup::v1::McClassBSessionReqPayload {
-                        mc_group_id_header:
-                            multicastsetup::v1::McClassBSessionReqPayloadMcGroupIdHeader {
-                                mc_group_id: 0,
+            let pl = match self.device_profile.app_layer_params.ts005_version {
+                Some(Ts005Version::V100) => {
+                    match self.fuota_deployment.multicast_group_type.as_ref() {
+                        "B" => multicastsetup::v1::Payload::McClassBSessionReq(
+                            multicastsetup::v1::McClassBSessionReqPayload {
+                                mc_group_id_header:
+                                    multicastsetup::v1::McClassBSessionReqPayloadMcGroupIdHeader {
+                                        mc_group_id: 0,
+                                    },
+                                session_time: (session_start - (session_start % 128)) as u32,
+                                time_out_periodicity:
+                                    multicastsetup::v1::McClassBSessionReqPayloadTimeOutPeriodicity {
+                                        time_out: self.fuota_deployment.multicast_timeout as u8,
+                                        periodicity: self.fuota_deployment.multicast_class_b_ping_slot_nb_k
+                                            as u8,
+                                    },
+                                dl_frequ: self.fuota_deployment.multicast_frequency as u32,
+                                dr: self.fuota_deployment.multicast_dr as u8,
                             },
-                        session_time: (session_start - (session_start % 128)) as u32,
-                        time_out_periodicity:
-                            multicastsetup::v1::McClassBSessionReqPayloadTimeOutPeriodicity {
-                                time_out: self.fuota_deployment.multicast_timeout as u8,
-                                periodicity: self.fuota_deployment.multicast_class_b_ping_slot_nb_k
-                                    as u8,
+                        ).to_vec()?,
+                        "C" => multicastsetup::v1::Payload::McClassCSessionReq(
+                            multicastsetup::v1::McClassCSessionReqPayload {
+                                mc_group_id_header:
+                                    multicastsetup::v1::McClassCSessionReqPayloadMcGroupIdHeader {
+                                        mc_group_id: 0,
+                                    },
+                                session_time: session_start as u32,
+                                session_time_out:
+                                    multicastsetup::v1::McClassCSessionReqPayloadSessionTimeOut {
+                                        time_out: self.fuota_deployment.multicast_timeout as u8,
+                                    },
+                                dl_frequ: self.fuota_deployment.multicast_frequency as u32,
+                                dr: self.fuota_deployment.multicast_dr as u8,
                             },
-                        dl_frequ: self.fuota_deployment.multicast_frequency as u32,
-                        dr: self.fuota_deployment.multicast_dr as u8,
-                    },
-                ),
-                "C" => multicastsetup::v1::Payload::McClassCSessionReq(
-                    multicastsetup::v1::McClassCSessionReqPayload {
-                        mc_group_id_header:
-                            multicastsetup::v1::McClassCSessionReqPayloadMcGroupIdHeader {
-                                mc_group_id: 0,
-                            },
-                        session_time: session_start as u32,
-                        session_time_out:
-                            multicastsetup::v1::McClassCSessionReqPayloadSessionTimeOut {
-                                time_out: self.fuota_deployment.multicast_timeout as u8,
-                            },
-                        dl_frequ: self.fuota_deployment.multicast_frequency as u32,
-                        dr: self.fuota_deployment.multicast_dr as u8,
-                    },
-                ),
-                _ => {
-                    return Err(anyhow!(
-                        "Unsupported group-type: {}",
-                        self.fuota_deployment.multicast_group_type
-                    ))
+                        ).to_vec()?,
+                        _ => {
+                            return Err(anyhow!(
+                                "Unsupported group-type: {}",
+                                self.fuota_deployment.multicast_group_type
+                            ))
+                        }
+                    }
                 }
+                Some(Ts005Version::V200) => {
+                    match self.fuota_deployment.multicast_group_type.as_ref() {
+                        "B" => multicastsetup::v2::Payload::McClassBSessionReq(
+                            multicastsetup::v2::McClassBSessionReqPayload {
+                                mc_group_id_header:
+                                    multicastsetup::v2::McClassBSessionReqPayloadMcGroupIdHeader {
+                                        mc_group_id: 0,
+                                    },
+                                session_time: (session_start - (session_start % 128)) as u32,
+                                time_out_periodicity:
+                                    multicastsetup::v2::McClassBSessionReqPayloadTimeOutPeriodicity {
+                                        time_out: self.fuota_deployment.multicast_timeout as u8,
+                                        periodicity: self.fuota_deployment.multicast_class_b_ping_slot_nb_k
+                                            as u8,
+                                    },
+                                dl_frequ: self.fuota_deployment.multicast_frequency as u32,
+                                dr: self.fuota_deployment.multicast_dr as u8,
+                            },
+                        ).to_vec()?,
+                        "C" => multicastsetup::v2::Payload::McClassCSessionReq(
+                            multicastsetup::v2::McClassCSessionReqPayload {
+                                mc_group_id_header:
+                                    multicastsetup::v2::McClassCSessionReqPayloadMcGroupIdHeader {
+                                        mc_group_id: 0,
+                                    },
+                                session_time: session_start as u32,
+                                session_time_out:
+                                    multicastsetup::v2::McClassCSessionReqPayloadSessionTimeOut {
+                                        time_out: self.fuota_deployment.multicast_timeout as u8,
+                                    },
+                                dl_frequ: self.fuota_deployment.multicast_frequency as u32,
+                                dr: self.fuota_deployment.multicast_dr as u8,
+                            },
+                        ).to_vec()?,
+                        _ => {
+                            return Err(anyhow!(
+                                "Unsupported group-type: {}",
+                                self.fuota_deployment.multicast_group_type
+                            ))
+                        }
+                    }
+                }
+                None => return Err(anyhow!("Device-profile does not support TS005")),
             };
 
             device_queue::enqueue_item(device_queue::DeviceQueueItem {
                 dev_eui: fuota_dev.dev_eui,
                 f_port: self.device_profile.app_layer_params.ts005_f_port.into(),
-                data: pl.to_vec()?,
+                data: pl,
                 ..Default::default()
             })
             .await?;
@@ -459,22 +619,55 @@ impl Flow {
         let mut payload = self.fuota_deployment.payload.clone();
         payload.extend_from_slice(&vec![0; padding]);
 
-        let encoded_fragments = fragmentation::v1::encode(&payload, fragment_size, redundancy)?;
-
-        for (i, frag) in encoded_fragments.iter().enumerate() {
-            let pl =
-                fragmentation::v1::Payload::DataFragment(fragmentation::v1::DataFragmentPayload {
-                    index_and_n: fragmentation::v1::DataFragmentPayloadIndexAndN {
-                        frag_index: 0,
-                        n: (i + 1) as u16,
-                    },
-                    data: frag.clone(),
-                });
+        let payloads = match self.device_profile.app_layer_params.ts004_version {
+            Some(Ts004Version::V100) => {
+                let mut payloads = Vec::new();
+                let encoded_fragments =
+                    fragmentation::v1::encode(&payload, fragment_size, redundancy)?;
+                for (i, frag) in encoded_fragments.iter().enumerate() {
+                    payloads.push(
+                        fragmentation::v1::Payload::DataFragment(
+                            fragmentation::v1::DataFragmentPayload {
+                                index_and_n: fragmentation::v1::DataFragmentPayloadIndexAndN {
+                                    frag_index: 0,
+                                    n: (i + 1) as u16,
+                                },
+                                data: frag.clone(),
+                            },
+                        )
+                        .to_vec()?,
+                    );
+                }
+                payloads
+            }
+            Some(Ts004Version::V200) => {
+                let mut payloads = Vec::new();
+                let encoded_fragments =
+                    fragmentation::v2::encode(&payload, fragment_size, redundancy)?;
+                for (i, frag) in encoded_fragments.iter().enumerate() {
+                    payloads.push(
+                        fragmentation::v2::Payload::DataFragment(
+                            fragmentation::v2::DataFragmentPayload {
+                                index_and_n: fragmentation::v2::DataFragmentPayloadIndexAndN {
+                                    frag_index: 0,
+                                    n: (i + 1) as u16,
+                                },
+                                data: frag.clone(),
+                            },
+                        )
+                        .to_vec()?,
+                    );
+                }
+                payloads
+            }
+            None => return Err(anyhow!("Device-profile does not support TS004")),
+        };
 
+        for pl in payloads {
             let _ = downlink::multicast::enqueue(multicast::MulticastGroupQueueItem {
                 multicast_group_id: self.fuota_deployment.id,
                 f_port: self.device_profile.app_layer_params.ts004_f_port as i16,
-                data: pl.to_vec()?,
+                data: pl,
                 ..Default::default()
             })
             .await?;
@@ -524,17 +717,28 @@ impl Flow {
         }
 
         for fuota_dev in &fuota_devices {
-            let pl = fragmentation::v1::Payload::FragSessionStatusReq(
-                fragmentation::v1::FragSessionStatusReqPayload {
-                    participants: true,
-                    frag_index: 0,
-                },
-            );
+            let pl = match self.device_profile.app_layer_params.ts004_version {
+                Some(Ts004Version::V100) => fragmentation::v1::Payload::FragSessionStatusReq(
+                    fragmentation::v1::FragSessionStatusReqPayload {
+                        participants: true,
+                        frag_index: 0,
+                    },
+                )
+                .to_vec()?,
+                Some(Ts004Version::V200) => fragmentation::v2::Payload::FragSessionStatusReq(
+                    fragmentation::v2::FragSessionStatusReqPayload {
+                        participants: true,
+                        frag_index: 0,
+                    },
+                )
+                .to_vec()?,
+                None => return Err(anyhow!("Device-profile does not support TS004")),
+            };
 
             device_queue::enqueue_item(device_queue::DeviceQueueItem {
                 dev_eui: fuota_dev.dev_eui,
                 f_port: self.device_profile.app_layer_params.ts004_f_port.into(),
-                data: pl.to_vec()?,
+                data: pl,
                 ..Default::default()
             })
             .await?;
diff --git a/chirpstack/src/applayer/multicastsetup.rs b/chirpstack/src/applayer/multicastsetup.rs
index c070273e..d407a5cc 100644
--- a/chirpstack/src/applayer/multicastsetup.rs
+++ b/chirpstack/src/applayer/multicastsetup.rs
@@ -18,6 +18,7 @@ pub async fn handle_uplink(
 
     match version {
         Ts005Version::V100 => handle_uplink_v100(dev, data).await,
+        Ts005Version::V200 => handle_uplink_v200(dev, data).await,
     }
 }
 
@@ -40,6 +41,25 @@ async fn handle_uplink_v100(dev: &device::Device, data: &[u8]) -> Result<()> {
     Ok(())
 }
 
+async fn handle_uplink_v200(dev: &device::Device, data: &[u8]) -> Result<()> {
+    let pl = multicastsetup::v2::Payload::from_slice(true, data)?;
+
+    match pl {
+        multicastsetup::v2::Payload::McGroupSetupAns(pl) => {
+            handle_v2_mc_group_setup_ans(dev, pl).await?
+        }
+        multicastsetup::v2::Payload::McClassBSessionAns(pl) => {
+            handle_v2_mc_class_b_session_ans(dev, pl).await?
+        }
+        multicastsetup::v2::Payload::McClassCSessionAns(pl) => {
+            handle_v2_mc_class_c_session_ans(dev, pl).await?
+        }
+        _ => {}
+    }
+
+    Ok(())
+}
+
 async fn handle_v1_mc_group_setup_ans(
     dev: &device::Device,
     pl: multicastsetup::v1::McGroupSetupAnsPayload,
@@ -64,6 +84,30 @@ async fn handle_v1_mc_group_setup_ans(
     Ok(())
 }
 
+async fn handle_v2_mc_group_setup_ans(
+    dev: &device::Device,
+    pl: multicastsetup::v2::McGroupSetupAnsPayload,
+) -> Result<()> {
+    info!("Handling McGroupSetupAns");
+
+    let mut fuota_dev = fuota::get_latest_device_by_dev_eui(dev.dev_eui).await?;
+
+    if pl.mc_group_id_header.id_error {
+        warn!(
+            mc_group_id = pl.mc_group_id_header.mc_group_id,
+            id_error = true,
+            "McGroupSetupAns contains errors"
+        );
+        fuota_dev.error_msg = "Error: McGroupSetupAns response id_error=true".into();
+    } else {
+        fuota_dev.mc_group_setup_completed_at = Some(Utc::now());
+    }
+
+    let _ = fuota::update_device(fuota_dev).await?;
+
+    Ok(())
+}
+
 async fn handle_v1_mc_class_b_session_ans(
     dev: &device::Device,
     pl: multicastsetup::v1::McClassBSessionAnsPayload,
@@ -101,6 +145,46 @@ async fn handle_v1_mc_class_b_session_ans(
     Ok(())
 }
 
+async fn handle_v2_mc_class_b_session_ans(
+    dev: &device::Device,
+    pl: multicastsetup::v2::McClassBSessionAnsPayload,
+) -> Result<()> {
+    info!("Handling McClassBSessionAns");
+
+    let mut fuota_dev = fuota::get_latest_device_by_dev_eui(dev.dev_eui).await?;
+
+    if pl.status_and_mc_group_id.dr_error
+        | pl.status_and_mc_group_id.freq_error
+        | pl.status_and_mc_group_id.mc_group_undefined
+        | pl.status_and_mc_group_id.start_missed
+    {
+        warn!(
+            dr_error = pl.status_and_mc_group_id.dr_error,
+            freq_error = pl.status_and_mc_group_id.freq_error,
+            mc_group_undefined = pl.status_and_mc_group_id.mc_group_undefined,
+            start_missed = pl.status_and_mc_group_id.start_missed,
+            "McClassBSessionAns contains errors"
+        );
+
+        fuota_dev.error_msg= format!("Error: McClassBSessionAns response dr_error: {}, freq_error: {}, mc_group_undefined: {}, start_missed: {}",
+            pl.status_and_mc_group_id.dr_error,
+            pl.status_and_mc_group_id.freq_error,
+            pl.status_and_mc_group_id.mc_group_undefined,
+            pl.status_and_mc_group_id.start_missed,
+        );
+    } else {
+        info!(
+            time_to_start = pl.time_to_start.unwrap_or_default(),
+            "McClassBSessionAns OK"
+        );
+        fuota_dev.mc_session_completed_at = Some(Utc::now());
+    }
+
+    let _ = fuota::update_device(fuota_dev).await?;
+
+    Ok(())
+}
+
 async fn handle_v1_mc_class_c_session_ans(
     dev: &device::Device,
     pl: multicastsetup::v1::McClassCSessionAnsPayload,
@@ -137,3 +221,43 @@ async fn handle_v1_mc_class_c_session_ans(
 
     Ok(())
 }
+
+async fn handle_v2_mc_class_c_session_ans(
+    dev: &device::Device,
+    pl: multicastsetup::v2::McClassCSessionAnsPayload,
+) -> Result<()> {
+    info!("Handling McClassCSessionAns");
+
+    let mut fuota_dev = fuota::get_latest_device_by_dev_eui(dev.dev_eui).await?;
+
+    if pl.status_and_mc_group_id.dr_error
+        | pl.status_and_mc_group_id.freq_error
+        | pl.status_and_mc_group_id.mc_group_undefined
+        | pl.status_and_mc_group_id.start_missed
+    {
+        warn!(
+            dr_error = pl.status_and_mc_group_id.dr_error,
+            freq_error = pl.status_and_mc_group_id.freq_error,
+            mc_group_undefined = pl.status_and_mc_group_id.mc_group_undefined,
+            start_missed = pl.status_and_mc_group_id.start_missed,
+            "McClassCSessionAns contains errors"
+        );
+
+        fuota_dev.error_msg = format!("Error: McClassCSessionAns response dr_error: {}, freq_error: {}, mc_group_undefined: {}, start_missed: {}",
+            pl.status_and_mc_group_id.dr_error,
+            pl.status_and_mc_group_id.freq_error,
+            pl.status_and_mc_group_id.mc_group_undefined,
+            pl.status_and_mc_group_id.start_missed,
+        );
+    } else {
+        info!(
+            time_to_start = pl.time_to_start.unwrap_or_default(),
+            "McClassCSessionAns OK"
+        );
+        fuota_dev.mc_session_completed_at = Some(Utc::now());
+    }
+
+    let _ = fuota::update_device(fuota_dev).await?;
+
+    Ok(())
+}
diff --git a/chirpstack/src/storage/fields/device_profile.rs b/chirpstack/src/storage/fields/device_profile.rs
index d91e03cf..b38263b0 100644
--- a/chirpstack/src/storage/fields/device_profile.rs
+++ b/chirpstack/src/storage/fields/device_profile.rs
@@ -314,16 +314,19 @@ impl serialize::ToSql<Text, Sqlite> for AppLayerParams {
 pub enum Ts003Version {
     #[default]
     V100,
+    V200,
 }
 
 #[derive(Default, Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
 pub enum Ts004Version {
     #[default]
     V100,
+    V200,
 }
 
 #[derive(Default, Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
 pub enum Ts005Version {
     #[default]
     V100,
+    V200,
 }
diff --git a/lrwn/src/applayer/fragmentation/v2.rs b/lrwn/src/applayer/fragmentation/v2.rs
index 400cdbea..989f6f5a 100644
--- a/lrwn/src/applayer/fragmentation/v2.rs
+++ b/lrwn/src/applayer/fragmentation/v2.rs
@@ -1,6 +1,15 @@
+#[cfg(feature = "crypto")]
+use aes::{
+    cipher::generic_array::GenericArray,
+    cipher::{BlockEncrypt, KeyInit},
+    Aes128, Block,
+};
 use anyhow::Result;
+#[cfg(feature = "crypto")]
+use cmac::{Cmac, Mac};
 
 use crate::applayer::PayloadCodec;
+use crate::AES128Key;
 
 pub enum Cid {
     PackageVersionReq,
@@ -662,6 +671,48 @@ fn matrix_line(n: usize, m: usize) -> Vec<usize> {
     line
 }
 
+pub fn get_data_block_int_key(app_key: AES128Key) -> Result<AES128Key> {
+    let mut b: [u8; 16] = [0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+
+    let key_bytes = app_key.to_bytes();
+    let key = GenericArray::from_slice(&key_bytes);
+    let cipher = Aes128::new(key);
+
+    let block = Block::from_mut_slice(&mut b);
+    cipher.encrypt_block(block);
+
+    Ok(AES128Key::from_slice(block)?)
+}
+
+pub fn calculate_mic(
+    data_block_int_key: AES128Key,
+    session_cnt: u16,
+    frag_index: u8,
+    descriptor: [u8; 4],
+    data: &[u8],
+) -> Result<[u8; 4]> {
+    let mut b0: [u8; 16] = [0; 16];
+    b0[0] = 0x49;
+    b0[1..3].clone_from_slice(&session_cnt.to_le_bytes());
+    b0[3] = frag_index;
+    b0[4..8].clone_from_slice(&descriptor);
+    b0[12..16].clone_from_slice(&(data.len() as u32).to_le_bytes());
+
+    let mut mac = <Cmac<Aes128> as Mac>::new_from_slice(&data_block_int_key.to_bytes()).unwrap();
+    mac.update(&b0);
+    mac.update(data);
+
+    let cmac = mac.finalize().into_bytes();
+    if cmac.len() < 4 {
+        return Err(anyhow!("cmac is less than 4 bytes"));
+    }
+
+    let mut mic: [u8; 4] = [0; 4];
+    mic.clone_from_slice(&cmac[0..4]);
+
+    Ok(mic)
+}
+
 #[cfg(test)]
 mod test {
     use super::*;
diff --git a/lrwn/src/applayer/multicastsetup/v1.rs b/lrwn/src/applayer/multicastsetup/v1.rs
index dddf816f..816d3d69 100644
--- a/lrwn/src/applayer/multicastsetup/v1.rs
+++ b/lrwn/src/applayer/multicastsetup/v1.rs
@@ -1,6 +1,9 @@
-use aes::cipher::BlockDecrypt;
-use aes::cipher::{generic_array::GenericArray, BlockEncrypt, KeyInit};
-use aes::{Aes128, Block};
+#[cfg(feature = "crypto")]
+use aes::{
+    cipher::BlockDecrypt,
+    cipher::{generic_array::GenericArray, BlockEncrypt, KeyInit},
+    Aes128, Block,
+};
 use anyhow::Result;
 
 use crate::applayer::PayloadCodec;
diff --git a/lrwn/src/applayer/multicastsetup/v2.rs b/lrwn/src/applayer/multicastsetup/v2.rs
index e2deb18b..0df56fa7 100644
--- a/lrwn/src/applayer/multicastsetup/v2.rs
+++ b/lrwn/src/applayer/multicastsetup/v2.rs
@@ -1,6 +1,9 @@
-use aes::cipher::BlockDecrypt;
-use aes::cipher::{generic_array::GenericArray, BlockEncrypt, KeyInit};
-use aes::{Aes128, Block};
+#[cfg(feature = "crypto")]
+use aes::{
+    cipher::BlockDecrypt,
+    cipher::{generic_array::GenericArray, BlockEncrypt, KeyInit},
+    Aes128, Block,
+};
 use anyhow::Result;
 
 use crate::applayer::PayloadCodec;
diff --git a/ui/src/views/device-profiles/DeviceProfileForm.tsx b/ui/src/views/device-profiles/DeviceProfileForm.tsx
index 59c589ae..75d85f70 100644
--- a/ui/src/views/device-profiles/DeviceProfileForm.tsx
+++ b/ui/src/views/device-profiles/DeviceProfileForm.tsx
@@ -1077,6 +1077,7 @@ function DeviceProfileForm(props: IProps) {
                 <Select disabled={props.disabled}>
                   <Select.Option value={Ts003Version.TS003_NOT_IMPLEMENTED}>Not implemented</Select.Option>
                   <Select.Option value={Ts003Version.TS003_V100}>v1.0.0</Select.Option>
+                  <Select.Option value={Ts003Version.TS003_V200}>v2.0.0</Select.Option>
                 </Select>
               </Form.Item>
             </Col>
@@ -1096,6 +1097,7 @@ function DeviceProfileForm(props: IProps) {
                 <Select disabled={props.disabled}>
                   <Select.Option value={Ts004Version.TS004_NOT_IMPLEMENTED}>Not implemented</Select.Option>
                   <Select.Option value={Ts004Version.TS004_V100}>v1.0.0</Select.Option>
+                  <Select.Option value={Ts004Version.TS004_V200}>v2.0.0</Select.Option>
                 </Select>
               </Form.Item>
             </Col>
@@ -1115,6 +1117,7 @@ function DeviceProfileForm(props: IProps) {
                 <Select disabled={props.disabled}>
                   <Select.Option value={Ts005Version.TS005_NOT_IMPLEMENTED}>Not implemented</Select.Option>
                   <Select.Option value={Ts005Version.TS005_V100}>v1.0.0</Select.Option>
+                  <Select.Option value={Ts005Version.TS005_V200}>v2.0.0</Select.Option>
                 </Select>
               </Form.Item>
             </Col>