diff --git a/csharp/App/SinexcelCommunication/DataTypes/DynamicPricingMode.cs b/csharp/App/SinexcelCommunication/DataTypes/DynamicPricingMode.cs
new file mode 100644
index 000000000..2391f93b4
--- /dev/null
+++ b/csharp/App/SinexcelCommunication/DataTypes/DynamicPricingMode.cs
@@ -0,0 +1,8 @@
+namespace InnovEnergy.App.SinexcelCommunication.DataTypes;
+
+public enum DynamicPricingMode
+{
+ Disabled,
+ Tou,
+ SpotPrice
+}
\ No newline at end of file
diff --git a/csharp/App/SinexcelCommunication/ESS/DynamicPriceDataType.cs b/csharp/App/SinexcelCommunication/ESS/DynamicPriceDataType.cs
new file mode 100644
index 000000000..bb2638b7e
--- /dev/null
+++ b/csharp/App/SinexcelCommunication/ESS/DynamicPriceDataType.cs
@@ -0,0 +1,10 @@
+namespace InnovEnergy.App.SinexcelCommunication.ESS;
+
+internal enum DesiredAction
+{
+ OptimizeSelfUse,
+ Charge,
+ Discharge
+}
+
+
diff --git a/csharp/App/SinexcelCommunication/ESS/DynamicPricingEngine.cs b/csharp/App/SinexcelCommunication/ESS/DynamicPricingEngine.cs
new file mode 100644
index 000000000..52e95809b
--- /dev/null
+++ b/csharp/App/SinexcelCommunication/ESS/DynamicPricingEngine.cs
@@ -0,0 +1,203 @@
+using System;
+using InnovEnergy.App.SinexcelCommunication.DataTypes;
+using InnovEnergy.App.SinexcelCommunication.SystemConfig;
+using InnovEnergy.Lib.Devices.Sinexcel_12K_TL.DataType;
+
+namespace InnovEnergy.App.SinexcelCommunication.ESS;
+/*
+public static class DynamicPricingEngine
+ {
+
+ public static readonly TimeSpan CheapStart = TimeSpan.FromHours(22); // 22:00
+ public static readonly TimeSpan CheapEnd = TimeSpan.FromHours(6); // 06:00
+
+ // Expensive (High tariff)
+ public static readonly TimeSpan HighStart = TimeSpan.FromHours(17); // 17:00
+ public static readonly TimeSpan HighEnd = TimeSpan.FromHours(21); // 21:00
+
+ ///
+ /// Call this from your main loop. It sets statusrecord.Mode.
+ ///
+ /// liveSpotPrice is only needed when DynamicPricingMode == SpotPrice.
+ /// If your inverter cannot directly force Charge/Discharge, set inverterSupportsDirectForce=false
+ /// and it will execute via TimeChargeDischarge + rolling short time window.
+ ///
+ public static void Apply(
+ DateTime nowLocal,
+ Decimal? liveSpotPrice,
+ StatusRecord statusrecord,
+ Boolean inverterSupportsDirectForce,
+ Int32 rollingWindowMinutes = 10)
+ {
+ if (statusrecord == null) throw new ArgumentNullException(nameof(statusrecord));
+ if (statusrecord.Config == null) throw new ArgumentNullException(nameof(statusrecord.Config));
+
+ var c = statusrecord.Config;
+
+ // 0) Manual override (optional)
+ if (!c.DynamicPricingEnabled)
+ {
+ Console.WriteLine(" Dynamic pricing is disabled");
+ return;
+ }
+
+ if (statusrecord.InverterRecord.OperatingPriority == OperatingPriority.ModeNotSynched)
+ {
+ Console.WriteLine(" Inverter mode are not synched ");
+ return;
+ }
+/*
+
+ }
+
+ // 1) Base operating mode: explicit modes ignore dynamic pricing
+ if (c.OperatingPriority == OperatingPriority.GridPriority)
+ {
+ SetMode(statusrecord, OperatingPriority.GridPriority);
+ return;
+ }
+
+ if (c.OperatingPriority == OperatingPriority.BatteryPriority)
+ {
+ SetMode(statusrecord, OperatingPriority.BatteryPriority);
+ return;
+ }
+
+ // 2) OperatingMode == OptimizeSelfUse -> dynamic pricing can apply
+ var desired = DecideDesiredAction(nowLocal, liveSpotPrice, c);
+
+ if (desired == DesiredAction.OptimizeSelfUse)
+ {
+ SetMode(statusrecord, OperatingPriority.LoadPriority);
+ return;
+ }
+
+ // 3) Execute desired action
+ if (inverterSupportsDirectForce)
+ {
+ statusrecord.Mode = desired == DesiredAction.Charge
+ ? BatteryMode.Charge
+ : BatteryMode.Discharge;
+ return;
+ }
+
+ // 4) Inverter limitation: execute via TimeChargeDischarge rolling window
+ statusrecord.Mode = BatteryMode.TimeChargeDischarge;
+
+ var (start, end) = MakeRollingWindow(nowLocal, rollingWindowMinutes);
+
+ if (desired == DesiredAction.Charge)
+ {
+ c.TimeChargeStart = start;
+ c.TimeChargeEnd = end;
+
+ // clear discharge window to avoid overlap
+ c.TimeDischargeStart = TimeSpan.Zero;
+ c.TimeDischargeEnd = TimeSpan.Zero;
+ }
+ else // Discharge
+ {
+ c.TimeDischargeStart = start;
+ c.TimeDischargeEnd = end;
+
+ // clear charge window to avoid overlap
+ c.TimeChargeStart = TimeSpan.Zero;
+ c.TimeChargeEnd = TimeSpan.Zero;
+ }
+ }
+
+
+ private static void SetMode( StatusRecord statusrecord, OperatingPriority o)
+ {
+ var operatingMode = o switch
+ {
+ OperatingPriority.LoadPriority => WorkingMode.SpontaneousSelfUse,
+ OperatingPriority.BatteryPriority => WorkingMode.TimeChargeDischarge,
+ OperatingPriority.GridPriority => WorkingMode.PrioritySellElectricity,
+ _ => WorkingMode.SpontaneousSelfUse
+ };
+
+ if (statusrecord.InverterRecord.OperatingPriority != OperatingPriority.ModeNotSynched)
+ {
+ foreach (var inv in statusrecord?.InverterRecord.Devices)
+ {
+ inv.WorkingMode = operatingMode;
+ }
+ }
+ else
+ {
+ foreach (var inv in statusrecord?.InverterRecord.Devices)
+ {
+ Console.WriteLine(" Inverter mode are not synched");
+ inv.WorkingMode = WorkingMode.SpontaneousSelfUse;
+ }
+ }
+ return;
+ }
+
+ // ----------------------------
+ // Decision logic
+ // ----------------------------
+ private static DesiredAction DecideDesiredAction(DateTime nowLocal, decimal? liveSpotPrice, Config c)
+ {
+ if (c.DynamicPricingMode == DynamicPricingMode.Disabled)
+ return DesiredAction.OptimizeSelfUse;
+
+ TimeSpan now = nowLocal.TimeOfDay;
+
+ if (c.DynamicPricingMode == DynamicPricingMode.Tou)
+ {
+ bool isCheap = IsInTimeWindow(now,CheapStart, CheapEnd);
+ bool isHigh = IsInTimeWindow(now, HighStart, HighEnd);
+
+ // Priority: cheap -> charge, then high -> discharge
+ if (isCheap) return DesiredAction.Charge;
+ if (isHigh) return DesiredAction.Discharge;
+ return DesiredAction.OptimizeSelfUse;
+ }
+
+ if (c.DynamicPricingMode == DynamicPricingMode.SpotPrice)
+ {
+ if (!liveSpotPrice.HasValue)
+ return DesiredAction.OptimizeSelfUse; // safe fallback
+
+ if (c.CheapPrice >= c.HighPrice)
+ throw new ArgumentException("Config error: CheapPrice must be lower than HighPrice.");
+
+ decimal p = liveSpotPrice.Value;
+
+ if (p <= c.CheapPrice) return DesiredAction.Charge;
+ if (p >= c.HighPrice) return DesiredAction.Discharge;
+ }
+
+ return DesiredAction.OptimizeSelfUse;
+ }
+
+ // ----------------------------
+ // Helpers
+ // ----------------------------
+ ///
+ /// [start, end) window, supports overnight. start==end means disabled.
+ ///
+ private static Boolean IsInTimeWindow(TimeSpan now, TimeSpan start, TimeSpan end)
+ {
+ if (start == end) return false;
+
+ // Same-day
+ if (start < end)
+ return now >= start && now < end;
+
+ // Overnight
+ return now >= start || now < end;
+ }
+
+ private static (TimeSpan start, TimeSpan end) MakeRollingWindow(DateTime nowLocal, int minutes)
+ {
+ if (minutes <= 0) throw new ArgumentOutOfRangeException(nameof(minutes));
+
+ var start = nowLocal.TimeOfDay;
+ var end = (nowLocal + TimeSpan.FromMinutes(minutes)).TimeOfDay;
+ return (start, end);
+ }
+ }
+}*/
\ No newline at end of file