diff --git a/include/YaSolRWebsite.h b/include/YaSolRWebsite.h
index 78597f75..ab185b90 100644
--- a/include/YaSolRWebsite.h
+++ b/include/YaSolRWebsite.h
@@ -6,6 +6,7 @@
 
 #include <YaSolR.h>
 
+#include <string>
 #include <unordered_map>
 
 namespace YaSolR {
@@ -19,23 +20,164 @@ namespace YaSolR {
       void resetPID();
 
     private:
-      void _boolConfig(Card& card, const char* key);
-      void _daysConfig(Card& card, const char* key);
-      void _floatConfig(Card& card, const char* key);
-      void _numConfig(Card& card, const char* key);
-      void _pinConfig(Card& card, const char* key);
-      void _passwordConfig(Card& card, const char* key);
-      void _sliderConfig(Card& card, const char* key);
-      void _percentageSlider(Card& card, const char* key);
-      void _textConfig(Card& card, const char* key);
-
-      void _outputDimmerSlider(Card& card, Mycila::RouterOutput& output);
-      void _outputBypassSwitch(Card& card, Mycila::RouterOutput& output);
-      void _relaySwitch(Card& card, Mycila::RouterRelay& relay);
-
-      void _pinout(Card& card, int32_t pin, std::unordered_map<int32_t, Card*>& pinout);
-      void _status(Card& card, const char* key, bool enabled, bool state = true, const char* err = "");
-      void _temperature(Card& card, Mycila::DS18& sensor);
-      void _temperature(Card& card, Mycila::RouterOutput& output);
+      void _boolConfig(dash::SwitchCard& card, const char* key) {
+        card.onChange([key, &card, this](bool value) {
+          config.setBool(key, value);
+          card.setValue(config.getBool(key) ? 1 : 0);
+          dashboard.refresh(card);
+          dashboardInitTask.resume();
+        });
+      }
+
+      template <typename T, std::enable_if_t<std::is_integral_v<T>, bool> = true>
+      void _sliderConfig(dash::SliderCard<T>& card, const char* key) {
+        card.onChange([key, &card](const T& value) {
+          config.set(key, std::to_string(value));
+          card.setValue(static_cast<T>(config.getInt(key)));
+          dashboard.refresh(card);
+        });
+      }
+
+      void _outputDimmerSlider(dash::SliderCard<float, 2>& card, Mycila::RouterOutput& output) {
+        card.onChange([&card, &output, this](float value) {
+          if (output.isDimmerEnabled()) {
+            output.setDimmerDutyCycle(value / 100);
+          }
+          card.setValue(output.getDimmerDutyCycle() * 100);
+          dashboard.refresh(card);
+          dashboardUpdateTask.requestEarlyRun();
+        });
+      }
+
+      void _outputBypassSwitch(dash::SwitchCard& card, Mycila::RouterOutput& output) {
+        card.onChange([&card, &output, this](bool value) {
+          if (output.isBypassEnabled()) {
+            output.setBypass(value);
+          }
+          card.setValue(output.isBypassOn());
+          dashboard.refresh(card);
+          dashboardUpdateTask.requestEarlyRun();
+        });
+      }
+
+      void _relaySwitch(dash::SwitchCard& card, Mycila::RouterRelay& relay) {
+        card.onChange([&card, &relay, this](bool value) {
+          relay.tryRelayState(value);
+          card.setValue(relay.isOn());
+          dashboard.refresh(card);
+        });
+      }
+
+#ifdef APP_MODEL_PRO
+      void _daysConfig(dash::WeekCard<const char*>& card, const char* key) {
+        card.onChange([key, &card, this](const char* value) {
+          config.set(key, value[0] ? value : YASOLR_WEEK_DAYS_EMPTY);
+          card.setValue(value);
+          dashboard.refresh(card);
+        });
+      }
+
+      template <typename T, std::enable_if_t<std::is_integral_v<T>, bool> = true>
+      void _numConfig(dash::TextInputCard<T>& card, const char* key) {
+        card.onChange([key, &card](const std::optional<T>& value) {
+          if (value.has_value()) {
+            config.set(key, std::to_string(value.value()));
+          } else {
+            config.unset(key);
+          }
+          card.setValue(static_cast<T>(config.getInt(key)));
+          dashboard.refresh(card);
+        });
+      }
+
+      template <typename T, std::enable_if_t<std::is_integral_v<T>, bool> = true>
+      void _numConfig(dash::DropdownCard<T>& card, const char* key) {
+        card.onChange([key, &card](const T& value) {
+          config.set(key, std::to_string(value));
+          card.setValue(config.getInt(key));
+          dashboard.refresh(card);
+        });
+      }
+
+      void _pinConfig(dash::FeedbackTextInputCard<int32_t>& card, const char* key) {
+        card.onChange([key, &card, this](const std::optional<int32_t> value) {
+          if (value.has_value()) {
+            config.set(key, std::to_string(value.value()));
+          } else {
+            config.unset(key);
+          }
+          card.setValue(config.getInt(key));
+          initCards();
+          dashboard.refresh(card);
+        });
+      }
+
+      void _passwordConfig(dash::PasswordCard& card, const char* key) {
+        card.onChange([key, &card, this](const std::optional<const char*>& value) {
+          if (value.has_value()) {
+            config.set(key, value.value());
+            card.setValue(value.value()); // will be replaced by stars
+          } else {
+            config.unset(key);
+            card.removeValue();
+          }
+          dashboard.refresh(card);
+        });
+      }
+
+      void _textConfig(dash::DropdownCard<const char*>& card, const char* key) {
+        card.onChange([key, &card](const char* value) {
+          config.set(key, value);
+          card.setValue(config.get(key));
+          dashboard.refresh(card);
+        });
+      }
+
+      void _textConfig(dash::TextInputCard<const char*>& card, const char* key) {
+        card.onChange([key, &card](const std::optional<std::string>& value) {
+          if (value.has_value()) {
+            config.set(key, value.value());
+            card.setValue(config.get(key));
+          } else {
+            config.unset(key);
+            card.removeValue();
+          }
+          dashboard.refresh(card);
+        });
+      }
+
+      void _pinout(dash::FeedbackTextInputCard<int32_t>& card, const char* key, std::unordered_map<int32_t, dash::FeedbackTextInputCard<int32_t>*>& pinout) {
+        int32_t pin = config.getInt(key);
+        card.setValue(pin);
+        if (pin == GPIO_NUM_NC) {
+          card.setFeedback("(" YASOLR_LBL_115 ")", dash::Status::IDLE);
+        } else if (pinout.find(pin) != pinout.end()) {
+          pinout[pin]->setFeedback("(" YASOLR_LBL_153 ")", dash::Status::DANGER);
+          card.setFeedback("(" YASOLR_LBL_153 ")", dash::Status::DANGER);
+        } else if (!GPIO_IS_VALID_GPIO(pin)) {
+          pinout[pin] = &card;
+          card.setFeedback("(" YASOLR_LBL_154 ")", dash::Status::DANGER);
+        } else if (!GPIO_IS_VALID_OUTPUT_GPIO(pin)) {
+          pinout[pin] = &card;
+          card.setFeedback("(" YASOLR_LBL_155 ")", dash::Status::WARNING);
+        } else {
+          pinout[pin] = &card;
+          card.setFeedback("(" YASOLR_LBL_156 ")", dash::Status::SUCCESS);
+        }
+      }
+
+      void _status(dash::FeedbackSwitchCard& card, const char* key, bool enabled, bool active = true, const char* err = "") {
+        const bool configEnabled = config.getBool(key);
+        card.setValue(configEnabled);
+        if (!configEnabled)
+          card.setFeedback(YASOLR_LBL_115, dash::Status::IDLE);
+        else if (!enabled)
+          card.setFeedback(YASOLR_LBL_124, dash::Status::DANGER);
+        else if (!active)
+          card.setFeedback(err, dash::Status::WARNING);
+        else
+          card.setFeedback(YASOLR_LBL_130, dash::Status::SUCCESS);
+      }
+#endif
   };
 } // namespace YaSolR
diff --git a/lib/ESPDASHPro b/lib/ESPDASHPro
index 72144f70..9db15bda 160000
--- a/lib/ESPDASHPro
+++ b/lib/ESPDASHPro
@@ -1 +1 @@
-Subproject commit 72144f7077d9f47f4e2c121c3104462268f7f363
+Subproject commit 9db15bdaf005c5deced496eb4388e460572ebe0b
diff --git a/platformio.ini b/platformio.ini
index 2f0715ee..cdafc7e1 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -166,7 +166,7 @@ build_flags = -D APP_MODEL_OSS
 lib_deps =
   mathieucarbou/MycilaWebSerial @ 7.0.1
   ; ayushsharma82/ESP-DASH @ 4.0.8
-  https://github.com/mathieucarbou/ayushsharma82-ESP-DASH#dev
+  https://github.com/mathieucarbou/ayushsharma82-ESP-DASH#dev-v5
 lib_ignore =
   ESPDASHPro
   WebSerialPro
diff --git a/src/Website.cpp b/src/Website.cpp
index 11b78b56..babda5b9 100644
--- a/src/Website.cpp
+++ b/src/Website.cpp
@@ -7,12 +7,10 @@
 #include <string>
 #include <unordered_map>
 
-#define HIDDEN_PWD "********"
-
 #ifdef APP_MODEL_OSS
-  #define LINE_CHART  BAR_CHART
-  #define AREA_CHART  BAR_CHART
-  #define ENERGY_CARD GENERIC_CARD
+  #define LineChart  BarChart
+  #define AreaChart  BarChart
+  #define EnergyCard GenericCard
 #endif
 
 #ifdef APP_MODEL_PRO
