#include "settings_html.h" #include #include "../../../src/communication/contactorcontrol/comm_contactorcontrol.h" #include "../../charger/CHARGERS.h" #include "../../communication/can/comm_can.h" #include "../../communication/nvm/comm_nvm.h" #include "../../datalayer/datalayer.h" #include "../../include.h" #include "index_html.h" extern bool settingsUpdated; template constexpr auto to_underlying(E e) noexcept { return static_cast>(e); } template std::vector enum_values() { static_assert(std::is_enum_v, "Template argument must be an enum type."); constexpr auto count = to_underlying(EnumType::Highest); std::vector values; for (int i = 1; i < count; ++i) { values.push_back(static_cast(i)); } return values; } template std::vector> enum_values_and_names(Func name_for_type, const EnumType* noneValue = nullptr) { auto values = enum_values(); std::vector> pairs; for (auto& type : values) { auto name = name_for_type(type); if (name != nullptr) { pairs.push_back(std::pair(String(name), type)); } } std::sort(pairs.begin(), pairs.end(), [](const auto& a, const auto& b) { return a.first < b.first; }); if (noneValue) { pairs.insert(pairs.begin(), std::pair(name_for_type(*noneValue), *noneValue)); } return pairs; } template String options_for_enum_with_none(TEnum selected, Func name_for_type, TEnum noneValue) { String options; TEnum none = noneValue; auto values = enum_values_and_names(name_for_type, &none); for (const auto& [name, type] : values) { options += (""; } return options; } template String options_for_enum(TEnum selected, Func name_for_type) { String options; auto values = enum_values_and_names(name_for_type, nullptr); for (const auto& [name, type] : values) { options += (""; } return options; } const char* name_for_button_type(STOP_BUTTON_BEHAVIOR behavior) { switch (behavior) { case STOP_BUTTON_BEHAVIOR::LATCHING_SWITCH: return "Latching"; case STOP_BUTTON_BEHAVIOR::MOMENTARY_SWITCH: return "Momentary"; case STOP_BUTTON_BEHAVIOR::NOT_CONNECTED: return "Not connected"; default: return nullptr; } } String settings_processor(const String& var, BatteryEmulatorSettingsStore& settings) { if (var == "BATTERYINTF") { if (battery) { return battery->interface_name(); } } if (var == "SSID") { return String(ssid.c_str()); } #ifndef COMMON_IMAGE if (var == "COMMONIMAGEDIVCLASS") { return "hidden"; } #endif if (var == "SAVEDCLASS") { if (!settingsUpdated) { return "hidden"; } } if (var == "BATTERY2CLASS") { if (!battery2) { return "hidden"; } } if (var == "BATTERY2INTF") { if (battery2) { return battery2->interface_name(); } } if (var == "INVCLASS") { if (!inverter) { return "hidden"; } } if (var == "SHUNTCLASS") { if (!shunt) { return "hidden"; } } if (var == "CHARGERCLASS") { if (!charger) { return "hidden"; } } if (var == "SHUNTCOMM") { return options_for_enum((comm_interface)settings.getUInt("SHUNTCOMM", (int)comm_interface::CanNative), name_for_comm_interface); } if (var == "BATTTYPE") { return options_for_enum_with_none((BatteryType)settings.getUInt("BATTTYPE", (int)BatteryType::None), name_for_battery_type, BatteryType::None); } if (var == "BATTCOMM") { return options_for_enum((comm_interface)settings.getUInt("BATTCOMM", (int)comm_interface::CanNative), name_for_comm_interface); } if (var == "BATTCHEM") { return options_for_enum((battery_chemistry_enum)settings.getUInt("BATTCHEM", (int)battery_chemistry_enum::NCA), name_for_chemistry); } if (var == "INVTYPE") { return options_for_enum_with_none( (InverterProtocolType)settings.getUInt("INVTYPE", (int)InverterProtocolType::None), name_for_inverter_type, InverterProtocolType::None); } if (var == "INVCOMM") { return options_for_enum((comm_interface)settings.getUInt("INVCOMM", (int)comm_interface::CanNative), name_for_comm_interface); } if (var == "CHGTYPE") { return options_for_enum_with_none((ChargerType)settings.getUInt("CHGTYPE", (int)ChargerType::None), name_for_charger_type, ChargerType::None); } if (var == "CHGCOMM") { return options_for_enum((comm_interface)settings.getUInt("CHGCOMM", (int)comm_interface::CanNative), name_for_comm_interface); } if (var == "EQSTOP") { return options_for_enum_with_none( (STOP_BUTTON_BEHAVIOR)settings.getUInt("EQSTOP", (int)STOP_BUTTON_BEHAVIOR::NOT_CONNECTED), name_for_button_type, STOP_BUTTON_BEHAVIOR::NOT_CONNECTED); } if (var == "BATT2COMM") { return options_for_enum((comm_interface)settings.getUInt("BATT2COMM", (int)comm_interface::CanNative), name_for_comm_interface); } if (var == "DBLBTR") { return settings.getBool("DBLBTR") ? "checked" : ""; } if (var == "CNTCTRL") { return settings.getBool("CNTCTRL") ? "checked" : ""; } if (var == "CNTCTRLDBL") { return settings.getBool("CNTCTRLDBL") ? "checked" : ""; } if (var == "PWMCNTCTRL") { return settings.getBool("PWMCNTCTRL") ? "checked" : ""; } if (var == "PERBMSRESET") { return settings.getBool("PERBMSRESET") ? "checked" : ""; } if (var == "REMBMSRESET") { return settings.getBool("REMBMSRESET") ? "checked" : ""; } if (var == "CANFDASCAN") { return settings.getBool("CANFDASCAN") ? "checked" : ""; } if (var == "WIFIAPENABLED") { return settings.getBool("WIFIAPENABLED") ? "checked" : ""; } if (var == "MQTTENABLED") { return settings.getBool("MQTTENABLED") ? "checked" : ""; } if (var == "MQTTSERVER") { return settings.getString("MQTTSERVER"); } if (var == "MQTTPORT") { return String(settings.getUInt("MQTTPORT", 1883)); } if (var == "MQTTUSER") { return settings.getString("MQTTUSER"); } if (var == "MQTTPASSWORD") { return settings.getString("MQTTPASSWORD"); } if (var == "MQTTTOPICS") { return settings.getBool("MQTTTOPICS") ? "checked" : ""; } if (var == "MQTTTOPIC") { return settings.getString("MQTTTOPIC"); } if (var == "MQTTOBJIDPREFIX") { return settings.getString("MQTTOBJIDPREFIX"); } if (var == "MQTTDEVICENAME") { return settings.getString("MQTTDEVICENAME"); } if (var == "HADEVICEID") { return settings.getString("HADEVICEID"); } if (var == "HADISC") { return settings.getBool("HADISC") ? "checked" : ""; } if (var == "BATTERY_WH_MAX") { return String(datalayer.battery.info.total_capacity_Wh); } if (var == "MAX_CHARGE_SPEED") { return String(datalayer.battery.settings.max_user_set_charge_dA / 10.0, 1); } if (var == "MAX_DISCHARGE_SPEED") { return String(datalayer.battery.settings.max_user_set_discharge_dA / 10.0, 1); } if (var == "SOC_MAX_PERCENTAGE") { return String(datalayer.battery.settings.max_percentage / 100.0, 1); } if (var == "SOC_MIN_PERCENTAGE") { return String(datalayer.battery.settings.min_percentage / 100.0, 1); } if (var == "CHARGE_VOLTAGE") { return String(datalayer.battery.settings.max_user_set_charge_voltage_dV / 10.0, 1); } if (var == "DISCHARGE_VOLTAGE") { return String(datalayer.battery.settings.max_user_set_discharge_voltage_dV / 10.0, 1); } if (var == "SOC_SCALING_ACTIVE_CLASS") { return datalayer.battery.settings.soc_scaling_active ? "active" : "inactive"; } if (var == "VOLTAGE_LIMITS_ACTIVE_CLASS") { return datalayer.battery.settings.user_set_voltage_limits_active ? "active" : "inactive"; } if (var == "SOC_SCALING_CLASS") { return datalayer.battery.settings.soc_scaling_active ? "active" : "inactiveSoc"; } if (var == "SOC_SCALING") { return datalayer.battery.settings.soc_scaling_active ? "✓" : "✕"; } if (var == "FAKE_VOLTAGE_CLASS") { return battery && battery->supports_set_fake_voltage() ? "" : "hidden"; } if (var == "MANUAL_BALANCING_CLASS") { return datalayer.battery.settings.user_requests_balancing ? "" : "inactiveSoc"; } if (var == "MANUAL_BALANCING") { if (datalayer.battery.settings.user_requests_balancing) { return "✓"; } else { return "✕"; } } if (var == "BATTERY_VOLTAGE") { if (battery) { return String(battery->get_voltage(), 1); } } if (var == "VOLTAGE_LIMITS") { if (datalayer.battery.settings.user_set_voltage_limits_active) { return "✓"; } else { return "✕"; } } if (var == "BALANCING_CLASS") { return datalayer.battery.settings.user_requests_balancing ? "active" : "inactive"; } if (var == "BALANCING_MAX_TIME") { return String(datalayer.battery.settings.balancing_time_ms / 60000.0, 1); } if (var == "BAL_POWER") { return String(datalayer.battery.settings.balancing_float_power_W / 1.0, 0); } if (var == "BAL_MAX_PACK_VOLTAGE") { return String(datalayer.battery.settings.balancing_max_pack_voltage_dV / 10.0, 0); } if (var == "BAL_MAX_CELL_VOLTAGE") { return String(datalayer.battery.settings.balancing_max_cell_voltage_mV / 1.0, 0); } if (var == "BAL_MAX_DEV_CELL_VOLTAGE") { return String(datalayer.battery.settings.balancing_max_deviation_cell_voltage_mV / 1.0, 0); } if (var == "CHARGER_CLASS") { if (!charger) { return "hidden"; } } if (var == "CHG_HV_CLASS") { if (datalayer.charger.charger_HV_enabled) { return "active"; } else { return "inactiveSoc"; } } if (var == "CHG_HV") { if (datalayer.charger.charger_HV_enabled) { return "✓"; } else { return "✕"; } } if (var == "CHG_AUX12V_CLASS") { if (datalayer.charger.charger_aux12V_enabled) { return "active"; } else { return "inactiveSoc"; } } if (var == "CHG_AUX12V") { if (datalayer.charger.charger_aux12V_enabled) { return "✓"; } else { return "✕"; } } if (var == "CHG_VOLTAGE_SETPOINT") { return String(datalayer.charger.charger_setpoint_HV_VDC, 1); } if (var == "CHG_CURRENT_SETPOINT") { return String(datalayer.charger.charger_setpoint_HV_IDC, 1); } return String(); } const char* getCANInterfaceName(CAN_Interface interface) { switch (interface) { case CAN_NATIVE: return "CAN"; case CANFD_NATIVE: if (use_canfd_as_can) { return "CAN-FD Native (Classic CAN)"; } else { return "CAN-FD Native"; } case CAN_ADDON_MCP2515: return "Add-on CAN via GPIO MCP2515"; case CANFD_ADDON_MCP2518: if (use_canfd_as_can) { return "Add-on CAN-FD via GPIO MCP2518 (Classic CAN)"; } else { return "Add-on CAN-FD via GPIO MCP2518"; } default: return "UNKNOWN"; } } #define SETTINGS_HTML_SCRIPTS \ R"rawliteral( )rawliteral" #define SETTINGS_STYLE \ R"rawliteral( )rawliteral" #define SETTINGS_HTML_BODY \ R"rawliteral(