@@ -22,92 +20,93 @@ static const ChartSize chartSize = {.xs = 12, .sm = 12, .md = 12, .lg = 12, .xl
 int _historyX[YASOLR_GRAPH_POINTS] = {0};
 
 // statistics
-Statistic _appName = Statistic(&dashboard, YASOLR_LBL_001);
-Statistic _appModel = Statistic(&dashboard, YASOLR_LBL_002);
-Statistic _appVersion = Statistic(&dashboard, YASOLR_LBL_003);
-Statistic _appManufacturer = Statistic(&dashboard, YASOLR_LBL_004);
-
-Statistic _deviceBootCount = Statistic(&dashboard, YASOLR_LBL_005);
-Statistic _deviceBootReason = Statistic(&dashboard, YASOLR_LBL_192);
-Statistic _deviceCores = Statistic(&dashboard, YASOLR_LBL_006);
-Statistic _deviceHeapTotal = Statistic(&dashboard, YASOLR_LBL_007);
-Statistic _deviceHeapUsage = Statistic(&dashboard, YASOLR_LBL_008);
-Statistic _deviceHeapUsed = Statistic(&dashboard, YASOLR_LBL_009);
-Statistic _deviceID = Statistic(&dashboard, YASOLR_LBL_010);
-Statistic _deviceModel = Statistic(&dashboard, YASOLR_LBL_011);
-Statistic _deviceRev = Statistic(&dashboard, YASOLR_LBL_012);
-
-Statistic _firmwareBuildHash = Statistic(&dashboard, YASOLR_LBL_013);
-Statistic _firmwareBuildTimestamp = Statistic(&dashboard, YASOLR_LBL_014);
-Statistic _firmwareFilename = Statistic(&dashboard, YASOLR_LBL_015);
-
-Statistic _gridEnergy = Statistic(&dashboard, YASOLR_LBL_016);
-Statistic _gridEnergyReturned = Statistic(&dashboard, YASOLR_LBL_017);
-Statistic _gridFrequency = Statistic(&dashboard, YASOLR_LBL_018);
-
-Statistic _udpMessageRateBuffer = Statistic(&dashboard, YASOLR_LBL_157);
-
-Statistic _networkHostname = Statistic(&dashboard, YASOLR_LBL_019);
-Statistic _networkInterface = Statistic(&dashboard, YASOLR_LBL_020);
-Statistic _networkAPIP = Statistic(&dashboard, YASOLR_LBL_021);
-Statistic _networkAPMAC = Statistic(&dashboard, YASOLR_LBL_022);
-Statistic _networkEthIP = Statistic(&dashboard, YASOLR_LBL_023);
-Statistic _networkEthMAC = Statistic(&dashboard, YASOLR_LBL_024);
-Statistic _networkWiFiIP = Statistic(&dashboard, YASOLR_LBL_025);
-Statistic _networkWiFiMAC = Statistic(&dashboard, YASOLR_LBL_026);
-Statistic _networkWiFiSSID = Statistic(&dashboard, YASOLR_LBL_027);
-Statistic _networkWiFiRSSI = Statistic(&dashboard, YASOLR_LBL_028);
-Statistic _networkWiFiSignal = Statistic(&dashboard, YASOLR_LBL_029);
-
-Statistic _output1RelaySwitchCount = Statistic(&dashboard, YASOLR_LBL_030);
-Statistic _output2RelaySwitchCount = Statistic(&dashboard, YASOLR_LBL_031);
-Statistic _relay1SwitchCount = Statistic(&dashboard, YASOLR_LBL_032);
-Statistic _relay2SwitchCount = Statistic(&dashboard, YASOLR_LBL_033);
-
-Statistic _time = Statistic(&dashboard, YASOLR_LBL_034);
-Statistic _uptime = Statistic(&dashboard, YASOLR_LBL_035);
+dash::StatisticValue<const char*> _appName(dashboard, YASOLR_LBL_001);
+dash::StatisticValue<const char*> _appModel(dashboard, YASOLR_LBL_002);
+dash::StatisticValue<const char*> _appVersion(dashboard, YASOLR_LBL_003);
+dash::StatisticValue<const char*> _appManufacturer(dashboard, YASOLR_LBL_004);
+
+dash::StatisticValue<uint32_t> _deviceBootCount(dashboard, YASOLR_LBL_005);
+dash::StatisticValue<const char*> _deviceBootReason(dashboard, YASOLR_LBL_192);
+dash::StatisticValue<uint8_t> _deviceCores(dashboard, YASOLR_LBL_006);
+dash::StatisticValue<size_t> _deviceHeapTotal(dashboard, YASOLR_LBL_007);
+dash::StatisticValue<float, 2> _deviceHeapUsage(dashboard, YASOLR_LBL_008);
+dash::StatisticValue<size_t> _deviceHeapUsed(dashboard, YASOLR_LBL_009);
+dash::StatisticValue<const char*> _deviceID(dashboard, YASOLR_LBL_010);
+dash::StatisticValue<const char*> _deviceModel(dashboard, YASOLR_LBL_011);
+dash::StatisticValue<uint16_t> _deviceRev(dashboard, YASOLR_LBL_012);
+
+dash::StatisticValue<const char*> _firmwareBuildHash(dashboard, YASOLR_LBL_013);
+dash::StatisticValue<const char*> _firmwareBuildTimestamp(dashboard, YASOLR_LBL_014);
+dash::StatisticValue<const char*> _firmwareFilename(dashboard, YASOLR_LBL_015);
+
+dash::StatisticValue<float, 3> _gridEnergy(dashboard, YASOLR_LBL_016);
+dash::StatisticValue<float, 3> _gridEnergyReturned(dashboard, YASOLR_LBL_017);
+dash::StatisticValue<float, 0> _gridFrequency(dashboard, YASOLR_LBL_018);
+
+dash::StatisticValue<float, 2> _udpMessageRateBuffer(dashboard, YASOLR_LBL_157);
+
+dash::StatisticValue<const char*> _networkHostname(dashboard, YASOLR_LBL_019);
+dash::StatisticValue<const char*> _networkInterface(dashboard, YASOLR_LBL_020);
+dash::StatisticValue _networkAPIP(dashboard, YASOLR_LBL_021);
+dash::StatisticValue _networkAPMAC(dashboard, YASOLR_LBL_022);
+dash::StatisticValue _networkEthIP(dashboard, YASOLR_LBL_023);
+dash::StatisticValue _networkEthMAC(dashboard, YASOLR_LBL_024);
+dash::StatisticValue _networkWiFiIP(dashboard, YASOLR_LBL_025);
+dash::StatisticValue _networkWiFiMAC(dashboard, YASOLR_LBL_026);
+dash::StatisticValue _networkWiFiSSID(dashboard, YASOLR_LBL_027);
+dash::StatisticValue<int8_t> _networkWiFiRSSI(dashboard, YASOLR_LBL_028);
+dash::StatisticValue<int8_t> _networkWiFiSignal(dashboard, YASOLR_LBL_029);
+
+dash::StatisticValue<uint64_t> _output1RelaySwitchCount(dashboard, YASOLR_LBL_030);
+dash::StatisticValue<uint64_t> _output2RelaySwitchCount(dashboard, YASOLR_LBL_031);
+dash::StatisticValue<uint64_t> _relay1SwitchCount(dashboard, YASOLR_LBL_032);
+dash::StatisticValue<uint64_t> _relay2SwitchCount(dashboard, YASOLR_LBL_033);
+
+dash::StatisticValue _time(dashboard, YASOLR_LBL_034);
+dash::StatisticValue _uptime(dashboard, YASOLR_LBL_035);
 #ifdef APP_MODEL_TRIAL
-Statistic _trialRemainingTime = Statistic(&dashboard, "Trial Remaining Time");
+dash::StatisticValue _trialRemainingTime(dashboard, "Trial Remaining Time");
 #endif
 
 // home
 
-Card _routerPower = Card(&dashboard, ENERGY_CARD, YASOLR_LBL_036, "W");
-Card _routerApparentPower = Card(&dashboard, ENERGY_CARD, YASOLR_LBL_037, "VA");
-Card _routerPowerFactor = Card(&dashboard, ENERGY_CARD, YASOLR_LBL_038);
-Card _routerTHDi = Card(&dashboard, ENERGY_CARD, YASOLR_LBL_039, "%");
-Card _routerVoltage = Card(&dashboard, ENERGY_CARD, YASOLR_LBL_040, "V");
-Card _routerCurrent = Card(&dashboard, ENERGY_CARD, YASOLR_LBL_041, "A");
-Card _routerResistance = Card(&dashboard, ENERGY_CARD, YASOLR_LBL_042, "Ω");
-Card _routerEnergy = Card(&dashboard, ENERGY_CARD, YASOLR_LBL_043, "kWh");
-Card _gridPower = Card(&dashboard, ENERGY_CARD, YASOLR_LBL_044, "W");
-Card _routerDS18State = Card(&dashboard, TEMPERATURE_CARD, YASOLR_LBL_045, "°C");
+dash::EnergyCard<float, 0> _routerPower(dashboard, YASOLR_LBL_036, "W");
+dash::EnergyCard<float, 0> _routerApparentPower(dashboard, YASOLR_LBL_037, "VA");
+dash::EnergyCard<float, 2> _routerPowerFactor(dashboard, YASOLR_LBL_038);
+dash::EnergyCard<float, 2> _routerTHDi(dashboard, YASOLR_LBL_039, "%");
+dash::EnergyCard<float, 0> _routerVoltage(dashboard, YASOLR_LBL_040, "V");
+dash::EnergyCard<float, 0> _routerCurrent(dashboard, YASOLR_LBL_041, "A");
+dash::EnergyCard<float, 0> _routerResistance(dashboard, YASOLR_LBL_042, "Ω");
+dash::EnergyCard<float, 3> _routerEnergy(dashboard, YASOLR_LBL_043, "kWh");
+dash::EnergyCard<float, 0> _gridPower(dashboard, YASOLR_LBL_044, "W");
+dash::TemperatureCard<float, 2> _routerDS18State(dashboard, YASOLR_LBL_045);
+
 #ifdef APP_MODEL_OSS
-Card _relay1Switch = Card(&dashboard, BUTTON_CARD, YASOLR_LBL_073);
-Card _relay2Switch = Card(&dashboard, BUTTON_CARD, YASOLR_LBL_076);
-
-Card _output1State = Card(&dashboard, STATUS_CARD, YASOLR_LBL_046, DASH_STATUS_IDLE);
-Card _output1DS18State = Card(&dashboard, TEMPERATURE_CARD, YASOLR_LBL_046 ": " YASOLR_LBL_048, "°C");
-Card _output1DimmerSlider = Card(&dashboard, SLIDER_CARD, YASOLR_LBL_046 ": " YASOLR_LBL_050, "%", 0.0f, 100.0f, 0.01f);
-Card _output1Bypass = Card(&dashboard, BUTTON_CARD, YASOLR_LBL_046 ": " YASOLR_LBL_051);
-
-Card _output2State = Card(&dashboard, STATUS_CARD, YASOLR_LBL_070, DASH_STATUS_IDLE);
-Card _output2DS18State = Card(&dashboard, TEMPERATURE_CARD, YASOLR_LBL_070 ": " YASOLR_LBL_048, "°C");
-Card _output2DimmerSlider = Card(&dashboard, SLIDER_CARD, YASOLR_LBL_070 ": " YASOLR_LBL_050, "%", 0.0f, 100.0f, 0.01f);
-Card _output2Bypass = Card(&dashboard, BUTTON_CARD, YASOLR_LBL_070 ": " YASOLR_LBL_051);
+dash::SwitchCard _relay1Switch(dashboard, YASOLR_LBL_073);
+dash::SwitchCard _relay2Switch(dashboard, YASOLR_LBL_076);
+
+dash::FeedbackCard<const char*> _output1State(dashboard, YASOLR_LBL_046);
+dash::TemperatureCard<float, 2> _output1DS18State(dashboard, YASOLR_LBL_046 ": " YASOLR_LBL_048);
+dash::SliderCard<float, 2> _output1DimmerSlider(dashboard, YASOLR_LBL_046 ": " YASOLR_LBL_050, 0.0f, 100.0f, 0.01f, "%");
+dash::SwitchCard _output1Bypass(dashboard, YASOLR_LBL_046 ": " YASOLR_LBL_051);
+
+dash::FeedbackCard<const char*> _output2State(dashboard, YASOLR_LBL_070);
+dash::TemperatureCard<float, 2> _output2DS18State(dashboard, YASOLR_LBL_070 ": " YASOLR_LBL_048);
+dash::SliderCard<float, 2> _output2DimmerSlider(dashboard, YASOLR_LBL_070 ": " YASOLR_LBL_050, 0.0f, 100.0f, 0.01f, "%");
+dash::SwitchCard _output2Bypass(dashboard, YASOLR_LBL_070 ": " YASOLR_LBL_051);
 #endif
 
 int _gridPowerHistoryY[YASOLR_GRAPH_POINTS] = {0};
 int _routedPowerHistoryY[YASOLR_GRAPH_POINTS] = {0};
 int _routerTHDiHistoryY[YASOLR_GRAPH_POINTS] = {0};
-Chart _gridPowerHistory = Chart(&dashboard, LINE_CHART, YASOLR_LBL_044 " (W)");
-Chart _routedPowerHistory = Chart(&dashboard, AREA_CHART, YASOLR_LBL_036 " (W)");
-Chart _routerTHDiHistory = Chart(&dashboard, BAR_CHART, YASOLR_LBL_039 " (%)");
+dash::LineChart<int, int> _gridPowerHistory(dashboard, YASOLR_LBL_044 " (W)");
+dash::AreaChart<int, int> _routedPowerHistory(dashboard, YASOLR_LBL_036 " (W)");
+dash::BarChart<int, int> _routerTHDiHistory(dashboard, YASOLR_LBL_039 " (%)");
 
 #ifdef APP_MODEL_OSS
-Card _output1PZEMSync = Card(&dashboard, BUTTON_CARD, YASOLR_LBL_147);
-Card _output2PZEMSync = Card(&dashboard, BUTTON_CARD, YASOLR_LBL_148);
-Card _resistanceCalibration = Card(&dashboard, BUTTON_CARD, YASOLR_LBL_186);
+dash::SwitchCard _output1PZEMSync(dashboard, YASOLR_LBL_147);
+dash::SwitchCard _output2PZEMSync(dashboard, YASOLR_LBL_148);
+dash::SwitchCard _resistanceCalibration(dashboard, YASOLR_LBL_186);
 #endif
 
 #ifdef APP_MODEL_PRO
@@ -115,175 +114,175 @@ Card _resistanceCalibration = Card(&dashboard, BUTTON_CARD, YASOLR_LBL_186);
 // https://en.wikipedia.org/wiki/List_of_Unicode_characters#Miscellaneous_Symbols
 // https://en.wikipedia.org/wiki/List_of_Unicode_characters#Dingbats
 
-Tab _output1Tab = Tab(&dashboard, "\u2600 " YASOLR_LBL_046);
-Card _output1State = Card(&dashboard, STATUS_CARD, YASOLR_LBL_047, DASH_STATUS_IDLE);
-Card _output1DS18State = Card(&dashboard, TEMPERATURE_CARD, YASOLR_LBL_048, "°C");
-Card _output1DimmerSlider = Card(&dashboard, SLIDER_CARD, YASOLR_LBL_050, "%", 0.0f, 100.0f, 0.01f);
-Card _output1DimmerSliderRO = Card(&dashboard, PROGRESS_CARD, YASOLR_LBL_050, "%", 0.0f, 100.0f, 0.01f);
-Card _output1Bypass = Card(&dashboard, BUTTON_CARD, YASOLR_LBL_051);
-Card _output1BypassRO = Card(&dashboard, STATUS_CARD, YASOLR_LBL_051);
-Card _output1Power = Card(&dashboard, ENERGY_CARD, YASOLR_LBL_052, "W");
-Card _output1ApparentPower = Card(&dashboard, ENERGY_CARD, YASOLR_LBL_053, "VA");
-Card _output1PowerFactor = Card(&dashboard, ENERGY_CARD, YASOLR_LBL_054);
-Card _output1THDi = Card(&dashboard, ENERGY_CARD, YASOLR_LBL_055, "%");
-Card _output1Voltage = Card(&dashboard, ENERGY_CARD, YASOLR_LBL_056, "V");
-Card _output1Current = Card(&dashboard, ENERGY_CARD, YASOLR_LBL_057, "A");
-Card _output1Resistance = Card(&dashboard, ENERGY_CARD, YASOLR_LBL_058, "Ω");
-Card _output1Energy = Card(&dashboard, ENERGY_CARD, YASOLR_LBL_059, "kWh");
-Card _output1DimmerDutyLimiter = Card(&dashboard, SLIDER_CARD, YASOLR_LBL_062, "%", 0, 100, 1);
-Card _output1DimmerTempLimiter = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_063, "°C");
-Card _output1DimmerAuto = Card(&dashboard, BUTTON_CARD, YASOLR_LBL_060);
-Card _output1DimmerReservedExcess = Card(&dashboard, SLIDER_CARD, YASOLR_LBL_061, "%", 0, 100, 1);
-Card _output1BypassAuto = Card(&dashboard, BUTTON_CARD, YASOLR_LBL_064);
-Card _output1AutoStartTemp = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_065, "°C");
-Card _output1AutoStoptTemp = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_066, "°C");
-Card _output1AutoStartTime = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_067);
-Card _output1AutoStoptTime = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_068);
-Card _output1AutoStartWDays = Card(&dashboard, WEEK_SELECTOR_CARD, YASOLR_LBL_069);
-
-Tab _output2Tab = Tab(&dashboard, "\u2600 " YASOLR_LBL_070);
-Card _output2State = Card(&dashboard, STATUS_CARD, YASOLR_LBL_047, DASH_STATUS_IDLE);
-Card _output2DS18State = Card(&dashboard, TEMPERATURE_CARD, YASOLR_LBL_048, "°C");
-Card _output2DimmerSlider = Card(&dashboard, SLIDER_CARD, YASOLR_LBL_050, "%", 0.0f, 100.0f, 0.01f);
-Card _output2DimmerSliderRO = Card(&dashboard, PROGRESS_CARD, YASOLR_LBL_050, "%", 0.0f, 100.0f, 0.01f);
-Card _output2Bypass = Card(&dashboard, BUTTON_CARD, YASOLR_LBL_051);
-Card _output2BypassRO = Card(&dashboard, STATUS_CARD, YASOLR_LBL_051);
-Card _output2Power = Card(&dashboard, ENERGY_CARD, YASOLR_LBL_052, "W");
-Card _output2ApparentPower = Card(&dashboard, ENERGY_CARD, YASOLR_LBL_053, "VA");
-Card _output2PowerFactor = Card(&dashboard, ENERGY_CARD, YASOLR_LBL_054);
-Card _output2THDi = Card(&dashboard, ENERGY_CARD, YASOLR_LBL_055, "%");
-Card _output2Voltage = Card(&dashboard, ENERGY_CARD, YASOLR_LBL_056, "V");
-Card _output2Current = Card(&dashboard, ENERGY_CARD, YASOLR_LBL_057, "A");
-Card _output2Resistance = Card(&dashboard, ENERGY_CARD, YASOLR_LBL_058, "Ω");
-Card _output2Energy = Card(&dashboard, ENERGY_CARD, YASOLR_LBL_059, "kWh");
-Card _output2DimmerDutyLimiter = Card(&dashboard, SLIDER_CARD, YASOLR_LBL_062, "%", 0, 100, 1);
-Card _output2DimmerTempLimiter = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_063, "°C");
-Card _output2DimmerAuto = Card(&dashboard, BUTTON_CARD, YASOLR_LBL_060);
-Card _output2DimmerReservedExcess = Card(&dashboard, SLIDER_CARD, YASOLR_LBL_158, "%", 0, 100, 1);
-Card _output2BypassAuto = Card(&dashboard, BUTTON_CARD, YASOLR_LBL_064);
-Card _output2AutoStartTemp = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_065);
-Card _output2AutoStoptTemp = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_066);
-Card _output2AutoStartTime = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_067);
-Card _output2AutoStoptTime = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_068);
-Card _output2AutoStartWDays = Card(&dashboard, WEEK_SELECTOR_CARD, YASOLR_LBL_069);
-
-Tab _relaysTab = Tab(&dashboard, "\u2600 " YASOLR_LBL_071);
-Card _relay1Switch = Card(&dashboard, BUTTON_CARD, YASOLR_LBL_073);
-Card _relay1SwitchRO = Card(&dashboard, STATUS_CARD, YASOLR_LBL_074);
-Card _relay2Switch = Card(&dashboard, BUTTON_CARD, YASOLR_LBL_076);
-Card _relay2SwitchRO = Card(&dashboard, STATUS_CARD, YASOLR_LBL_077);
-
-Tab _managementTab = Tab(&dashboard, "\u2764 " YASOLR_LBL_078);
-Card _configBackup = Card(&dashboard, LINK_CARD, YASOLR_LBL_079);
-Card _configRestore = Card(&dashboard, FILE_UPLOAD_CARD, YASOLR_LBL_080, ".txt");
-Card _restart = Card(&dashboard, PUSH_BUTTON_CARD, YASOLR_LBL_082);
-Card _safeBoot = Card(&dashboard, PUSH_BUTTON_CARD, YASOLR_LBL_081);
-Card _energyReset = Card(&dashboard, PUSH_BUTTON_CARD, YASOLR_LBL_085);
-Card _reset = Card(&dashboard, PUSH_BUTTON_CARD, YASOLR_LBL_086);
-Card _debugMode = Card(&dashboard, BUTTON_CARD, YASOLR_LBL_083);
-Card _consoleLink = Card(&dashboard, LINK_CARD, YASOLR_LBL_084);
-Card _debugInfo = Card(&dashboard, LINK_CARD, YASOLR_LBL_178);
-
-Tab _networkConfigTab = Tab(&dashboard, "\u2728 " YASOLR_LBL_087);
-Card _adminPwd = Card(&dashboard, PASSWORD_CARD, YASOLR_LBL_088);
-Card _ntpServer = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_089);
-Card _ntpTimezone = Card(&dashboard, ASYNC_DROPDOWN_CARD, YASOLR_LBL_090);
-Card _ntpSync = Card(&dashboard, TIME_SYNC_CARD, YASOLR_LBL_091);
-Card _wifiSSID = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_092);
-Card _wifiPwd = Card(&dashboard, PASSWORD_CARD, YASOLR_LBL_093);
-Card _staticIP = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_188);
-Card _subnetMask = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_189);
-Card _gateway = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_190);
-Card _dnsServer = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_191);
-Card _apMode = Card(&dashboard, BUTTON_CARD, YASOLR_LBL_094);
-
-Tab _mqttConfigTab = Tab(&dashboard, "\u2728 " YASOLR_LBL_095);
-Card _mqttServer = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_096);
-Card _mqttPort = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_097);
-Card _mqttUser = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_098);
-Card _mqttPwd = Card(&dashboard, PASSWORD_CARD, YASOLR_LBL_099);
-Card _mqttSecured = Card(&dashboard, BUTTON_CARD, YASOLR_LBL_100);
-Card _mqttServerCert = Card(&dashboard, FILE_UPLOAD_CARD, YASOLR_LBL_101, ".pem");
-Card _mqttServerCertDelete = Card(&dashboard, PUSH_BUTTON_CARD, YASOLR_LBL_049);
-Card _mqttPublishInterval = Card(&dashboard, SLIDER_CARD, YASOLR_LBL_102, "s", 1, 30, 1);
-Card _mqttTopic = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_103);
-Card _haDiscovery = Card(&dashboard, BUTTON_CARD, YASOLR_LBL_104);
-Card _haDiscoveryTopic = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_105);
-Card _mqttGridVoltage = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_106);
-Card _mqttGridPower = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_107);
-Card _mqttTempO1 = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_181);
-Card _mqttTempO2 = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_182);
-
-Tab _pinConfigTab = Tab(&dashboard, "\u21C6 " YASOLR_LBL_108);
-Card _pinDisplayClock = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_111);
-Card _pinDisplayData = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_112);
-Card _pinJsyRX = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_116);
-Card _pinJsyTX = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_117);
-Card _pinLEDGreen = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_118);
-Card _pinLEDRed = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_119);
-Card _pinLEDYellow = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_120);
-Card _pinDimmerO1 = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_131);
-Card _pinDS18O1 = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_132);
-Card _pinRelayO1 = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_134);
-Card _pinDimmerO2 = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_135);
-Card _pinDS18O2 = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_136);
-Card _pinRelayO2 = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_138);
-Card _pinPZEMRX = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_121);
-Card _pinPZEMTX = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_122);
-Card _pinRelay1 = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_074);
-Card _pinRelay2 = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_077);
-Card _pinDS18Router = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_139);
-Card _pinZCD = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_125);
-
-Tab _hardwareEnableTab = Tab(&dashboard, "\u2699 " YASOLR_LBL_126);
-Card _display = Card(&dashboard, BUTTON_CARD, YASOLR_LBL_127);
-Card _jsy = Card(&dashboard, BUTTON_CARD, YASOLR_LBL_128);
-Card _led = Card(&dashboard, BUTTON_CARD, YASOLR_LBL_129);
-Card _mqtt = Card(&dashboard, BUTTON_CARD, YASOLR_LBL_095);
-Card _output1Dimmer = Card(&dashboard, BUTTON_CARD, YASOLR_LBL_131);
-Card _output1DS18 = Card(&dashboard, BUTTON_CARD, YASOLR_LBL_132);
-Card _output1PZEM = Card(&dashboard, BUTTON_CARD, YASOLR_LBL_133);
-Card _output1Relay = Card(&dashboard, BUTTON_CARD, YASOLR_LBL_134);
-Card _output2Dimmer = Card(&dashboard, BUTTON_CARD, YASOLR_LBL_135);
-Card _output2DS18 = Card(&dashboard, BUTTON_CARD, YASOLR_LBL_136);
-Card _output2PZEM = Card(&dashboard, BUTTON_CARD, YASOLR_LBL_137);
-Card _output2Relay = Card(&dashboard, BUTTON_CARD, YASOLR_LBL_138);
-Card _relay1 = Card(&dashboard, BUTTON_CARD, YASOLR_LBL_074);
-Card _relay2 = Card(&dashboard, BUTTON_CARD, YASOLR_LBL_077);
-Card _routerDS18 = Card(&dashboard, BUTTON_CARD, YASOLR_LBL_139);
-Card _zcd = Card(&dashboard, BUTTON_CARD, YASOLR_LBL_125);
-
-Tab _hardwareConfigTab = Tab(&dashboard, "\u2699 " YASOLR_LBL_140);
-Card _gridFreq = Card(&dashboard, DROPDOWN_CARD, YASOLR_LBL_141);
-Card _displaySpeed = Card(&dashboard, SLIDER_CARD, YASOLR_LBL_142, "s", 1, 10, 1);
-Card _displayType = Card(&dashboard, DROPDOWN_CARD, YASOLR_LBL_143);
-Card _displayRotation = Card(&dashboard, DROPDOWN_CARD, YASOLR_LBL_144);
-Card _relay1Type = Card(&dashboard, DROPDOWN_CARD, YASOLR_LBL_151);
-Card _relay2Type = Card(&dashboard, DROPDOWN_CARD, YASOLR_LBL_152);
-Card _relay1Load = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_072);
-Card _relay2Load = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_075);
-Card _output1RelayType = Card(&dashboard, DROPDOWN_CARD, YASOLR_LBL_149);
-Card _output2RelayType = Card(&dashboard, DROPDOWN_CARD, YASOLR_LBL_150);
-Card _output1DimmerMapper = Card(&dashboard, RANGE_SLIDER_CARD, YASOLR_LBL_183, "%", 0, 100, 1);
-Card _output2DimmerMapper = Card(&dashboard, RANGE_SLIDER_CARD, YASOLR_LBL_184, "%", 0, 100, 1);
-Card _output1PZEMSync = Card(&dashboard, BUTTON_CARD, YASOLR_LBL_147);
-Card _output2PZEMSync = Card(&dashboard, BUTTON_CARD, YASOLR_LBL_148);
-Card _output1ResistanceInput = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_145);
-Card _output2ResistanceInput = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_146);
-Card _resistanceCalibration = Card(&dashboard, BUTTON_CARD, YASOLR_LBL_186);
-
-Tab _pidTab = Tab(&dashboard, "\u2699 " YASOLR_LBL_159);
-Card _pidView = Card(&dashboard, BUTTON_CARD, YASOLR_LBL_169);
-Card _pidPMode = Card(&dashboard, DROPDOWN_CARD, YASOLR_LBL_160);
-Card _pidDMode = Card(&dashboard, DROPDOWN_CARD, YASOLR_LBL_161);
-Card _pidICMode = Card(&dashboard, DROPDOWN_CARD, YASOLR_LBL_162);
-Card _pidSetpoint = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_163);
-Card _pidKp = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_166);
-Card _pidKi = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_167);
-Card _pidKd = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_168);
-Card _pidOutMin = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_164);
-Card _pidOutMax = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_165);
-Card _pidReset = Card(&dashboard, PUSH_BUTTON_CARD, YASOLR_LBL_177);
+dash::Tab _output1Tab(dashboard, "\u2600 " YASOLR_LBL_046);
+dash::FeedbackCard<const char*> _output1State(dashboard, YASOLR_LBL_047);
+dash::TemperatureCard<float, 2> _output1DS18State(dashboard, YASOLR_LBL_048);
+dash::SliderCard<float, 2> _output1DimmerSlider(dashboard, YASOLR_LBL_050, 0.0f, 100.0f, 0.01f, "%");
+dash::ProgressCard<float, 2> _output1DimmerSliderRO(dashboard, YASOLR_LBL_050, 0.0f, 100.0f, "%");
+dash::SwitchCard _output1Bypass(dashboard, YASOLR_LBL_051);
+dash::FeedbackCard<const char*> _output1BypassRO(dashboard, YASOLR_LBL_051);
+dash::EnergyCard<float, 0> _output1Power(dashboard, YASOLR_LBL_052, "W");
+dash::EnergyCard<float, 0> _output1ApparentPower(dashboard, YASOLR_LBL_053, "VA");
+dash::EnergyCard<float, 2> _output1PowerFactor(dashboard, YASOLR_LBL_054);
+dash::EnergyCard<float, 2> _output1THDi(dashboard, YASOLR_LBL_055, "%");
+dash::EnergyCard<float, 0> _output1Voltage(dashboard, YASOLR_LBL_056, "V");
+dash::EnergyCard<float, 0> _output1Current(dashboard, YASOLR_LBL_057, "A");
+dash::EnergyCard<float, 0> _output1Resistance(dashboard, YASOLR_LBL_058, "Ω");
+dash::EnergyCard<float, 3> _output1Energy(dashboard, YASOLR_LBL_059, "kWh");
+dash::PercentageSliderCard _output1DimmerDutyLimiter(dashboard, YASOLR_LBL_062);
+dash::TextInputCard<uint8_t> _output1DimmerTempLimiter(dashboard, YASOLR_LBL_063);
+dash::SwitchCard _output1DimmerAuto(dashboard, YASOLR_LBL_060);
+dash::PercentageSliderCard _output1DimmerReservedExcess(dashboard, YASOLR_LBL_061);
+dash::SwitchCard _output1BypassAuto(dashboard, YASOLR_LBL_064);
+dash::TextInputCard<uint8_t> _output1AutoStartTemp(dashboard, YASOLR_LBL_065);
+dash::TextInputCard<uint8_t> _output1AutoStoptTemp(dashboard, YASOLR_LBL_066);
+dash::TextInputCard<const char*> _output1AutoStartTime(dashboard, YASOLR_LBL_067);
+dash::TextInputCard<const char*> _output1AutoStoptTime(dashboard, YASOLR_LBL_068);
+dash::WeekCard<const char*> _output1AutoStartWDays(dashboard, YASOLR_LBL_069);
+
+dash::Tab _output2Tab(dashboard, "\u2600 " YASOLR_LBL_070);
+dash::FeedbackCard<const char*> _output2State(dashboard, YASOLR_LBL_047);
+dash::TemperatureCard<float, 2> _output2DS18State(dashboard, YASOLR_LBL_048);
+dash::SliderCard<float, 2> _output2DimmerSlider(dashboard, YASOLR_LBL_050, 0.0f, 100.0f, 0.01f, "%");
+dash::ProgressCard<float, 2> _output2DimmerSliderRO(dashboard, YASOLR_LBL_050, 0.0f, 100.0f, "%");
+dash::SwitchCard _output2Bypass(dashboard, YASOLR_LBL_051);
+dash::FeedbackCard<const char*> _output2BypassRO(dashboard, YASOLR_LBL_051);
+dash::EnergyCard<float, 0> _output2Power(dashboard, YASOLR_LBL_052, "W");
+dash::EnergyCard<float, 0> _output2ApparentPower(dashboard, YASOLR_LBL_053, "VA");
+dash::EnergyCard<float, 2> _output2PowerFactor(dashboard, YASOLR_LBL_054);
+dash::EnergyCard<float, 2> _output2THDi(dashboard, YASOLR_LBL_055, "%");
+dash::EnergyCard<float, 0> _output2Voltage(dashboard, YASOLR_LBL_056, "V");
+dash::EnergyCard<float, 0> _output2Current(dashboard, YASOLR_LBL_057, "A");
+dash::EnergyCard<float, 0> _output2Resistance(dashboard, YASOLR_LBL_058, "Ω");
+dash::EnergyCard<float, 3> _output2Energy(dashboard, YASOLR_LBL_059, "kWh");
+dash::PercentageSliderCard _output2DimmerDutyLimiter(dashboard, YASOLR_LBL_062);
+dash::TextInputCard<uint8_t> _output2DimmerTempLimiter(dashboard, YASOLR_LBL_063);
+dash::SwitchCard _output2DimmerAuto(dashboard, YASOLR_LBL_060);
+dash::PercentageSliderCard _output2DimmerReservedExcess(dashboard, YASOLR_LBL_158);
+dash::SwitchCard _output2BypassAuto(dashboard, YASOLR_LBL_064);
+dash::TextInputCard<uint8_t> _output2AutoStartTemp(dashboard, YASOLR_LBL_065);
+dash::TextInputCard<uint8_t> _output2AutoStoptTemp(dashboard, YASOLR_LBL_066);
+dash::TextInputCard<const char*> _output2AutoStartTime(dashboard, YASOLR_LBL_067);
+dash::TextInputCard<const char*> _output2AutoStoptTime(dashboard, YASOLR_LBL_068);
+dash::WeekCard<const char*> _output2AutoStartWDays(dashboard, YASOLR_LBL_069);
+
+dash::Tab _relaysTab(dashboard, "\u2600 " YASOLR_LBL_071);
+dash::SwitchCard _relay1Switch(dashboard, YASOLR_LBL_073);
+dash::FeedbackCard<const char*> _relay1SwitchRO(dashboard, YASOLR_LBL_074);
+dash::SwitchCard _relay2Switch(dashboard, YASOLR_LBL_076);
+dash::FeedbackCard<const char*> _relay2SwitchRO(dashboard, YASOLR_LBL_077);
+
+dash::Tab _managementTab(dashboard, "\u2764 " YASOLR_LBL_078);
+dash::LinkCard<const char*> _configBackup(dashboard, YASOLR_LBL_079);
+dash::FileUploadCard<const char*> _configRestore(dashboard, YASOLR_LBL_080, ".txt");
+dash::PushButtonCard _restart(dashboard, YASOLR_LBL_082);
+dash::PushButtonCard _safeBoot(dashboard, YASOLR_LBL_081);
+dash::PushButtonCard _energyReset(dashboard, YASOLR_LBL_085);
+dash::PushButtonCard _reset(dashboard, YASOLR_LBL_086);
+dash::SwitchCard _debugMode(dashboard, YASOLR_LBL_083);
+dash::LinkCard<const char*> _consoleLink(dashboard, YASOLR_LBL_084);
+dash::LinkCard<const char*> _debugInfo(dashboard, YASOLR_LBL_178);
+
+dash::Tab _networkConfigTab(dashboard, "\u2728 " YASOLR_LBL_087);
+dash::PasswordCard _adminPwd(dashboard, YASOLR_LBL_088, YASOLR_HIDDEN_PWD);
+dash::TextInputCard<const char*> _ntpServer(dashboard, YASOLR_LBL_089);
+dash::AsyncDropdownCard<const char*> _ntpTimezone(dashboard, YASOLR_LBL_090, "/timezones");
+dash::TimeSyncCard _ntpSync(dashboard, YASOLR_LBL_091);
+dash::TextInputCard<const char*> _wifiSSID(dashboard, YASOLR_LBL_092);
+dash::PasswordCard _wifiPwd(dashboard, YASOLR_LBL_093, YASOLR_HIDDEN_PWD);
+dash::TextInputCard<const char*> _staticIP(dashboard, YASOLR_LBL_188);
+dash::TextInputCard<const char*> _subnetMask(dashboard, YASOLR_LBL_189);
+dash::TextInputCard<const char*> _gateway(dashboard, YASOLR_LBL_190);
+dash::TextInputCard<const char*> _dnsServer(dashboard, YASOLR_LBL_191);
+dash::SwitchCard _apMode(dashboard, YASOLR_LBL_094);
+
+dash::Tab _mqttConfigTab(dashboard, "\u2728 " YASOLR_LBL_095);
+dash::TextInputCard<const char*> _mqttServer(dashboard, YASOLR_LBL_096);
+dash::TextInputCard<uint16_t> _mqttPort(dashboard, YASOLR_LBL_097);
+dash::TextInputCard<const char*> _mqttUser(dashboard, YASOLR_LBL_098);
+dash::PasswordCard _mqttPwd(dashboard, YASOLR_LBL_099, YASOLR_HIDDEN_PWD);
+dash::SwitchCard _mqttSecured(dashboard, YASOLR_LBL_100);
+dash::FileUploadCard _mqttServerCert(dashboard, YASOLR_LBL_101, ".pem");
+dash::PushButtonCard _mqttServerCertDelete(dashboard, YASOLR_LBL_049);
+dash::SliderCard<uint8_t> _mqttPublishInterval(dashboard, YASOLR_LBL_102, 1, 30, 1, "s");
+dash::TextInputCard<const char*> _mqttTopic(dashboard, YASOLR_LBL_103);
+dash::SwitchCard _haDiscovery(dashboard, YASOLR_LBL_104);
+dash::TextInputCard<const char*> _haDiscoveryTopic(dashboard, YASOLR_LBL_105);
+dash::TextInputCard<const char*> _mqttGridVoltage(dashboard, YASOLR_LBL_106);
+dash::TextInputCard<const char*> _mqttGridPower(dashboard, YASOLR_LBL_107);
+dash::TextInputCard<const char*> _mqttTempO1(dashboard, YASOLR_LBL_181);
+dash::TextInputCard<const char*> _mqttTempO2(dashboard, YASOLR_LBL_182);
+
+dash::Tab _pinConfigTab(dashboard, "\u21C6 " YASOLR_LBL_108);
+dash::FeedbackTextInputCard<int32_t> _pinDisplayClock(dashboard, YASOLR_LBL_111);
+dash::FeedbackTextInputCard<int32_t> _pinDisplayData(dashboard, YASOLR_LBL_112);
+dash::FeedbackTextInputCard<int32_t> _pinJsyRX(dashboard, YASOLR_LBL_116);
+dash::FeedbackTextInputCard<int32_t> _pinJsyTX(dashboard, YASOLR_LBL_117);
+dash::FeedbackTextInputCard<int32_t> _pinLEDGreen(dashboard, YASOLR_LBL_118);
+dash::FeedbackTextInputCard<int32_t> _pinLEDRed(dashboard, YASOLR_LBL_119);
+dash::FeedbackTextInputCard<int32_t> _pinLEDYellow(dashboard, YASOLR_LBL_120);
+dash::FeedbackTextInputCard<int32_t> _pinDimmerO1(dashboard, YASOLR_LBL_131);
+dash::FeedbackTextInputCard<int32_t> _pinDS18O1(dashboard, YASOLR_LBL_132);
+dash::FeedbackTextInputCard<int32_t> _pinRelayO1(dashboard, YASOLR_LBL_134);
+dash::FeedbackTextInputCard<int32_t> _pinDimmerO2(dashboard, YASOLR_LBL_135);
+dash::FeedbackTextInputCard<int32_t> _pinDS18O2(dashboard, YASOLR_LBL_136);
+dash::FeedbackTextInputCard<int32_t> _pinRelayO2(dashboard, YASOLR_LBL_138);
+dash::FeedbackTextInputCard<int32_t> _pinPZEMRX(dashboard, YASOLR_LBL_121);
+dash::FeedbackTextInputCard<int32_t> _pinPZEMTX(dashboard, YASOLR_LBL_122);
+dash::FeedbackTextInputCard<int32_t> _pinRelay1(dashboard, YASOLR_LBL_074);
+dash::FeedbackTextInputCard<int32_t> _pinRelay2(dashboard, YASOLR_LBL_077);
+dash::FeedbackTextInputCard<int32_t> _pinDS18Router(dashboard, YASOLR_LBL_139);
+dash::FeedbackTextInputCard<int32_t> _pinZCD(dashboard, YASOLR_LBL_125);
+
+dash::Tab _hardwareEnableTab(dashboard, "\u2699 " YASOLR_LBL_126);
+dash::FeedbackSwitchCard _display(dashboard, YASOLR_LBL_127);
+dash::FeedbackSwitchCard _jsy(dashboard, YASOLR_LBL_128);
+dash::FeedbackSwitchCard _led(dashboard, YASOLR_LBL_129);
+dash::FeedbackSwitchCard _mqtt(dashboard, YASOLR_LBL_095);
+dash::FeedbackSwitchCard _output1Dimmer(dashboard, YASOLR_LBL_131);
+dash::FeedbackSwitchCard _output1DS18(dashboard, YASOLR_LBL_132);
+dash::FeedbackSwitchCard _output1PZEM(dashboard, YASOLR_LBL_133);
+dash::FeedbackSwitchCard _output1Relay(dashboard, YASOLR_LBL_134);
+dash::FeedbackSwitchCard _output2Dimmer(dashboard, YASOLR_LBL_135);
+dash::FeedbackSwitchCard _output2DS18(dashboard, YASOLR_LBL_136);
+dash::FeedbackSwitchCard _output2PZEM(dashboard, YASOLR_LBL_137);
+dash::FeedbackSwitchCard _output2Relay(dashboard, YASOLR_LBL_138);
+dash::FeedbackSwitchCard _relay1(dashboard, YASOLR_LBL_074);
+dash::FeedbackSwitchCard _relay2(dashboard, YASOLR_LBL_077);
+dash::FeedbackSwitchCard _routerDS18(dashboard, YASOLR_LBL_139);
+dash::FeedbackSwitchCard _zcd(dashboard, YASOLR_LBL_125);
+
+dash::Tab _hardwareConfigTab(dashboard, "\u2699 " YASOLR_LBL_140);
+dash::DropdownCard<const char*> _gridFreq(dashboard, YASOLR_LBL_141, "Auto-detect,50 Hz,60 Hz");
+dash::SliderCard<uint8_t> _displaySpeed(dashboard, YASOLR_LBL_142, 1, 10, 1, "s");
+dash::DropdownCard<const char*> _displayType(dashboard, YASOLR_LBL_143, "SH1106,SH1107,SSD1306");
+dash::DropdownCard<uint16_t> _displayRotation(dashboard, YASOLR_LBL_144, "0,90,180,270");
+dash::DropdownCard<const char*> _relay1Type(dashboard, YASOLR_LBL_151, "NO,NC");
+dash::DropdownCard<const char*> _relay2Type(dashboard, YASOLR_LBL_152, "NO,NC");
+dash::TextInputCard<uint16_t> _relay1Load(dashboard, YASOLR_LBL_072);
+dash::TextInputCard<uint16_t> _relay2Load(dashboard, YASOLR_LBL_075);
+dash::DropdownCard<const char*> _output1RelayType(dashboard, YASOLR_LBL_149, "NO,NC");
+dash::DropdownCard<const char*> _output2RelayType(dashboard, YASOLR_LBL_150, "NO,NC");
+dash::RangeSliderCard<uint8_t> _output1DimmerMapper(dashboard, YASOLR_LBL_183, 0, 100, 1, "%");
+dash::RangeSliderCard<uint8_t> _output2DimmerMapper(dashboard, YASOLR_LBL_184, 0, 100, 1, "%");
+dash::SwitchCard _output1PZEMSync(dashboard, YASOLR_LBL_147);
+dash::SwitchCard _output2PZEMSync(dashboard, YASOLR_LBL_148);
+dash::FeedbackTextInputCard<float, 2> _output1ResistanceInput(dashboard, YASOLR_LBL_145);
+dash::FeedbackTextInputCard<float, 2> _output2ResistanceInput(dashboard, YASOLR_LBL_146);
+dash::SwitchCard _resistanceCalibration(dashboard, YASOLR_LBL_186);
+
+dash::Tab _pidTab(dashboard, "\u2699 " YASOLR_LBL_159);
+dash::SwitchCard _pidView(dashboard, YASOLR_LBL_169);
+dash::DropdownCard<const char*> _pidPMode(dashboard, YASOLR_LBL_160, YASOLR_PID_P_MODE_1 "," YASOLR_PID_P_MODE_2 "," YASOLR_PID_P_MODE_3);
+dash::DropdownCard<const char*> _pidDMode(dashboard, YASOLR_LBL_161, YASOLR_PID_D_MODE_1 "," YASOLR_PID_D_MODE_2);
+dash::DropdownCard<const char*> _pidICMode(dashboard, YASOLR_LBL_162, YASOLR_PID_IC_MODE_0 "," YASOLR_PID_IC_MODE_1 "," YASOLR_PID_IC_MODE_2);
+dash::TextInputCard<int> _pidSetpoint(dashboard, YASOLR_LBL_163);
+dash::TextInputCard<float, 4> _pidKp(dashboard, YASOLR_LBL_166);
+dash::TextInputCard<float, 4> _pidKi(dashboard, YASOLR_LBL_167);
+dash::TextInputCard<float, 4> _pidKd(dashboard, YASOLR_LBL_168);
+dash::TextInputCard<int> _pidOutMin(dashboard, YASOLR_LBL_164);
+dash::TextInputCard<int> _pidOutMax(dashboard, YASOLR_LBL_165);
+dash::PushButtonCard _pidReset(dashboard, YASOLR_LBL_177);
 
 // input,output,error,pTerm,iTerm,dTerm,sum
 int _pidInputHistoryY[YASOLR_GRAPH_POINTS] = {0};
@@ -293,13 +292,13 @@ int _pidPTermHistoryY[YASOLR_GRAPH_POINTS] = {0};
 int _pidITermHistoryY[YASOLR_GRAPH_POINTS] = {0};
 int _pidDTermHistoryY[YASOLR_GRAPH_POINTS] = {0};
 int _pidSumHistoryY[YASOLR_GRAPH_POINTS] = {0};
-Chart _pidInputHistory = Chart(&dashboard, LINE_CHART, YASOLR_LBL_170);
-Chart _pidOutputHistory = Chart(&dashboard, LINE_CHART, YASOLR_LBL_171);
-Chart _pidErrorHistory = Chart(&dashboard, LINE_CHART, YASOLR_LBL_172);
-Chart _pidSumHistory = Chart(&dashboard, BAR_CHART, YASOLR_LBL_173);
-Chart _pidPTermHistory = Chart(&dashboard, LINE_CHART, YASOLR_LBL_174);
-Chart _pidITermHistory = Chart(&dashboard, LINE_CHART, YASOLR_LBL_175);
-Chart _pidDTermHistory = Chart(&dashboard, LINE_CHART, YASOLR_LBL_176);
+dash::LineChart<int, int> _pidInputHistory(dashboard, YASOLR_LBL_170);
+dash::LineChart<int, int> _pidOutputHistory(dashboard, YASOLR_LBL_171);
+dash::LineChart<int, int> _pidErrorHistory(dashboard, YASOLR_LBL_172);
+dash::BarChart<int, int> _pidSumHistory(dashboard, YASOLR_LBL_173);
+dash::LineChart<int, int> _pidPTermHistory(dashboard, YASOLR_LBL_174);
+dash::LineChart<int, int> _pidITermHistory(dashboard, YASOLR_LBL_175);
+dash::LineChart<int, int> _pidDTermHistory(dashboard, YASOLR_LBL_176);
 #endif
 
 void YaSolR::Website::initLayout() {
@@ -310,9 +309,9 @@ void YaSolR::Website::initLayout() {
 
   // overview
 
-  _gridPowerHistory.updateX(_historyX, YASOLR_GRAPH_POINTS);
-  _routedPowerHistory.updateX(_historyX, YASOLR_GRAPH_POINTS);
-  _routerTHDiHistory.updateX(_historyX, YASOLR_GRAPH_POINTS);
+  _gridPowerHistory.setX(_historyX, YASOLR_GRAPH_POINTS);
+  _routedPowerHistory.setX(_historyX, YASOLR_GRAPH_POINTS);
+  _routerTHDiHistory.setX(_historyX, YASOLR_GRAPH_POINTS);
 
   _outputBypassSwitch(_output1Bypass, output1);
   _outputDimmerSlider(_output1DimmerSlider, output1);
@@ -323,19 +322,19 @@ void YaSolR::Website::initLayout() {
   _relaySwitch(_relay1Switch, routerRelay1);
   _relaySwitch(_relay2Switch, routerRelay2);
 
-  _output1PZEMSync.attachCallback([this](int32_t value) {
+  _output1PZEMSync.onChange([](bool value) {
     pzemO1PairingTask.resume();
-    _output1PZEMSync.update(!pzemO1PairingTask.isPaused());
-    dashboard.refreshCard(&_output1PZEMSync);
+    _output1PZEMSync.setValue(!pzemO1PairingTask.isPaused());
+    dashboard.refresh(_output1PZEMSync);
   });
 
-  _output2PZEMSync.attachCallback([this](int32_t value) {
+  _output2PZEMSync.onChange([](bool value) {
     pzemO2PairingTask.resume();
-    _output2PZEMSync.update(!pzemO2PairingTask.isPaused());
-    dashboard.refreshCard(&_output2PZEMSync);
+    _output2PZEMSync.setValue(!pzemO2PairingTask.isPaused());
+    dashboard.refresh(_output2PZEMSync);
   });
 
-  _resistanceCalibration.attachCallback([this](int32_t value) {
+  _resistanceCalibration.onChange([this](bool value) {
     config.set(KEY_ENABLE_OUTPUT1_AUTO_BYPASS, YASOLR_FALSE, false);
     config.set(KEY_ENABLE_OUTPUT1_AUTO_DIMMER, YASOLR_FALSE, false);
     config.set(KEY_OUTPUT1_DIMMER_LIMIT, "100", false);
@@ -352,8 +351,8 @@ void YaSolR::Website::initLayout() {
     mqttPublishConfigTask.resume();
     mqttPublishTask.requestEarlyRun();
 
-    _resistanceCalibration.update(router.isCalibrationRunning());
-    dashboard.refreshCard(&_resistanceCalibration);
+    _resistanceCalibration.setValue(router.isCalibrationRunning());
+    dashboard.refresh(_resistanceCalibration);
   });
 
 #ifdef APP_MODEL_PRO
@@ -367,13 +366,13 @@ void YaSolR::Website::initLayout() {
 
   // PID
 
-  _pidInputHistory.updateX(_historyX, YASOLR_GRAPH_POINTS);
-  _pidOutputHistory.updateX(_historyX, YASOLR_GRAPH_POINTS);
-  _pidErrorHistory.updateX(_historyX, YASOLR_GRAPH_POINTS);
-  _pidPTermHistory.updateX(_historyX, YASOLR_GRAPH_POINTS);
-  _pidITermHistory.updateX(_historyX, YASOLR_GRAPH_POINTS);
-  _pidDTermHistory.updateX(_historyX, YASOLR_GRAPH_POINTS);
-  _pidSumHistory.updateX(_historyX, YASOLR_GRAPH_POINTS);
+  _pidInputHistory.setX(_historyX, YASOLR_GRAPH_POINTS);
+  _pidOutputHistory.setX(_historyX, YASOLR_GRAPH_POINTS);
+  _pidErrorHistory.setX(_historyX, YASOLR_GRAPH_POINTS);
+  _pidPTermHistory.setX(_historyX, YASOLR_GRAPH_POINTS);
+  _pidITermHistory.setX(_historyX, YASOLR_GRAPH_POINTS);
+  _pidDTermHistory.setX(_historyX, YASOLR_GRAPH_POINTS);
+  _pidSumHistory.setX(_historyX, YASOLR_GRAPH_POINTS);
 
   _pidInputHistory.setSize(chartSize);
   _pidOutputHistory.setSize(chartSize);
@@ -385,33 +384,33 @@ void YaSolR::Website::initLayout() {
 
   // output 1
 
-  _output1State.setTab(&_output1Tab);
-  _output1DS18State.setTab(&_output1Tab);
-  _output1DimmerSlider.setTab(&_output1Tab);
-  _output1DimmerSliderRO.setTab(&_output1Tab);
-  _output1Bypass.setTab(&_output1Tab);
-  _output1BypassRO.setTab(&_output1Tab);
-
-  _output1Power.setTab(&_output1Tab);
-  _output1PowerFactor.setTab(&_output1Tab);
-  _output1THDi.setTab(&_output1Tab);
-  _output1Energy.setTab(&_output1Tab);
-
-  _output1ApparentPower.setTab(&_output1Tab);
-  _output1Voltage.setTab(&_output1Tab);
-  _output1Current.setTab(&_output1Tab);
-  _output1Resistance.setTab(&_output1Tab);
-
-  _output1BypassAuto.setTab(&_output1Tab);
-  _output1DimmerAuto.setTab(&_output1Tab);
-  _output1AutoStartTemp.setTab(&_output1Tab);
-  _output1AutoStartTime.setTab(&_output1Tab);
-  _output1AutoStartWDays.setTab(&_output1Tab);
-  _output1AutoStoptTemp.setTab(&_output1Tab);
-  _output1AutoStoptTime.setTab(&_output1Tab);
-  _output1DimmerReservedExcess.setTab(&_output1Tab);
-  _output1DimmerDutyLimiter.setTab(&_output1Tab);
-  _output1DimmerTempLimiter.setTab(&_output1Tab);
+  _output1State.setTab(_output1Tab);
+  _output1DS18State.setTab(_output1Tab);
+  _output1DimmerSlider.setTab(_output1Tab);
+  _output1DimmerSliderRO.setTab(_output1Tab);
+  _output1Bypass.setTab(_output1Tab);
+  _output1BypassRO.setTab(_output1Tab);
+
+  _output1Power.setTab(_output1Tab);
+  _output1PowerFactor.setTab(_output1Tab);
+  _output1THDi.setTab(_output1Tab);
+  _output1Energy.setTab(_output1Tab);
+
+  _output1ApparentPower.setTab(_output1Tab);
+  _output1Voltage.setTab(_output1Tab);
+  _output1Current.setTab(_output1Tab);
+  _output1Resistance.setTab(_output1Tab);
+
+  _output1BypassAuto.setTab(_output1Tab);
+  _output1DimmerAuto.setTab(_output1Tab);
+  _output1AutoStartTemp.setTab(_output1Tab);
+  _output1AutoStartTime.setTab(_output1Tab);
+  _output1AutoStartWDays.setTab(_output1Tab);
+  _output1AutoStoptTemp.setTab(_output1Tab);
+  _output1AutoStoptTime.setTab(_output1Tab);
+  _output1DimmerReservedExcess.setTab(_output1Tab);
+  _output1DimmerDutyLimiter.setTab(_output1Tab);
+  _output1DimmerTempLimiter.setTab(_output1Tab);
 
   _boolConfig(_output1BypassAuto, KEY_ENABLE_OUTPUT1_AUTO_BYPASS);
   _boolConfig(_output1DimmerAuto, KEY_ENABLE_OUTPUT1_AUTO_DIMMER);
@@ -419,40 +418,40 @@ void YaSolR::Website::initLayout() {
   _numConfig(_output1AutoStartTemp, KEY_OUTPUT1_TEMPERATURE_START);
   _numConfig(_output1AutoStoptTemp, KEY_OUTPUT1_TEMPERATURE_STOP);
   _numConfig(_output1DimmerTempLimiter, KEY_OUTPUT1_DIMMER_STOP_TEMP);
-  _percentageSlider(_output1DimmerDutyLimiter, KEY_OUTPUT1_DIMMER_LIMIT);
-  _percentageSlider(_output1DimmerReservedExcess, KEY_OUTPUT1_RESERVED_EXCESS);
+  _sliderConfig(_output1DimmerDutyLimiter, KEY_OUTPUT1_DIMMER_LIMIT);
+  _sliderConfig(_output1DimmerReservedExcess, KEY_OUTPUT1_RESERVED_EXCESS);
   _textConfig(_output1AutoStartTime, KEY_OUTPUT1_TIME_START);
   _textConfig(_output1AutoStoptTime, KEY_OUTPUT1_TIME_STOP);
 
   // output 2
 
-  _output2State.setTab(&_output2Tab);
-  _output2DS18State.setTab(&_output2Tab);
-  _output2DimmerSlider.setTab(&_output2Tab);
-  _output2DimmerSliderRO.setTab(&_output2Tab);
-  _output2Bypass.setTab(&_output2Tab);
-  _output2BypassRO.setTab(&_output2Tab);
-
-  _output2Power.setTab(&_output2Tab);
-  _output2PowerFactor.setTab(&_output2Tab);
-  _output2THDi.setTab(&_output2Tab);
-  _output2Energy.setTab(&_output2Tab);
-
-  _output2ApparentPower.setTab(&_output2Tab);
-  _output2Voltage.setTab(&_output2Tab);
-  _output2Current.setTab(&_output2Tab);
-  _output2Resistance.setTab(&_output2Tab);
-
-  _output2BypassAuto.setTab(&_output2Tab);
-  _output2DimmerAuto.setTab(&_output2Tab);
-  _output2AutoStartTemp.setTab(&_output2Tab);
-  _output2AutoStartTime.setTab(&_output2Tab);
-  _output2AutoStartWDays.setTab(&_output2Tab);
-  _output2AutoStoptTemp.setTab(&_output2Tab);
-  _output2AutoStoptTime.setTab(&_output2Tab);
-  _output2DimmerReservedExcess.setTab(&_output2Tab);
-  _output2DimmerDutyLimiter.setTab(&_output2Tab);
-  _output2DimmerTempLimiter.setTab(&_output2Tab);
+  _output2State.setTab(_output2Tab);
+  _output2DS18State.setTab(_output2Tab);
+  _output2DimmerSlider.setTab(_output2Tab);
+  _output2DimmerSliderRO.setTab(_output2Tab);
+  _output2Bypass.setTab(_output2Tab);
+  _output2BypassRO.setTab(_output2Tab);
+
+  _output2Power.setTab(_output2Tab);
+  _output2PowerFactor.setTab(_output2Tab);
+  _output2THDi.setTab(_output2Tab);
+  _output2Energy.setTab(_output2Tab);
+
+  _output2ApparentPower.setTab(_output2Tab);
+  _output2Voltage.setTab(_output2Tab);
+  _output2Current.setTab(_output2Tab);
+  _output2Resistance.setTab(_output2Tab);
+
+  _output2BypassAuto.setTab(_output2Tab);
+  _output2DimmerAuto.setTab(_output2Tab);
+  _output2AutoStartTemp.setTab(_output2Tab);
+  _output2AutoStartTime.setTab(_output2Tab);
+  _output2AutoStartWDays.setTab(_output2Tab);
+  _output2AutoStoptTemp.setTab(_output2Tab);
+  _output2AutoStoptTime.setTab(_output2Tab);
+  _output2DimmerReservedExcess.setTab(_output2Tab);
+  _output2DimmerDutyLimiter.setTab(_output2Tab);
+  _output2DimmerTempLimiter.setTab(_output2Tab);
 
   _boolConfig(_output2BypassAuto, KEY_ENABLE_OUTPUT2_AUTO_BYPASS);
   _boolConfig(_output2DimmerAuto, KEY_ENABLE_OUTPUT2_AUTO_DIMMER);
@@ -460,91 +459,92 @@ void YaSolR::Website::initLayout() {
   _numConfig(_output2AutoStartTemp, KEY_OUTPUT2_TEMPERATURE_START);
   _numConfig(_output2AutoStoptTemp, KEY_OUTPUT2_TEMPERATURE_STOP);
   _numConfig(_output2DimmerTempLimiter, KEY_OUTPUT2_DIMMER_STOP_TEMP);
-  _percentageSlider(_output2DimmerDutyLimiter, KEY_OUTPUT2_DIMMER_LIMIT);
-  _percentageSlider(_output2DimmerReservedExcess, KEY_OUTPUT2_RESERVED_EXCESS);
+  _sliderConfig(_output2DimmerDutyLimiter, KEY_OUTPUT2_DIMMER_LIMIT);
+  _sliderConfig(_output2DimmerReservedExcess, KEY_OUTPUT2_RESERVED_EXCESS);
   _textConfig(_output2AutoStartTime, KEY_OUTPUT2_TIME_START);
   _textConfig(_output2AutoStoptTime, KEY_OUTPUT2_TIME_STOP);
 
   // relays
 
-  _relay1Switch.setTab(&_relaysTab);
-  _relay1SwitchRO.setTab(&_relaysTab);
-  _relay2Switch.setTab(&_relaysTab);
-  _relay2SwitchRO.setTab(&_relaysTab);
+  _relay1Switch.setTab(_relaysTab);
+  _relay1SwitchRO.setTab(_relaysTab);
+  _relay2Switch.setTab(_relaysTab);
+  _relay2SwitchRO.setTab(_relaysTab);
 
   // management
 
-  _configBackup.setTab(&_managementTab);
-  _configRestore.setTab(&_managementTab);
-  _consoleLink.setTab(&_managementTab);
-  _debugInfo.setTab(&_managementTab);
-  _debugMode.setTab(&_managementTab);
-  _safeBoot.setTab(&_managementTab);
-  _reset.setTab(&_managementTab);
-  _restart.setTab(&_managementTab);
-  _energyReset.setTab(&_managementTab);
+  _configBackup.setTab(_managementTab);
+  _configRestore.setTab(_managementTab);
+  _consoleLink.setTab(_managementTab);
+  _debugInfo.setTab(_managementTab);
+  _debugMode.setTab(_managementTab);
+  _safeBoot.setTab(_managementTab);
+  _reset.setTab(_managementTab);
+  _restart.setTab(_managementTab);
+  _energyReset.setTab(_managementTab);
 
   _boolConfig(_debugMode, KEY_ENABLE_DEBUG);
 
-  _energyReset.attachCallback([]() {
+  _energyReset.onPush([]() {
     jsy.resetEnergy();
     pzemO1.resetEnergy();
     pzemO2.resetEnergy();
   });
-  _reset.attachCallback([]() { resetTask.resume(); });
-  _restart.attachCallback([]() { restartTask.resume(); });
-  _safeBoot.attachCallback([]() { safeBootTask.resume(); });
+  _reset.onPush([]() { resetTask.resume(); });
+  _restart.onPush([]() { restartTask.resume(); });
+  _safeBoot.onPush([]() { safeBootTask.resume(); });
 
   // network (config)
 
-  _adminPwd.setTab(&_networkConfigTab);
-  _apMode.setTab(&_networkConfigTab);
-  _ntpServer.setTab(&_networkConfigTab);
-  _ntpSync.setTab(&_networkConfigTab);
-  _ntpTimezone.setTab(&_networkConfigTab);
-  _wifiPwd.setTab(&_networkConfigTab);
-  _wifiSSID.setTab(&_networkConfigTab);
-  _staticIP.setTab(&_networkConfigTab);
-  _subnetMask.setTab(&_networkConfigTab);
-  _gateway.setTab(&_networkConfigTab);
-  _dnsServer.setTab(&_networkConfigTab);
+  _adminPwd.setTab(_networkConfigTab);
+  _apMode.setTab(_networkConfigTab);
+  _ntpServer.setTab(_networkConfigTab);
+  _ntpSync.setTab(_networkConfigTab);
+  _ntpTimezone.setTab(_networkConfigTab);
+  _wifiPwd.setTab(_networkConfigTab);
+  _wifiSSID.setTab(_networkConfigTab);
+  _staticIP.setTab(_networkConfigTab);
+  _subnetMask.setTab(_networkConfigTab);
+  _gateway.setTab(_networkConfigTab);
+  _dnsServer.setTab(_networkConfigTab);
+
+  _ntpTimezone.onChange([](const char* value) {
+    config.set(KEY_NTP_TIMEZONE, value);
+    _ntpTimezone.setValue(value);
+    dashboard.refresh(_ntpTimezone);
+  });
 
   _boolConfig(_apMode, KEY_ENABLE_AP_MODE);
   _passwordConfig(_adminPwd, KEY_ADMIN_PASSWORD);
   _passwordConfig(_wifiPwd, KEY_WIFI_PASSWORD);
   _textConfig(_ntpServer, KEY_NTP_SERVER);
-  _textConfig(_ntpTimezone, KEY_NTP_TIMEZONE);
   _textConfig(_wifiSSID, KEY_WIFI_SSID);
   _textConfig(_staticIP, KEY_NET_IP);
   _textConfig(_subnetMask, KEY_NET_SUBNET);
   _textConfig(_gateway, KEY_NET_GATEWAY);
   _textConfig(_dnsServer, KEY_NET_DNS);
 
-  _ntpSync.attachCallback([](const char* value) {
-    const size_t len = strlen(value);
-    timeval tv;
-    tv.tv_sec = std::stoul(std::string(value, len - 3));
-    tv.tv_usec = atol(value + len - 3);
+  _ntpSync.onSync([](const timeval& tv) {
     Mycila::NTP.sync(tv);
   });
 
   // mqtt (config)
 
-  _haDiscovery.setTab(&_mqttConfigTab);
-  _haDiscoveryTopic.setTab(&_mqttConfigTab);
-  _mqttGridPower.setTab(&_mqttConfigTab);
-  _mqttGridVoltage.setTab(&_mqttConfigTab);
-  _mqttPort.setTab(&_mqttConfigTab);
-  _mqttPublishInterval.setTab(&_mqttConfigTab);
-  _mqttPwd.setTab(&_mqttConfigTab);
-  _mqttSecured.setTab(&_mqttConfigTab);
-  _mqttServer.setTab(&_mqttConfigTab);
-  _mqttServerCert.setTab(&_mqttConfigTab);
-  _mqttServerCertDelete.setTab(&_mqttConfigTab);
-  _mqttTopic.setTab(&_mqttConfigTab);
-  _mqttUser.setTab(&_mqttConfigTab);
-  _mqttTempO1.setTab(&_mqttConfigTab);
-  _mqttTempO2.setTab(&_mqttConfigTab);
+  _haDiscovery.setTab(_mqttConfigTab);
+  _haDiscoveryTopic.setTab(_mqttConfigTab);
+  _mqttGridPower.setTab(_mqttConfigTab);
+  _mqttGridVoltage.setTab(_mqttConfigTab);
+  _mqttPort.setTab(_mqttConfigTab);
+  _mqttPublishInterval.setTab(_mqttConfigTab);
+  _mqttPwd.setTab(_mqttConfigTab);
+  _mqttSecured.setTab(_mqttConfigTab);
+  _mqttServer.setTab(_mqttConfigTab);
+  _mqttServerCert.setTab(_mqttConfigTab);
+  _mqttServerCertDelete.setTab(_mqttConfigTab);
+  _mqttTopic.setTab(_mqttConfigTab);
+  _mqttUser.setTab(_mqttConfigTab);
+  _mqttTempO1.setTab(_mqttConfigTab);
+  _mqttTempO2.setTab(_mqttConfigTab);
 
   _boolConfig(_haDiscovery, KEY_ENABLE_HA_DISCOVERY);
   _boolConfig(_mqttSecured, KEY_MQTT_SECURED);
@@ -560,7 +560,7 @@ void YaSolR::Website::initLayout() {
   _textConfig(_mqttTopic, KEY_MQTT_TOPIC);
   _textConfig(_mqttUser, KEY_MQTT_USERNAME);
 
-  _mqttServerCertDelete.attachCallback([this]() {
+  _mqttServerCertDelete.onPush([this]() {
     if (LittleFS.exists(YASOLR_MQTT_SERVER_CERT_FILE) && LittleFS.remove(YASOLR_MQTT_SERVER_CERT_FILE)) {
       logger.warn(TAG, "MQTT server certificate deleted successfully!");
       dashboardInitTask.resume();
@@ -569,25 +569,25 @@ void YaSolR::Website::initLayout() {
 
   // GPIO (configuration)
 
-  _pinDimmerO1.setTab(&_pinConfigTab);
-  _pinDimmerO2.setTab(&_pinConfigTab);
-  _pinDisplayClock.setTab(&_pinConfigTab);
-  _pinDisplayData.setTab(&_pinConfigTab);
-  _pinDS18O1.setTab(&_pinConfigTab);
-  _pinDS18O2.setTab(&_pinConfigTab);
-  _pinDS18Router.setTab(&_pinConfigTab);
-  _pinJsyRX.setTab(&_pinConfigTab);
-  _pinJsyTX.setTab(&_pinConfigTab);
-  _pinLEDGreen.setTab(&_pinConfigTab);
-  _pinLEDRed.setTab(&_pinConfigTab);
-  _pinLEDYellow.setTab(&_pinConfigTab);
-  _pinPZEMRX.setTab(&_pinConfigTab);
-  _pinPZEMTX.setTab(&_pinConfigTab);
-  _pinRelay1.setTab(&_pinConfigTab);
-  _pinRelay2.setTab(&_pinConfigTab);
-  _pinRelayO1.setTab(&_pinConfigTab);
-  _pinRelayO2.setTab(&_pinConfigTab);
-  _pinZCD.setTab(&_pinConfigTab);
+  _pinDimmerO1.setTab(_pinConfigTab);
+  _pinDimmerO2.setTab(_pinConfigTab);
+  _pinDisplayClock.setTab(_pinConfigTab);
+  _pinDisplayData.setTab(_pinConfigTab);
+  _pinDS18O1.setTab(_pinConfigTab);
+  _pinDS18O2.setTab(_pinConfigTab);
+  _pinDS18Router.setTab(_pinConfigTab);
+  _pinJsyRX.setTab(_pinConfigTab);
+  _pinJsyTX.setTab(_pinConfigTab);
+  _pinLEDGreen.setTab(_pinConfigTab);
+  _pinLEDRed.setTab(_pinConfigTab);
+  _pinLEDYellow.setTab(_pinConfigTab);
+  _pinPZEMRX.setTab(_pinConfigTab);
+  _pinPZEMTX.setTab(_pinConfigTab);
+  _pinRelay1.setTab(_pinConfigTab);
+  _pinRelay2.setTab(_pinConfigTab);
+  _pinRelayO1.setTab(_pinConfigTab);
+  _pinRelayO2.setTab(_pinConfigTab);
+  _pinZCD.setTab(_pinConfigTab);
 
   _pinConfig(_pinDimmerO1, KEY_PIN_OUTPUT1_DIMMER);
   _pinConfig(_pinDimmerO2, KEY_PIN_OUTPUT2_DIMMER);
@@ -611,22 +611,22 @@ void YaSolR::Website::initLayout() {
 
   // Hardware
 
-  _display.setTab(&_hardwareEnableTab);
-  _jsy.setTab(&_hardwareEnableTab);
-  _led.setTab(&_hardwareEnableTab);
-  _mqtt.setTab(&_hardwareEnableTab);
-  _output1Dimmer.setTab(&_hardwareEnableTab);
-  _output1PZEM.setTab(&_hardwareEnableTab);
-  _output1Relay.setTab(&_hardwareEnableTab);
-  _output1DS18.setTab(&_hardwareEnableTab);
-  _output2Dimmer.setTab(&_hardwareEnableTab);
-  _output2PZEM.setTab(&_hardwareEnableTab);
-  _output2Relay.setTab(&_hardwareEnableTab);
-  _output2DS18.setTab(&_hardwareEnableTab);
-  _relay1.setTab(&_hardwareEnableTab);
-  _relay2.setTab(&_hardwareEnableTab);
-  _routerDS18.setTab(&_hardwareEnableTab);
-  _zcd.setTab(&_hardwareEnableTab);
+  _display.setTab(_hardwareEnableTab);
+  _jsy.setTab(_hardwareEnableTab);
+  _led.setTab(_hardwareEnableTab);
+  _mqtt.setTab(_hardwareEnableTab);
+  _output1Dimmer.setTab(_hardwareEnableTab);
+  _output1PZEM.setTab(_hardwareEnableTab);
+  _output1Relay.setTab(_hardwareEnableTab);
+  _output1DS18.setTab(_hardwareEnableTab);
+  _output2Dimmer.setTab(_hardwareEnableTab);
+  _output2PZEM.setTab(_hardwareEnableTab);
+  _output2Relay.setTab(_hardwareEnableTab);
+  _output2DS18.setTab(_hardwareEnableTab);
+  _relay1.setTab(_hardwareEnableTab);
+  _relay2.setTab(_hardwareEnableTab);
+  _routerDS18.setTab(_hardwareEnableTab);
+  _zcd.setTab(_hardwareEnableTab);
 
   _boolConfig(_display, KEY_ENABLE_DISPLAY);
   _boolConfig(_jsy, KEY_ENABLE_JSY);
@@ -647,93 +647,172 @@ void YaSolR::Website::initLayout() {
 
   // Hardware (config)
 
-  _gridFreq.setTab(&_hardwareConfigTab);
-  _displayRotation.setTab(&_hardwareConfigTab);
-  _displayType.setTab(&_hardwareConfigTab);
-  _displaySpeed.setTab(&_hardwareConfigTab);
-  _output1PZEMSync.setTab(&_hardwareConfigTab);
-  _output2PZEMSync.setTab(&_hardwareConfigTab);
-  _output1RelayType.setTab(&_hardwareConfigTab);
-  _output2RelayType.setTab(&_hardwareConfigTab);
-  _relay1Type.setTab(&_hardwareConfigTab);
-  _relay2Type.setTab(&_hardwareConfigTab);
-  _relay1Load.setTab(&_hardwareConfigTab);
-  _relay2Load.setTab(&_hardwareConfigTab);
-  _output1ResistanceInput.setTab(&_hardwareConfigTab);
-  _output2ResistanceInput.setTab(&_hardwareConfigTab);
-  _output1DimmerMapper.setTab(&_hardwareConfigTab);
-  _output2DimmerMapper.setTab(&_hardwareConfigTab);
-  _resistanceCalibration.setTab(&_hardwareConfigTab);
-
-  _numConfig(_gridFreq, KEY_GRID_FREQUENCY);
+  _gridFreq.setTab(_hardwareConfigTab);
+  _displayRotation.setTab(_hardwareConfigTab);
+  _displayType.setTab(_hardwareConfigTab);
+  _displaySpeed.setTab(_hardwareConfigTab);
+  _output1PZEMSync.setTab(_hardwareConfigTab);
+  _output2PZEMSync.setTab(_hardwareConfigTab);
+  _output1RelayType.setTab(_hardwareConfigTab);
+  _output2RelayType.setTab(_hardwareConfigTab);
+  _relay1Type.setTab(_hardwareConfigTab);
+  _relay2Type.setTab(_hardwareConfigTab);
+  _relay1Load.setTab(_hardwareConfigTab);
+  _relay2Load.setTab(_hardwareConfigTab);
+  _output1ResistanceInput.setTab(_hardwareConfigTab);
+  _output2ResistanceInput.setTab(_hardwareConfigTab);
+  _output1DimmerMapper.setTab(_hardwareConfigTab);
+  _output2DimmerMapper.setTab(_hardwareConfigTab);
+  _resistanceCalibration.setTab(_hardwareConfigTab);
+
+  _gridFreq.onChange([](const char* value) {
+    if (strcmp(value, "50 Hz") == 0)
+      config.set(KEY_GRID_FREQUENCY, "50");
+    else if (strcmp(value, "60 Hz") == 0)
+      config.set(KEY_GRID_FREQUENCY, "60");
+    else
+      config.unset(KEY_GRID_FREQUENCY);
+    _gridFreq.setValue(config.get(KEY_GRID_FREQUENCY));
+    dashboard.refresh(_gridFreq);
+  });
+
   _numConfig(_displayRotation, KEY_DISPLAY_ROTATION);
   _numConfig(_relay1Load, KEY_RELAY1_LOAD);
   _numConfig(_relay2Load, KEY_RELAY2_LOAD);
   _textConfig(_displayType, KEY_DISPLAY_TYPE);
-  _floatConfig(_output1ResistanceInput, KEY_OUTPUT1_RESISTANCE);
-  _floatConfig(_output2ResistanceInput, KEY_OUTPUT2_RESISTANCE);
   _textConfig(_output1RelayType, KEY_OUTPUT1_RELAY_TYPE);
   _textConfig(_output2RelayType, KEY_OUTPUT2_RELAY_TYPE);
   _textConfig(_relay1Type, KEY_RELAY1_TYPE);
   _textConfig(_relay2Type, KEY_RELAY2_TYPE);
   _sliderConfig(_displaySpeed, KEY_DISPLAY_SPEED);
 
-  _output1DimmerMapper.attachCallback([this](const char* value) {
-    const char* comma = strchr(value, ',');
-    if (comma != nullptr) {
-      config.set(KEY_OUTPUT1_DIMMER_MIN, std::string(value, comma - value));
-      config.set(KEY_OUTPUT1_DIMMER_MAX, comma + 1);
-    }
-    _output1DimmerMapper.update(value);
-    dashboard.refreshCard(&_output1DimmerMapper);
+  _output1ResistanceInput.onChange([](const std::optional<float> value) {
+    if (value.has_value())
+      config.set(KEY_OUTPUT1_RESISTANCE, dash::to_string<float, 2>(value.value()));
+    else
+      config.unset(KEY_OUTPUT1_RESISTANCE);
+    _output1ResistanceInput.setValue(config.getFloat(KEY_OUTPUT1_RESISTANCE));
+    dashboard.refresh(_output1ResistanceInput);
   });
-  _output2DimmerMapper.attachCallback([this](const char* value) {
-    const char* comma = strchr(value, ',');
-    if (comma != nullptr) {
-      config.set(KEY_OUTPUT2_DIMMER_MIN, std::string(value, comma - value));
-      config.set(KEY_OUTPUT2_DIMMER_MAX, comma + 1);
-    }
-    _output2DimmerMapper.update(value);
-    dashboard.refreshCard(&_output2DimmerMapper);
+  _output2ResistanceInput.onChange([](const std::optional<float> value) {
+    if (value.has_value())
+      config.set(KEY_OUTPUT2_RESISTANCE, dash::to_string<float, 2>(value.value()));
+    else
+      config.unset(KEY_OUTPUT2_RESISTANCE);
+    _output2ResistanceInput.setValue(config.getFloat(KEY_OUTPUT2_RESISTANCE));
+    dashboard.refresh(_output2ResistanceInput);
+  });
+
+  _output1DimmerMapper.onChange([](const dash::Range<uint8_t>& range) {
+    config.set(KEY_OUTPUT1_DIMMER_MIN, std::to_string(range.low()));
+    config.set(KEY_OUTPUT1_DIMMER_MAX, std::to_string(range.high()));
+    _output1DimmerMapper.setValue({static_cast<uint8_t>(config.getInt(KEY_OUTPUT1_DIMMER_MIN)),
+                                   static_cast<uint8_t>(config.getInt(KEY_OUTPUT1_DIMMER_MAX))});
+    dashboard.refresh(_output1DimmerMapper);
+  });
+
+  _output2DimmerMapper.onChange([](const dash::Range<uint8_t>& range) {
+    config.set(KEY_OUTPUT2_DIMMER_MIN, std::to_string(range.low()));
+    config.set(KEY_OUTPUT2_DIMMER_MAX, std::to_string(range.high()));
+    _output2DimmerMapper.setValue({static_cast<uint8_t>(config.getInt(KEY_OUTPUT2_DIMMER_MIN)),
+                                   static_cast<uint8_t>(config.getInt(KEY_OUTPUT2_DIMMER_MAX))});
+    dashboard.refresh(_output2DimmerMapper);
   });
 
   // PID
 
-  _pidView.setTab(&_pidTab);
-  _pidPMode.setTab(&_pidTab);
-  _pidDMode.setTab(&_pidTab);
-  _pidICMode.setTab(&_pidTab);
-  _pidSetpoint.setTab(&_pidTab);
-  _pidKp.setTab(&_pidTab);
-  _pidKi.setTab(&_pidTab);
-  _pidKd.setTab(&_pidTab);
-  _pidOutMin.setTab(&_pidTab);
-  _pidOutMax.setTab(&_pidTab);
-
-  _pidInputHistory.setTab(&_pidTab);
-  _pidOutputHistory.setTab(&_pidTab);
-  _pidErrorHistory.setTab(&_pidTab);
-  _pidSumHistory.setTab(&_pidTab);
-  _pidPTermHistory.setTab(&_pidTab);
-  _pidITermHistory.setTab(&_pidTab);
-  _pidDTermHistory.setTab(&_pidTab);
-  _pidReset.setTab(&_pidTab);
-
-  _pidReset.attachCallback([this]() {
+  _pidView.setTab(_pidTab);
+  _pidPMode.setTab(_pidTab);
+  _pidDMode.setTab(_pidTab);
+  _pidICMode.setTab(_pidTab);
+  _pidSetpoint.setTab(_pidTab);
+  _pidKp.setTab(_pidTab);
+  _pidKi.setTab(_pidTab);
+  _pidKd.setTab(_pidTab);
+  _pidOutMin.setTab(_pidTab);
+  _pidOutMax.setTab(_pidTab);
+
+  _pidInputHistory.setTab(_pidTab);
+  _pidOutputHistory.setTab(_pidTab);
+  _pidErrorHistory.setTab(_pidTab);
+  _pidSumHistory.setTab(_pidTab);
+  _pidPTermHistory.setTab(_pidTab);
+  _pidITermHistory.setTab(_pidTab);
+  _pidDTermHistory.setTab(_pidTab);
+  _pidReset.setTab(_pidTab);
+
+  _pidReset.onPush([this]() {
     resetPID();
     updatePID();
   });
 
+  _pidPMode.onChange([](const char* value) {
+    if (strcmp(value, YASOLR_PID_P_MODE_1) == 0)
+      config.set(KEY_PID_P_MODE, "1");
+    else if (strcmp(value, YASOLR_PID_P_MODE_2) == 0)
+      config.set(KEY_PID_P_MODE, "2");
+    else if (strcmp(value, YASOLR_PID_P_MODE_3) == 0)
+      config.set(KEY_PID_P_MODE, "3");
+    else
+      config.unset(KEY_PID_P_MODE);
+    _pidPMode.setValue(config.get(KEY_PID_P_MODE));
+    dashboard.refresh(_pidPMode);
+  });
+
+  _pidDMode.onChange([](const char* value) {
+    if (strcmp(value, YASOLR_PID_D_MODE_1) == 0)
+      config.set(KEY_PID_D_MODE, "1");
+    else if (strcmp(value, YASOLR_PID_D_MODE_2) == 0)
+      config.set(KEY_PID_D_MODE, "2");
+    else
+      config.unset(KEY_PID_D_MODE);
+    _pidDMode.setValue(config.get(KEY_PID_D_MODE));
+    dashboard.refresh(_pidDMode);
+  });
+
+  _pidICMode.onChange([](const char* value) {
+    if (strcmp(value, YASOLR_PID_IC_MODE_0) == 0)
+      config.set(KEY_PID_IC_MODE, "0");
+    else if (strcmp(value, YASOLR_PID_IC_MODE_1) == 0)
+      config.set(KEY_PID_IC_MODE, "1");
+    else if (strcmp(value, YASOLR_PID_IC_MODE_2) == 0)
+      config.set(KEY_PID_IC_MODE, "2");
+    else
+      config.unset(KEY_PID_IC_MODE);
+    _pidICMode.setValue(config.get(KEY_PID_IC_MODE));
+    dashboard.refresh(_pidICMode);
+  });
+
   _boolConfig(_pidView, KEY_ENABLE_PID_VIEW);
-  _numConfig(_pidPMode, KEY_PID_P_MODE);
-  _numConfig(_pidDMode, KEY_PID_D_MODE);
-  _numConfig(_pidICMode, KEY_PID_IC_MODE);
   _numConfig(_pidSetpoint, KEY_PID_SETPOINT);
-  _floatConfig(_pidKp, KEY_PID_KP);
-  _floatConfig(_pidKi, KEY_PID_KI);
-  _floatConfig(_pidKd, KEY_PID_KD);
   _numConfig(_pidOutMin, KEY_PID_OUT_MIN);
   _numConfig(_pidOutMax, KEY_PID_OUT_MAX);
+
+  _pidKp.onChange([](const std::optional<float> value) {
+    if (value.has_value())
+      config.set(KEY_PID_KP, dash::to_string<float, 4>(value.value()));
+    else
+      config.unset(KEY_PID_KP);
+    _pidKp.setValue(config.getFloat(KEY_PID_KP));
+    dashboard.refresh(_pidKp);
+  });
+  _pidKi.onChange([](const std::optional<float> value) {
+    if (value.has_value())
+      config.set(KEY_PID_KI, dash::to_string<float, 4>(value.value()));
+    else
+      config.unset(KEY_PID_KI);
+    _pidKi.setValue(config.getFloat(KEY_PID_KI));
+    dashboard.refresh(_pidKi);
+  });
+  _pidKd.onChange([](const std::optional<float> value) {
+    if (value.has_value())
+      config.set(KEY_PID_KD, dash::to_string<float, 4>(value.value()));
+    else
+      config.unset(KEY_PID_KD);
+    _pidKd.setValue(config.getFloat(KEY_PID_KD));
+    dashboard.refresh(_pidKd);
+  });
+
 #endif
 }
 
@@ -742,28 +821,34 @@ void YaSolR::Website::initCards() {
 
   // Statistics
 
-  _appManufacturer.set(Mycila::AppInfo.manufacturer);
-  _appModel.set(Mycila::AppInfo.model);
-  _appName.set(Mycila::AppInfo.name);
-  _appVersion.set(Mycila::AppInfo.version);
-  _deviceBootCount.set(std::to_string(Mycila::System::getBootCount()));
-  _deviceBootReason.set(Mycila::System::getLastRebootReason());
-  _deviceCores.set(std::to_string(ESP.getChipCores()));
-  _deviceModel.set(ESP.getChipModel());
-  _deviceRev.set(std::to_string(ESP.getChipRevision()));
-  _deviceID.set(Mycila::AppInfo.id);
-  _firmwareBuildHash.set(Mycila::AppInfo.buildHash);
-  _firmwareBuildTimestamp.set(Mycila::AppInfo.buildDate);
-  _firmwareFilename.set(Mycila::AppInfo.firmware);
-  _networkAPMAC.set(espConnect.getMACAddress(Mycila::ESPConnect::Mode::AP));
-  _networkEthMAC.set(espConnect.getMACAddress(Mycila::ESPConnect::Mode::ETH).empty() ? std::string("N/A") : espConnect.getMACAddress(Mycila::ESPConnect::Mode::ETH));
-  _networkHostname.set(Mycila::AppInfo.defaultHostname);
-  _networkWiFiMAC.set(espConnect.getMACAddress(Mycila::ESPConnect::Mode::STA));
+  Mycila::ESPConnect::Mode mode = espConnect.getMode();
+  _appManufacturer.setValue(Mycila::AppInfo.manufacturer.c_str());
+  _appModel.setValue(Mycila::AppInfo.model.c_str());
+  _appName.setValue(Mycila::AppInfo.name.c_str());
+  _appVersion.setValue(Mycila::AppInfo.version.c_str());
+  _deviceBootCount.setValue(Mycila::System::getBootCount());
+  _deviceBootReason.setValue(Mycila::System::getLastRebootReason());
+  _deviceCores.setValue(ESP.getChipCores());
+  _deviceID.setValue(Mycila::AppInfo.id.c_str());
+  _deviceModel.setValue(ESP.getChipModel());
+  _deviceRev.setValue(ESP.getChipRevision());
+  _firmwareBuildHash.setValue(Mycila::AppInfo.buildHash.c_str());
+  _firmwareBuildTimestamp.setValue(Mycila::AppInfo.buildDate.c_str());
+  _firmwareFilename.setValue(Mycila::AppInfo.firmware.c_str());
+  _networkAPIP.setValue(espConnect.getIPAddress(Mycila::ESPConnect::Mode::AP).toString().c_str());
+  _networkAPMAC.setValue(espConnect.getMACAddress(Mycila::ESPConnect::Mode::AP));
+  _networkEthIP.setValue(espConnect.getIPAddress(Mycila::ESPConnect::Mode::ETH).toString().c_str());
+  _networkEthMAC.setValue(espConnect.getMACAddress(Mycila::ESPConnect::Mode::ETH).empty() ? std::string("N/A") : espConnect.getMACAddress(Mycila::ESPConnect::Mode::ETH));
+  _networkHostname.setValue(Mycila::AppInfo.defaultHostname.c_str());
+  _networkInterface.setValue(mode == Mycila::ESPConnect::Mode::AP ? "AP" : (mode == Mycila::ESPConnect::Mode::STA ? "WiFi" : (mode == Mycila::ESPConnect::Mode::ETH ? "Ethernet" : "")));
+  _networkWiFiIP.setValue(espConnect.getIPAddress(Mycila::ESPConnect::Mode::STA).toString().c_str());
+  _networkWiFiMAC.setValue(espConnect.getMACAddress(Mycila::ESPConnect::Mode::STA));
+  _networkWiFiSSID.setValue(espConnect.getWiFiSSID());
 
 #ifdef APP_MODEL_PRO
   const bool jsyEnabled = config.getBool(KEY_ENABLE_JSY);
 
-  // output 1 (control)
+  // output 1
 
   const bool dimmer1Enabled = config.getBool(KEY_ENABLE_OUTPUT1_DIMMER);
   const bool output1RelayEnabled = config.getBool(KEY_ENABLE_OUTPUT1_RELAY);
@@ -772,17 +857,18 @@ void YaSolR::Website::initCards() {
   const bool autoBypass1Activated = config.getBool(KEY_ENABLE_OUTPUT1_AUTO_BYPASS);
   const bool output1TempEnabled = config.getBool(KEY_ENABLE_OUTPUT1_DS18) || config.isEmpty(KEY_OUTPUT1_TEMPERATURE_MQTT_TOPIC);
   const bool pzem1Enabled = config.getBool(KEY_ENABLE_OUTPUT1_PZEM);
-
-  _output1DimmerAuto.update(autoDimmer1Activated);
-  _output1DimmerReservedExcess.update(config.getInt(KEY_OUTPUT1_RESERVED_EXCESS));
-  _output1DimmerDutyLimiter.update(config.getInt(KEY_OUTPUT1_DIMMER_LIMIT));
-  _output1DimmerTempLimiter.update(config.get(KEY_OUTPUT1_DIMMER_STOP_TEMP));
-  _output1BypassAuto.update(autoBypass1Activated);
-  _output1AutoStartWDays.update(config.get(KEY_OUTPUT1_DAYS));
-  _output1AutoStartTemp.update(config.get(KEY_OUTPUT1_TEMPERATURE_START));
-  _output1AutoStartTime.update(config.get(KEY_OUTPUT1_TIME_START));
-  _output1AutoStoptTemp.update(config.get(KEY_OUTPUT1_TEMPERATURE_STOP));
-  _output1AutoStoptTime.update(config.get(KEY_OUTPUT1_TIME_STOP));
+  const char* output1Days = config.get(KEY_OUTPUT1_DAYS);
+
+  _output1DimmerAuto.setValue(autoDimmer1Activated);
+  _output1DimmerReservedExcess.setValue(config.getInt(KEY_OUTPUT1_RESERVED_EXCESS));
+  _output1DimmerDutyLimiter.setValue(config.getInt(KEY_OUTPUT1_DIMMER_LIMIT));
+  _output1DimmerTempLimiter.setValue(config.getInt(KEY_OUTPUT1_DIMMER_STOP_TEMP));
+  _output1BypassAuto.setValue(autoBypass1Activated);
+  _output1AutoStartWDays.setValue(strcmp(output1Days, YASOLR_WEEK_DAYS_EMPTY) == 0 ? "" : output1Days);
+  _output1AutoStartTemp.setValue(config.getInt(KEY_OUTPUT1_TEMPERATURE_START));
+  _output1AutoStartTime.setValue(config.get(KEY_OUTPUT1_TIME_START));
+  _output1AutoStoptTemp.setValue(config.getInt(KEY_OUTPUT1_TEMPERATURE_STOP));
+  _output1AutoStoptTime.setValue(config.get(KEY_OUTPUT1_TIME_STOP));
 
   _output1Tab.setDisplay(dimmer1Enabled || output1TempEnabled || output1RelayEnabled);
   _output1DimmerSlider.setDisplay(dimmer1Enabled && !autoDimmer1Activated);
@@ -808,7 +894,7 @@ void YaSolR::Website::initCards() {
   _output1AutoStartTemp.setDisplay(bypass1Possible && autoBypass1Activated && output1TempEnabled);
   _output1AutoStoptTemp.setDisplay(bypass1Possible && autoBypass1Activated && output1TempEnabled);
 
-  // output 2 (control)
+  // output 2
 
   const bool dimmer2Enabled = config.getBool(KEY_ENABLE_OUTPUT2_DIMMER);
   const bool output2RelayEnabled = config.getBool(KEY_ENABLE_OUTPUT2_RELAY);
@@ -817,17 +903,18 @@ void YaSolR::Website::initCards() {
   const bool autoBypass2Activated = config.getBool(KEY_ENABLE_OUTPUT2_AUTO_BYPASS);
   const bool output2TempEnabled = config.getBool(KEY_ENABLE_OUTPUT2_DS18) || !config.isEmpty(KEY_OUTPUT2_TEMPERATURE_MQTT_TOPIC);
   const bool pzem2Enabled = config.getBool(KEY_ENABLE_OUTPUT2_PZEM);
-
-  _output2DimmerAuto.update(autoDimmer2Activated);
-  _output2DimmerReservedExcess.update(config.getInt(KEY_OUTPUT2_RESERVED_EXCESS));
-  _output2DimmerDutyLimiter.update(config.getInt(KEY_OUTPUT2_DIMMER_LIMIT));
-  _output2DimmerTempLimiter.update(config.get(KEY_OUTPUT2_DIMMER_STOP_TEMP));
-  _output2BypassAuto.update(autoBypass2Activated);
-  _output2AutoStartWDays.update(config.get(KEY_OUTPUT2_DAYS));
-  _output2AutoStartTemp.update(config.get(KEY_OUTPUT2_TEMPERATURE_START));
-  _output2AutoStartTime.update(config.get(KEY_OUTPUT2_TIME_START));
-  _output2AutoStoptTemp.update(config.get(KEY_OUTPUT2_TEMPERATURE_STOP));
-  _output2AutoStoptTime.update(config.get(KEY_OUTPUT2_TIME_STOP));
+  const char* output2Days = config.get(KEY_OUTPUT2_DAYS);
+
+  _output2DimmerAuto.setValue(autoDimmer2Activated);
+  _output2DimmerReservedExcess.setValue(config.getInt(KEY_OUTPUT2_RESERVED_EXCESS));
+  _output2DimmerDutyLimiter.setValue(config.getInt(KEY_OUTPUT2_DIMMER_LIMIT));
+  _output2DimmerTempLimiter.setValue(config.getInt(KEY_OUTPUT2_DIMMER_STOP_TEMP));
+  _output2BypassAuto.setValue(autoBypass2Activated);
+  _output2AutoStartWDays.setValue(strcmp(output2Days, YASOLR_WEEK_DAYS_EMPTY) == 0 ? "" : output2Days);
+  _output2AutoStartTemp.setValue(config.getInt(KEY_OUTPUT2_TEMPERATURE_START));
+  _output2AutoStartTime.setValue(config.get(KEY_OUTPUT2_TIME_START));
+  _output2AutoStoptTemp.setValue(config.getInt(KEY_OUTPUT2_TEMPERATURE_STOP));
+  _output2AutoStoptTime.setValue(config.get(KEY_OUTPUT2_TIME_STOP));
 
   _output2Tab.setDisplay(dimmer2Enabled || output2TempEnabled || output2RelayEnabled);
   _output2DimmerSlider.setDisplay(dimmer2Enabled && !autoDimmer2Activated);
@@ -853,10 +940,10 @@ void YaSolR::Website::initCards() {
   _output2AutoStartTemp.setDisplay(bypass2Possible && autoBypass2Activated && output2TempEnabled);
   _output2AutoStoptTemp.setDisplay(bypass2Possible && autoBypass2Activated && output2TempEnabled);
 
-  // relays (control)
+  // relays
 
-  const int load1 = config.getInt(KEY_RELAY1_LOAD);
-  const int load2 = config.getInt(KEY_RELAY2_LOAD);
+  const uint16_t load1 = config.getInt(KEY_RELAY1_LOAD);
+  const uint16_t load2 = config.getInt(KEY_RELAY2_LOAD);
   const bool relay1Enabled = config.getBool(KEY_ENABLE_RELAY1);
   const bool relay2Enabled = config.getBool(KEY_ENABLE_RELAY2);
   _relaysTab.setDisplay(relay1Enabled || relay2Enabled);
@@ -867,43 +954,43 @@ void YaSolR::Website::initCards() {
 
   // management
 
-  _configBackup.update("/api/config/backup");
-  _configRestore.update("/api/config/restore");
-  _consoleLink.update("/console");
-  _debugInfo.update("/api/debug");
-  _debugMode.update(config.getBool(KEY_ENABLE_DEBUG));
+  _configBackup.setValue("/api/config/backup");
+  _configRestore.setValue("/api/config/restore");
+  _consoleLink.setValue("/console");
+  _debugInfo.setValue("/api/debug");
+  _debugMode.setValue(config.getBool(KEY_ENABLE_DEBUG));
   _energyReset.setDisplay(jsyEnabled || pzem1Enabled || pzem2Enabled);
   _debugInfo.setDisplay(config.getBool(KEY_ENABLE_DEBUG));
 
-  // network (config)
-
-  _adminPwd.update(config.isEmpty(KEY_ADMIN_PASSWORD) ? "" : HIDDEN_PWD);
-  _apMode.update(config.getBool(KEY_ENABLE_AP_MODE));
-  _ntpServer.update(config.get(KEY_NTP_SERVER));
-  _ntpTimezone.update(config.get(KEY_NTP_TIMEZONE), "/timezones");
-  _wifiPwd.update(config.isEmpty(KEY_WIFI_PASSWORD) ? "" : HIDDEN_PWD);
-  _wifiSSID.update(config.get(KEY_WIFI_SSID));
-  _staticIP.update(config.get(KEY_NET_IP));
-  _subnetMask.update(config.get(KEY_NET_SUBNET));
-  _gateway.update(config.get(KEY_NET_GATEWAY));
-  _dnsServer.update(config.get(KEY_NET_DNS));
-
-  // mqtt (config)
-
-  _haDiscovery.update(config.getBool(KEY_ENABLE_HA_DISCOVERY));
-  _haDiscoveryTopic.update(config.get(KEY_HA_DISCOVERY_TOPIC));
-  _mqttGridPower.update(config.get(KEY_GRID_POWER_MQTT_TOPIC));
-  _mqttGridVoltage.update(config.get(KEY_GRID_VOLTAGE_MQTT_TOPIC));
-  _mqttTempO1.update(config.get(KEY_OUTPUT1_TEMPERATURE_MQTT_TOPIC));
-  _mqttTempO2.update(config.get(KEY_OUTPUT2_TEMPERATURE_MQTT_TOPIC));
-  _mqttPort.update(config.get(KEY_MQTT_PORT));
-  _mqttPublishInterval.update(config.get(KEY_MQTT_PUBLISH_INTERVAL));
-  _mqttPwd.update(config.isEmpty(KEY_MQTT_PASSWORD) ? "" : HIDDEN_PWD);
-  _mqttSecured.update(config.getBool(KEY_MQTT_SECURED));
-  _mqttServer.update(config.get(KEY_MQTT_SERVER));
-  _mqttServerCert.update("/api/config/mqttServerCertificate");
-  _mqttTopic.update(config.get(KEY_MQTT_TOPIC));
-  _mqttUser.update(config.get(KEY_MQTT_USERNAME));
+  // network
+
+  _adminPwd.setValue(config.get(KEY_ADMIN_PASSWORD));
+  _apMode.setValue(config.getBool(KEY_ENABLE_AP_MODE));
+  _ntpServer.setValue(config.get(KEY_NTP_SERVER));
+  _ntpTimezone.setValue(config.get(KEY_NTP_TIMEZONE));
+  _wifiPwd.setValue(config.get(KEY_WIFI_PASSWORD));
+  _wifiSSID.setValue(config.get(KEY_WIFI_SSID));
+  _staticIP.setValue(config.get(KEY_NET_IP));
+  _subnetMask.setValue(config.get(KEY_NET_SUBNET));
+  _gateway.setValue(config.get(KEY_NET_GATEWAY));
+  _dnsServer.setValue(config.get(KEY_NET_DNS));
+
+  // mqtt
+
+  _haDiscovery.setValue(config.getBool(KEY_ENABLE_HA_DISCOVERY));
+  _haDiscoveryTopic.setValue(config.get(KEY_HA_DISCOVERY_TOPIC));
+  _mqttGridPower.setValue(config.get(KEY_GRID_POWER_MQTT_TOPIC));
+  _mqttGridVoltage.setValue(config.get(KEY_GRID_VOLTAGE_MQTT_TOPIC));
+  _mqttTempO1.setValue(config.get(KEY_OUTPUT1_TEMPERATURE_MQTT_TOPIC));
+  _mqttTempO2.setValue(config.get(KEY_OUTPUT2_TEMPERATURE_MQTT_TOPIC));
+  _mqttPort.setValue(config.getInt(KEY_MQTT_PORT));
+  _mqttPublishInterval.setValue(config.getInt(KEY_MQTT_PUBLISH_INTERVAL));
+  _mqttPwd.setValue(config.get(KEY_MQTT_PASSWORD));
+  _mqttSecured.setValue(config.getBool(KEY_MQTT_SECURED));
+  _mqttServer.setValue(config.get(KEY_MQTT_SERVER));
+  _mqttServerCert.setValue("/api/config/mqttServerCertificate");
+  _mqttTopic.setValue(config.get(KEY_MQTT_TOPIC));
+  _mqttUser.setValue(config.get(KEY_MQTT_USERNAME));
 
   const bool serverCertExists = LittleFS.exists(YASOLR_MQTT_SERVER_CERT_FILE);
   _mqttConfigTab.setDisplay(config.getBool(KEY_ENABLE_MQTT));
@@ -912,26 +999,26 @@ void YaSolR::Website::initCards() {
 
   // GPIO
 
-  std::unordered_map<int32_t, Card*> pinout = {};
-  _pinout(_pinDimmerO1, config.getLong(KEY_PIN_OUTPUT1_DIMMER), pinout);
-  _pinout(_pinDimmerO2, config.getLong(KEY_PIN_OUTPUT2_DIMMER), pinout);
-  _pinout(_pinDisplayClock, config.getLong(KEY_PIN_DISPLAY_SCL), pinout);
-  _pinout(_pinDisplayData, config.getLong(KEY_PIN_DISPLAY_SDA), pinout);
-  _pinout(_pinDS18O1, config.getLong(KEY_PIN_OUTPUT1_DS18), pinout);
-  _pinout(_pinDS18O2, config.getLong(KEY_PIN_OUTPUT2_DS18), pinout);
-  _pinout(_pinDS18Router, config.getLong(KEY_PIN_ROUTER_DS18), pinout);
-  _pinout(_pinJsyRX, config.getLong(KEY_PIN_JSY_RX), pinout);
-  _pinout(_pinJsyTX, config.getLong(KEY_PIN_JSY_TX), pinout);
-  _pinout(_pinLEDGreen, config.getLong(KEY_PIN_LIGHTS_GREEN), pinout);
-  _pinout(_pinLEDRed, config.getLong(KEY_PIN_LIGHTS_RED), pinout);
-  _pinout(_pinLEDYellow, config.getLong(KEY_PIN_LIGHTS_YELLOW), pinout);
-  _pinout(_pinPZEMRX, config.getLong(KEY_PIN_PZEM_RX), pinout);
-  _pinout(_pinPZEMTX, config.getLong(KEY_PIN_PZEM_TX), pinout);
-  _pinout(_pinRelay1, config.getLong(KEY_PIN_RELAY1), pinout);
-  _pinout(_pinRelay2, config.getLong(KEY_PIN_RELAY2), pinout);
-  _pinout(_pinRelayO1, config.getLong(KEY_PIN_OUTPUT1_RELAY), pinout);
-  _pinout(_pinRelayO2, config.getLong(KEY_PIN_OUTPUT2_RELAY), pinout);
-  _pinout(_pinZCD, config.getLong(KEY_PIN_ZCD), pinout);
+  std::unordered_map<int32_t, dash::FeedbackTextInputCard<int32_t>*> pinout = {};
+  _pinout(_pinDimmerO1, KEY_PIN_OUTPUT1_DIMMER, pinout);
+  _pinout(_pinDimmerO2, KEY_PIN_OUTPUT2_DIMMER, pinout);
+  _pinout(_pinDisplayClock, KEY_PIN_DISPLAY_SCL, pinout);
+  _pinout(_pinDisplayData, KEY_PIN_DISPLAY_SDA, pinout);
+  _pinout(_pinDS18O1, KEY_PIN_OUTPUT1_DS18, pinout);
+  _pinout(_pinDS18O2, KEY_PIN_OUTPUT2_DS18, pinout);
+  _pinout(_pinDS18Router, KEY_PIN_ROUTER_DS18, pinout);
+  _pinout(_pinJsyRX, KEY_PIN_JSY_RX, pinout);
+  _pinout(_pinJsyTX, KEY_PIN_JSY_TX, pinout);
+  _pinout(_pinLEDGreen, KEY_PIN_LIGHTS_GREEN, pinout);
+  _pinout(_pinLEDRed, KEY_PIN_LIGHTS_RED, pinout);
+  _pinout(_pinLEDYellow, KEY_PIN_LIGHTS_YELLOW, pinout);
+  _pinout(_pinPZEMRX, KEY_PIN_PZEM_RX, pinout);
+  _pinout(_pinPZEMTX, KEY_PIN_PZEM_TX, pinout);
+  _pinout(_pinRelay1, KEY_PIN_RELAY1, pinout);
+  _pinout(_pinRelay2, KEY_PIN_RELAY2, pinout);
+  _pinout(_pinRelayO1, KEY_PIN_OUTPUT1_RELAY, pinout);
+  _pinout(_pinRelayO2, KEY_PIN_OUTPUT2_RELAY, pinout);
+  _pinout(_pinZCD, KEY_PIN_ZCD, pinout);
   pinout.clear();
 
   // Hardware
@@ -943,34 +1030,37 @@ void YaSolR::Website::initCards() {
   _status(_relay1, KEY_ENABLE_RELAY1, relay1.isEnabled());
   _status(_relay2, KEY_ENABLE_RELAY2, relay2.isEnabled());
 
-  // Hardware (config)
+  // Hardware Config
 
-  switch (config.getLong(KEY_GRID_FREQUENCY)) {
+  switch (config.getInt(KEY_GRID_FREQUENCY)) {
     case 50:
-      _gridFreq.update("50 Hz", "Auto-detect,50 Hz,60 Hz");
+      _gridFreq.setValue("50 Hz");
       break;
     case 60:
-      _gridFreq.update("60 Hz", "Auto-detect,50 Hz,60 Hz");
+      _gridFreq.setValue("60 Hz");
       break;
     default:
-      _gridFreq.update("Auto-detect", "Auto-detect,50 Hz,60 Hz");
+      _gridFreq.setValue("Auto-detect");
       break;
   }
 
-  _displayType.update(config.get(KEY_DISPLAY_TYPE), "SH1106,SH1107,SSD1306");
-  _displaySpeed.update(config.getInt(KEY_DISPLAY_SPEED));
-  _displayRotation.update(config.getString(KEY_DISPLAY_ROTATION) + "°", "0°,90°,180°,270°");
-  _output1RelayType.update(config.get(KEY_OUTPUT1_RELAY_TYPE), "NO,NC");
-  _output2RelayType.update(config.get(KEY_OUTPUT2_RELAY_TYPE), "NO,NC");
-  _relay1Type.update(config.get(KEY_RELAY1_TYPE), "NO,NC");
-  _relay2Type.update(config.get(KEY_RELAY2_TYPE), "NO,NC");
-  _relay1Load.update(load1);
-  _relay2Load.update(load2);
-  _output1ResistanceInput.update(config.get(KEY_OUTPUT1_RESISTANCE), config.getFloat(KEY_OUTPUT1_RESISTANCE) == 0 ? DASH_STATUS_DANGER : DASH_STATUS_SUCCESS);
-  _output2ResistanceInput.update(config.get(KEY_OUTPUT2_RESISTANCE), config.getFloat(KEY_OUTPUT2_RESISTANCE) == 0 ? DASH_STATUS_DANGER : DASH_STATUS_SUCCESS);
-  _output1DimmerMapper.update(config.getString(KEY_OUTPUT1_DIMMER_MIN) + "," + config.get(KEY_OUTPUT1_DIMMER_MAX));
-  _output2DimmerMapper.update(config.getString(KEY_OUTPUT2_DIMMER_MIN) + "," + config.get(KEY_OUTPUT2_DIMMER_MAX));
-
+  _displayType.setValue(config.get(KEY_DISPLAY_TYPE));
+  _displaySpeed.setValue(config.getInt(KEY_DISPLAY_SPEED));
+  _displayRotation.setValue(config.getInt(KEY_DISPLAY_ROTATION));
+  _output1RelayType.setValue(config.get(KEY_OUTPUT1_RELAY_TYPE));
+  _output2RelayType.setValue(config.get(KEY_OUTPUT2_RELAY_TYPE));
+  _relay1Type.setValue(config.get(KEY_RELAY1_TYPE));
+  _relay2Type.setValue(config.get(KEY_RELAY2_TYPE));
+  _relay1Load.setValue(load1);
+  _relay2Load.setValue(load2);
+  _output1ResistanceInput.setValue(config.getFloat(KEY_OUTPUT1_RESISTANCE));
+  _output1ResistanceInput.setStatus(_output1ResistanceInput.value() ? dash::Status::SUCCESS : dash::Status::DANGER);
+  _output2ResistanceInput.setValue(config.getFloat(KEY_OUTPUT2_RESISTANCE));
+  _output2ResistanceInput.setStatus(_output2ResistanceInput.value() ? dash::Status::SUCCESS : dash::Status::DANGER);
+  _output1DimmerMapper.setValue({static_cast<uint8_t>(config.getInt(KEY_OUTPUT1_DIMMER_MIN)),
+                                 static_cast<uint8_t>(config.getInt(KEY_OUTPUT1_DIMMER_MAX))});
+  _output2DimmerMapper.setValue({static_cast<uint8_t>(config.getInt(KEY_OUTPUT2_DIMMER_MIN)),
+                                 static_cast<uint8_t>(config.getInt(KEY_OUTPUT2_DIMMER_MAX))});
   _displayType.setDisplay(config.getBool(KEY_ENABLE_DISPLAY));
   _displaySpeed.setDisplay(config.getBool(KEY_ENABLE_DISPLAY));
   _displayRotation.setDisplay(config.getBool(KEY_ENABLE_DISPLAY));
@@ -994,52 +1084,52 @@ void YaSolR::Website::initCards() {
   // PID
 
   const bool pidViewEnabled = config.getBool(KEY_ENABLE_PID_VIEW);
-  _pidView.update(pidViewEnabled);
-  switch (config.getLong(KEY_PID_P_MODE)) {
+  _pidView.setValue(pidViewEnabled);
+  switch (config.getInt(KEY_PID_P_MODE)) {
     case 1:
-      _pidPMode.update(YASOLR_PID_P_MODE_1, YASOLR_PID_P_MODE_1 "," YASOLR_PID_P_MODE_2 "," YASOLR_PID_P_MODE_3);
+      _pidPMode.setValue(YASOLR_PID_P_MODE_1);
       break;
     case 2:
-      _pidPMode.update(YASOLR_PID_P_MODE_2, YASOLR_PID_P_MODE_1 "," YASOLR_PID_P_MODE_2 "," YASOLR_PID_P_MODE_3);
+      _pidPMode.setValue(YASOLR_PID_P_MODE_2);
       break;
     case 3:
-      _pidPMode.update(YASOLR_PID_P_MODE_3, YASOLR_PID_P_MODE_1 "," YASOLR_PID_P_MODE_2 "," YASOLR_PID_P_MODE_3);
+      _pidPMode.setValue(YASOLR_PID_P_MODE_3);
       break;
     default:
-      _pidPMode.update("", YASOLR_PID_P_MODE_1 "," YASOLR_PID_P_MODE_2 "," YASOLR_PID_P_MODE_3);
+      _pidPMode.setValue("");
       break;
   }
-  switch (config.getLong(KEY_PID_D_MODE)) {
+  switch (config.getInt(KEY_PID_D_MODE)) {
     case 1:
-      _pidDMode.update(YASOLR_PID_D_MODE_1, YASOLR_PID_D_MODE_1 "," YASOLR_PID_D_MODE_2);
+      _pidDMode.setValue(YASOLR_PID_D_MODE_1);
       break;
     case 2:
-      _pidDMode.update(YASOLR_PID_D_MODE_2, YASOLR_PID_D_MODE_1 "," YASOLR_PID_D_MODE_2);
+      _pidDMode.setValue(YASOLR_PID_D_MODE_2);
       break;
     default:
-      _pidDMode.update("", YASOLR_PID_D_MODE_1 "," YASOLR_PID_D_MODE_2);
+      _pidDMode.setValue("");
       break;
   }
-  switch (config.getLong(KEY_PID_IC_MODE)) {
+  switch (config.getInt(KEY_PID_IC_MODE)) {
     case 0:
-      _pidICMode.update(YASOLR_PID_IC_MODE_0, YASOLR_PID_IC_MODE_0 "," YASOLR_PID_IC_MODE_1 "," YASOLR_PID_IC_MODE_2);
+      _pidICMode.setValue(YASOLR_PID_IC_MODE_0);
       break;
     case 1:
-      _pidICMode.update(YASOLR_PID_IC_MODE_1, YASOLR_PID_IC_MODE_0 "," YASOLR_PID_IC_MODE_1 "," YASOLR_PID_IC_MODE_2);
+      _pidICMode.setValue(YASOLR_PID_IC_MODE_1);
       break;
     case 2:
-      _pidICMode.update(YASOLR_PID_IC_MODE_2, YASOLR_PID_IC_MODE_0 "," YASOLR_PID_IC_MODE_1 "," YASOLR_PID_IC_MODE_2);
+      _pidICMode.setValue(YASOLR_PID_IC_MODE_2);
       break;
     default:
-      _pidICMode.update("", YASOLR_PID_IC_MODE_0 "," YASOLR_PID_IC_MODE_1 "," YASOLR_PID_IC_MODE_2);
+      _pidICMode.setValue("");
       break;
   }
-  _pidSetpoint.update(config.get(KEY_PID_SETPOINT));
-  _pidKp.update(config.get(KEY_PID_KP));
-  _pidKi.update(config.get(KEY_PID_KI));
-  _pidKd.update(config.get(KEY_PID_KD));
-  _pidOutMin.update(config.get(KEY_PID_OUT_MIN));
-  _pidOutMax.update(config.get(KEY_PID_OUT_MAX));
+  _pidSetpoint.setValue(config.getInt(KEY_PID_SETPOINT));
+  _pidKp.setValue(config.getFloat(KEY_PID_KP));
+  _pidKi.setValue(config.getFloat(KEY_PID_KI));
+  _pidKd.setValue(config.getFloat(KEY_PID_KD));
+  _pidOutMin.setValue(config.getInt(KEY_PID_OUT_MIN));
+  _pidOutMax.setValue(config.getInt(KEY_PID_OUT_MAX));
 
   _pidInputHistory.setDisplay(pidViewEnabled);
   _pidOutputHistory.setDisplay(pidViewEnabled);
@@ -1067,133 +1157,125 @@ void YaSolR::Website::updateCards() {
   // stats
   Mycila::System::Memory memory;
   Mycila::System::getMemory(memory);
-  Mycila::ESPConnect::Mode mode = espConnect.getMode();
-  _output1RelaySwitchCount.set(std::to_string(bypassRelayO1.getSwitchCount()));
-  _output2RelaySwitchCount.set(std::to_string(bypassRelayO2.getSwitchCount()));
-  _deviceHeapTotal.set(std::to_string(memory.total) + " bytes");
-  _deviceHeapUsed.set(std::to_string(memory.used) + " bytes");
-  _deviceHeapUsage.set(Mycila::string::to_string(memory.usage, 2) + " %");
-  _gridEnergy.set(Mycila::string::to_string(gridMetrics.energy, 3) + " kWh");
-  _gridEnergyReturned.set(Mycila::string::to_string(gridMetrics.energyReturned, 3) + " kWh");
-  _gridFrequency.set(Mycila::string::to_string(detectGridFrequency(), 0) + " Hz");
-  _networkAPIP.set(espConnect.getIPAddress(Mycila::ESPConnect::Mode::AP).toString().c_str());
-  _networkEthIP.set(espConnect.getIPAddress(Mycila::ESPConnect::Mode::ETH).toString().c_str());
-  _networkInterface.set(mode == Mycila::ESPConnect::Mode::AP ? "AP" : (mode == Mycila::ESPConnect::Mode::STA ? "WiFi" : (mode == Mycila::ESPConnect::Mode::ETH ? "Ethernet" : "")));
-  _networkWiFiIP.set(espConnect.getIPAddress(Mycila::ESPConnect::Mode::STA).toString().c_str());
-  _networkWiFiRSSI.set(std::to_string(espConnect.getWiFiRSSI()) + " dBm");
-  _networkWiFiSignal.set(std::to_string(espConnect.getWiFiSignalQuality()) + " %");
-  _networkWiFiSSID.set(espConnect.getWiFiSSID());
-  _relay1SwitchCount.set(std::to_string(relay1.getSwitchCount()));
-  _relay2SwitchCount.set(std::to_string(relay2.getSwitchCount()));
-  _udpMessageRateBuffer.set(Mycila::string::to_string(udpMessageRateBuffer.rate(), 2) + " msg/s");
-  _time.set(Mycila::Time::getLocalStr());
-  _uptime.set(Mycila::Time::toDHHMMSS(Mycila::System::getUptime()));
+  _output1RelaySwitchCount.setValue(bypassRelayO1.getSwitchCount());
+  _output2RelaySwitchCount.setValue(bypassRelayO2.getSwitchCount());
+  _deviceHeapTotal.setValue(memory.total);
+  _deviceHeapUsed.setValue(memory.used);
+  _deviceHeapUsage.setValue(memory.usage);
+  _gridEnergy.setValue(gridMetrics.energy);
+  _gridEnergyReturned.setValue(gridMetrics.energyReturned);
+  _gridFrequency.setValue(detectGridFrequency());
+  _networkWiFiRSSI.setValue(espConnect.getWiFiRSSI());
+  _networkWiFiSignal.setValue(espConnect.getWiFiSignalQuality());
+  _relay1SwitchCount.setValue(relay1.getSwitchCount());
+  _relay2SwitchCount.setValue(relay2.getSwitchCount());
+  _udpMessageRateBuffer.setValue(udpMessageRateBuffer.rate());
+  _time.setValue(Mycila::Time::getLocalStr());
+  _uptime.setValue(Mycila::Time::toDHHMMSS(Mycila::System::getUptime()));
 #ifdef APP_MODEL_TRIAL
-  _trialRemainingTime.set(Mycila::Time::toDHHMMSS(Mycila::Trial.getRemaining()));
+  _trialRemainingTime.setValue(Mycila::Time::toDHHMMSS(Mycila::Trial.getRemaining()));
 #endif
 
   // home
 
-  _routerPower.update(routerMetrics.power);
-  _routerApparentPower.update(routerMetrics.apparentPower);
-  _routerPowerFactor.update(routerMetrics.powerFactor);
-  _routerTHDi.update(routerMetrics.thdi * 100);
-  _routerVoltage.update(gridMetrics.voltage);
-  _routerCurrent.update(routerMetrics.current);
-  _routerResistance.update(routerMetrics.resistance);
-  _routerEnergy.update(routerMetrics.energy);
-
-  _gridPower.update(gridMetrics.power);
-  _temperature(_routerDS18State, ds18Sys);
+  _routerPower.setValue(routerMetrics.power);
+  _routerApparentPower.setValue(routerMetrics.apparentPower);
+  _routerPowerFactor.setValue(routerMetrics.powerFactor);
+  _routerTHDi.setValue(routerMetrics.thdi * 100);
+  _routerVoltage.setValue(gridMetrics.voltage);
+  _routerCurrent.setValue(routerMetrics.current);
+  _routerResistance.setValue(routerMetrics.resistance);
+  _routerEnergy.setValue(routerMetrics.energy);
+  _gridPower.setValue(gridMetrics.power);
+  _routerDS18State.setValue(ds18Sys.getTemperature().value_or(0.0f));
 
   // output 1
 
   switch (output1.getState()) {
     case Mycila::RouterOutput::State::OUTPUT_DISABLED:
     case Mycila::RouterOutput::State::OUTPUT_IDLE:
-      _output1State.update(output1.getStateName(), DASH_STATUS_IDLE);
+      _output1State.setFeedback(output1.getStateName(), dash::Status::IDLE);
       break;
     case Mycila::RouterOutput::State::OUTPUT_BYPASS_AUTO:
     case Mycila::RouterOutput::State::OUTPUT_BYPASS_MANUAL:
-      _output1State.update(output1.getStateName(), DASH_STATUS_WARNING);
+      _output1State.setFeedback(output1.getStateName(), dash::Status::WARNING);
       break;
     case Mycila::RouterOutput::State::OUTPUT_ROUTING:
-      _output1State.update(output1.getStateName(), DASH_STATUS_SUCCESS);
+      _output1State.setFeedback(output1.getStateName(), dash::Status::SUCCESS);
       break;
     default:
-      _output1State.update(YASOLR_LBL_109, DASH_STATUS_DANGER);
+      _output1State.setFeedback(YASOLR_LBL_109, dash::Status::DANGER);
       break;
   }
-  _temperature(_output1DS18State, output1);
-  _output1DimmerSlider.update(dimmerO1.getDutyCycle() * 100);
-  _output1Bypass.update(output1.isBypassOn());
+  _output1DS18State.setValue(output1.temperature().orElse(0.0f));
+  _output1DimmerSlider.setValue(dimmerO1.getDutyCycle() * 100);
+  _output1Bypass.setValue(output1.isBypassOn());
 
   // output 2
 
   switch (output2.getState()) {
     case Mycila::RouterOutput::State::OUTPUT_DISABLED:
     case Mycila::RouterOutput::State::OUTPUT_IDLE:
-      _output2State.update(output2.getStateName(), DASH_STATUS_IDLE);
+      _output2State.setFeedback(output2.getStateName(), dash::Status::IDLE);
       break;
     case Mycila::RouterOutput::State::OUTPUT_BYPASS_AUTO:
     case Mycila::RouterOutput::State::OUTPUT_BYPASS_MANUAL:
-      _output2State.update(output2.getStateName(), DASH_STATUS_WARNING);
+      _output2State.setFeedback(output2.getStateName(), dash::Status::WARNING);
       break;
     case Mycila::RouterOutput::State::OUTPUT_ROUTING:
-      _output2State.update(output2.getStateName(), DASH_STATUS_SUCCESS);
+      _output2State.setFeedback(output2.getStateName(), dash::Status::SUCCESS);
       break;
     default:
-      _output2State.update(YASOLR_LBL_109, DASH_STATUS_DANGER);
+      _output2State.setFeedback(YASOLR_LBL_109, dash::Status::DANGER);
       break;
   }
-  _temperature(_output2DS18State, output2);
-  _output2DimmerSlider.update(dimmerO2.getDutyCycle() * 100);
-  _output2Bypass.update(output2.isBypassOn());
+  _output2DS18State.setValue(output2.temperature().orElse(0.0f));
+  _output2DimmerSlider.setValue(dimmerO2.getDutyCycle() * 100);
+  _output2Bypass.setValue(output2.isBypassOn());
 
   // relay
 
-  _relay1Switch.update(relay1.isOn());
-  _relay2Switch.update(relay2.isOn());
+  _relay1Switch.setValue(relay1.isOn());
+  _relay2Switch.setValue(relay2.isOn());
 
   // Hardware (config)
 
-  _output1PZEMSync.update(!pzemO1PairingTask.isPaused());
-  _output2PZEMSync.update(!pzemO2PairingTask.isPaused());
-  _resistanceCalibration.update(router.isCalibrationRunning());
+  _output1PZEMSync.setValue(!pzemO1PairingTask.isPaused());
+  _output2PZEMSync.setValue(!pzemO2PairingTask.isPaused());
+  _resistanceCalibration.setValue(router.isCalibrationRunning());
 
 #ifdef APP_MODEL_PRO
   // Output 1
 
-  _output1DimmerSliderRO.update(dimmerO1.getDutyCycle() * 100);
-  _output1BypassRO.update(YASOLR_STATE(output1.isBypassOn()), output1.isBypassOn() ? DASH_STATUS_SUCCESS : DASH_STATUS_IDLE);
-  _output1Power.update(output1Measurements.power);
-  _output1ApparentPower.update(output1Measurements.apparentPower);
-  _output1PowerFactor.update(output1Measurements.powerFactor);
-  _output1THDi.update(output1Measurements.thdi * 100);
-  _output1Voltage.update(output1Measurements.dimmedVoltage);
-  _output1Current.update(output1Measurements.current);
-  _output1Resistance.update(output1Measurements.resistance);
-  _output1Energy.update(output1Measurements.energy);
+  _output1DimmerSliderRO.setValue(dimmerO1.getDutyCycle() * 100);
+  _output1BypassRO.setFeedback(YASOLR_STATE(output1.isBypassOn()), output1.isBypassOn() ? dash::Status::SUCCESS : dash::Status::IDLE);
+  _output1Power.setValue(output1Measurements.power);
+  _output1ApparentPower.setValue(output1Measurements.apparentPower);
+  _output1PowerFactor.setValue(output1Measurements.powerFactor);
+  _output1THDi.setValue(output1Measurements.thdi * 100);
+  _output1Voltage.setValue(output1Measurements.dimmedVoltage);
+  _output1Current.setValue(output1Measurements.current);
+  _output1Resistance.setValue(output1Measurements.resistance);
+  _output1Energy.setValue(output1Measurements.energy);
 
   // output 2
 
-  _output2DimmerSliderRO.update(dimmerO2.getDutyCycle() * 100);
-  _output2BypassRO.update(YASOLR_STATE(output2.isBypassOn()), output2.isBypassOn() ? DASH_STATUS_SUCCESS : DASH_STATUS_IDLE);
-  _output2Power.update(output2Measurements.power);
-  _output2ApparentPower.update(output2Measurements.apparentPower);
-  _output2PowerFactor.update(output2Measurements.powerFactor);
-  _output2THDi.update(output2Measurements.thdi * 100);
-  _output2Voltage.update(output2Measurements.dimmedVoltage);
-  _output2Current.update(output2Measurements.current);
-  _output2Resistance.update(output2Measurements.resistance);
-  _output2Energy.update(output2Measurements.energy);
+  _output2DimmerSliderRO.setValue(dimmerO2.getDutyCycle() * 100);
+  _output2BypassRO.setFeedback(YASOLR_STATE(output2.isBypassOn()), output2.isBypassOn() ? dash::Status::SUCCESS : dash::Status::IDLE);
+  _output2Power.setValue(output2Measurements.power);
+  _output2ApparentPower.setValue(output2Measurements.apparentPower);
+  _output2PowerFactor.setValue(output2Measurements.powerFactor);
+  _output2THDi.setValue(output2Measurements.thdi * 100);
+  _output2Voltage.setValue(output2Measurements.dimmedVoltage);
+  _output2Current.setValue(output2Measurements.current);
+  _output2Resistance.setValue(output2Measurements.resistance);
+  _output2Energy.setValue(output2Measurements.energy);
 
   // relays
 
-  _relay1SwitchRO.update(YASOLR_STATE(relay1.isOn()), relay1.isOn() ? DASH_STATUS_SUCCESS : DASH_STATUS_IDLE);
-  _relay2SwitchRO.update(YASOLR_STATE(relay2.isOn()), relay2.isOn() ? DASH_STATUS_SUCCESS : DASH_STATUS_IDLE);
+  _relay1SwitchRO.setFeedback(YASOLR_STATE(relay1.isOn()), relay1.isOn() ? dash::Status::SUCCESS : dash::Status::IDLE);
 
-  // Hardware (status)
+  // Hardware
 
   _status(_jsy, KEY_ENABLE_JSY, jsy.isEnabled(), jsy.isConnected(), YASOLR_LBL_110);
   _status(_mqtt, KEY_ENABLE_MQTT, mqtt.isEnabled(), mqtt.isConnected(), mqtt.getLastError() ? mqtt.getLastError() : YASOLR_LBL_113);
@@ -1228,9 +1310,9 @@ void YaSolR::Website::updateCharts() {
   _routerTHDiHistoryY[YASOLR_GRAPH_POINTS - 1] = round(routerMetrics.thdi * 100);
 
   // update charts
-  _gridPowerHistory.updateY(_gridPowerHistoryY, YASOLR_GRAPH_POINTS);
-  _routedPowerHistory.updateY(_routedPowerHistoryY, YASOLR_GRAPH_POINTS);
-  _routerTHDiHistory.updateY(_routerTHDiHistoryY, YASOLR_GRAPH_POINTS);
+  _gridPowerHistory.setY(_gridPowerHistoryY, YASOLR_GRAPH_POINTS);
+  _routedPowerHistory.setY(_routedPowerHistoryY, YASOLR_GRAPH_POINTS);
+  _routerTHDiHistory.setY(_routerTHDiHistoryY, YASOLR_GRAPH_POINTS);
 }
 
 void YaSolR::Website::updatePID() {
@@ -1255,13 +1337,13 @@ void YaSolR::Website::updatePID() {
   _pidDTermHistoryY[YASOLR_GRAPH_POINTS - 1] = round(pidController.getDTerm());
 
   // update charts
-  _pidInputHistory.updateY(_pidInputHistoryY, YASOLR_GRAPH_POINTS);
-  _pidOutputHistory.updateY(_pidOutputHistoryY, YASOLR_GRAPH_POINTS);
-  _pidErrorHistory.updateY(_pidErrorHistoryY, YASOLR_GRAPH_POINTS);
-  _pidSumHistory.updateY(_pidSumHistoryY, YASOLR_GRAPH_POINTS);
-  _pidPTermHistory.updateY(_pidPTermHistoryY, YASOLR_GRAPH_POINTS);
-  _pidITermHistory.updateY(_pidITermHistoryY, YASOLR_GRAPH_POINTS);
-  _pidDTermHistory.updateY(_pidDTermHistoryY, YASOLR_GRAPH_POINTS);
+  _pidInputHistory.setY(_pidInputHistoryY, YASOLR_GRAPH_POINTS);
+  _pidOutputHistory.setY(_pidOutputHistoryY, YASOLR_GRAPH_POINTS);
+  _pidErrorHistory.setY(_pidErrorHistoryY, YASOLR_GRAPH_POINTS);
+  _pidSumHistory.setY(_pidSumHistoryY, YASOLR_GRAPH_POINTS);
+  _pidPTermHistory.setY(_pidPTermHistoryY, YASOLR_GRAPH_POINTS);
+  _pidITermHistory.setY(_pidITermHistoryY, YASOLR_GRAPH_POINTS);
+  _pidDTermHistory.setY(_pidDTermHistoryY, YASOLR_GRAPH_POINTS);
 #endif
 }
 
@@ -1276,183 +1358,3 @@ void YaSolR::Website::resetPID() {
   memset(_pidDTermHistoryY, 0, sizeof(_pidDTermHistoryY));
 #endif
 }
-
-void YaSolR::Website::_sliderConfig(Card& card, const char* key) {
-  card.attachCallback([key, &card](int value) {
-    config.set(key, std::to_string(value));
-    card.update(config.getInt(key));
-    dashboard.refreshCard(&card);
-  });
-}
-
-void YaSolR::Website::_percentageSlider(Card& card, const char* key) {
-  card.attachCallback([key, &card](int value) {
-    config.set(key, std::to_string(value));
-    card.update(value);
-    dashboard.refreshCard(&card);
-  });
-}
-
-void YaSolR::Website::_floatConfig(Card& card, const char* key) {
-#ifdef APP_MODEL_PRO
-  card.attachCallback([key, &card](const char* value) {
-    if (value[0]) {
-      config.set(key, value);
-    } else {
-      config.unset(key);
-    }
-    card.update(config.get(key));
-    dashboard.refreshCard(&card);
-  });
-#endif
-}
-
-void YaSolR::Website::_numConfig(Card& card, const char* key) {
-#ifdef APP_MODEL_PRO
-  card.attachCallback([key, &card](const char* value) {
-    if (value[0]) {
-      config.set(key, std::to_string(strtol(value, nullptr, 10)));
-    } else {
-      config.unset(key);
-    }
-    card.update(config.getInt(key));
-    dashboard.refreshCard(&card);
-  });
-#endif
-}
-
-void YaSolR::Website::_pinConfig(Card& card, const char* key) {
-#ifdef APP_MODEL_PRO
-  card.attachCallback([key, &card, this](const char* value) {
-    if (value[0]) {
-      config.set(key, std::to_string(strtol(value, nullptr, 10)));
-    } else {
-      config.unset(key);
-    }
-    dashboard.refreshCard(&card);
-  });
-#endif
-}
-
-void YaSolR::Website::_boolConfig(Card& card, const char* key) {
-  card.attachCallback([key, &card, this](int value) {
-    config.setBool(key, value);
-    card.update(config.getBool(key) ? 1 : 0);
-    dashboard.refreshCard(&card);
-  });
-}
-
-void YaSolR::Website::_textConfig(Card& card, const char* key) {
-#ifdef APP_MODEL_PRO
-  card.attachCallback([key, &card](const char* value) {
-    config.set(key, value);
-    card.update(config.get(key));
-    dashboard.refreshCard(&card);
-  });
-#endif
-}
-
-void YaSolR::Website::_daysConfig(Card& card, const char* key) {
-#ifdef APP_MODEL_PRO
-  card.attachCallback([key, &card, this](const char* value) {
-    config.set(key, value[0] ? value : "none");
-    card.update(config.get(key));
-    dashboard.refreshCard(&card);
-  });
-#endif
-}
-
-void YaSolR::Website::_passwordConfig(Card& card, const char* key) {
-#ifdef APP_MODEL_PRO
-  card.attachCallback([key, &card, this](const char* value) {
-    if (value[0]) {
-      config.set(key, value);
-    } else {
-      config.unset(key);
-    }
-    card.update(config.isEmpty(key) ? "" : HIDDEN_PWD);
-    dashboard.refreshCard(&card);
-  });
-#endif
-}
-
-void YaSolR::Website::_relaySwitch(Card& card, Mycila::RouterRelay& relay) {
-  card.attachCallback([&card, &relay, this](int value) {
-    relay.tryRelayState(value);
-    card.update(relay.isOn());
-    dashboard.refreshCard(&card);
-  });
-}
-
-void YaSolR::Website::_outputBypassSwitch(Card& card, Mycila::RouterOutput& output) {
-  card.attachCallback([&card, &output, this](int value) {
-    if (output.isBypassEnabled()) {
-      output.setBypass(value);
-    }
-    card.update(output.isBypassOn());
-    dashboard.refreshCard(&card);
-    dashboardInitTask.resume();
-  });
-}
-
-void YaSolR::Website::_outputDimmerSlider(Card& card, Mycila::RouterOutput& output) {
-  card.attachCallbackF([&card, &output, this](float value) {
-    if (output.isDimmerEnabled()) {
-      output.setDimmerDutyCycle(value / 100);
-    }
-    card.update(output.getDimmerDutyCycle() * 100);
-    dashboard.refreshCard(&card);
-    dashboardUpdateTask.requestEarlyRun();
-  });
-}
-
-void YaSolR::Website::_temperature(Card& card, Mycila::DS18& sensor) {
-  if (!sensor.isEnabled()) {
-    card.update(YASOLR_LBL_115, "");
-  } else if (!sensor.isValid()) {
-    card.update(YASOLR_LBL_123, "");
-  } else {
-    card.update(sensor.getTemperature().value_or(0), "°C");
-  }
-}
-
-void YaSolR::Website::_temperature(Card& card, Mycila::RouterOutput& output) {
-  if (output.temperature().neverUpdated()) {
-    card.update(YASOLR_LBL_115, "");
-  } else if (output.temperature().isAbsent()) {
-    card.update(YASOLR_LBL_123, "");
-  } else {
-    card.update(output.temperature().get(), "°C");
-  }
-}
-
-void YaSolR::Website::_status(Card& card, const char* key, bool enabled, bool active, const char* err) {
-  const bool configEnabled = config.getBool(key);
-  if (!configEnabled)
-    card.update(config.getBool(key), DASH_STATUS_IDLE "," YASOLR_LBL_115);
-  else if (!enabled)
-    card.update(config.getBool(key), DASH_STATUS_DANGER "," YASOLR_LBL_124);
-  else if (!active)
-    card.update(config.getBool(key), (std::string(DASH_STATUS_WARNING) + "," + err).c_str());
-  else
-    card.update(config.getBool(key), DASH_STATUS_SUCCESS "," YASOLR_LBL_130);
-}
-
-void YaSolR::Website::_pinout(Card& card, int32_t pin, std::unordered_map<int32_t, Card*>& pinout) {
-  if (pin == GPIO_NUM_NC) {
-    card.update(YASOLR_LBL_115, DASH_STATUS_IDLE);
-  } else if (pinout.find(pin) != pinout.end()) {
-    std::string v = std::to_string(pin) + " (" YASOLR_LBL_153 ")";
-    pinout[pin]->update(v, DASH_STATUS_DANGER);
-    card.update(v, DASH_STATUS_DANGER);
-  } else if (!GPIO_IS_VALID_GPIO(pin)) {
-    pinout[pin] = &card;
-    card.update(std::to_string(pin) + " (" YASOLR_LBL_154 ")", DASH_STATUS_DANGER);
-  } else if (!GPIO_IS_VALID_OUTPUT_GPIO(pin)) {
-    pinout[pin] = &card;
-    card.update(std::to_string(pin) + " (" YASOLR_LBL_155 ")", DASH_STATUS_WARNING);
-  } else {
-    pinout[pin] = &card;
-    card.update(std::to_string(pin) + " (" YASOLR_LBL_156 ")", DASH_STATUS_SUCCESS);
-  }
-}
diff --git a/src/main.cpp b/src/main.cpp
index a4a5fd9d..d591e64c 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -52,7 +52,7 @@ AsyncWebServer webServer(80);
 AsyncWebSocket wsDebugPID("/ws/pid/csv");
 AuthenticationMiddleware authMiddleware;
 LoggingMiddleware loggingMiddleware;
-ESPDash dashboard = ESPDash(&webServer, "/dashboard", false);
+ESPDash dashboard = ESPDash(webServer, "/dashboard", false);
 Mycila::ESPConnect espConnect(webServer);
 
 void setup() {