SSID: %SSID%

Password: ########

Settings saved. Reboot to take the settings into use.

Battery interface: %BATTERYINTF%

Battery interface: %BATTERY2INTF%

Inverter interface: %INVINTF%

Shunt interface: %SHUNTINTF%

Battery capacity: %BATTERY_WH_MAX% Wh

Rescale SOC: %SOC_SCALING%

SOC max percentage: %SOC_MAX_PERCENTAGE%

SOC min percentage: %SOC_MIN_PERCENTAGE%

Max charge speed: %MAX_CHARGE_SPEED% A

Max discharge speed: %MAX_DISCHARGE_SPEED% A

Manual charge voltage limits: %VOLTAGE_LIMITS%

Target charge voltage: %CHARGE_VOLTAGE% V

Target discharge voltage: %DISCHARGE_VOLTAGE% V

Fake battery voltage: %BATTERY_VOLTAGE% V

Manual LFP balancing: %MANUAL_BALANCING%

Balancing max time: %BAL_MAX_TIME% Minutes

Balancing float power: %BAL_POWER% W

Max battery voltage: %BAL_MAX_PACK_VOLTAGE% V

Max cell voltage: %BAL_MAX_CELL_VOLTAGE% mV

Max cell voltage deviation: %BAL_MAX_DEV_CELL_VOLTAGE% mV

Charger HVDC Enabled: %CHG_HV%

Charger Aux12VDC Enabled: %CHG_AUX12V%

Charger Voltage Setpoint: %CHG_VOLTAGE_SETPOINT% V

Charger Current Setpoint: %CHG_CURRENT_SETPOINT% A

)rawliteral" const char settings_html[] = INDEX_HTML_HEADER COMMON_JAVASCRIPT SETTINGS_STYLE SETTINGS_HTML_BODY SETTINGS_HTML_SCRIPTS INDEX_HTML_FOOTER;