#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 "index_html.h" #include "src/battery/BATTERIES.h" #include "src/inverter/INVERTERS.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 += ("(type)) + "\"" + (selected == type ? " selected" : "") + ">"); options += name; 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 += ("(type)) + "\"" + (selected == type ? " selected" : "") + ">"); options += name; options += ""; } return options; } template String options_from_map(int selected, const TMap& value_name_map) { String options; for (const auto& [value, name] : value_name_map) { options += ""; options += name; options += ""; } return options; } static const std::map led_modes = {{0, "Classic"}, {1, "Energy Flow"}, {2, "Heartbeat"}}; static const std::map tesla_countries = { {21843, "US (USA)"}, {17217, "CA (Canada)"}, {18242, "GB (UK & N Ireland)"}, {17483, "DK (Denmark)"}, {17477, "DE (Germany)"}, {16725, "AU (Australia)"}}; static const std::map tesla_mapregion = { {8, "ME (Middle East)"}, {2, "NONE"}, {3, "CN (China)"}, {6, "TW (Taiwan)"}, {5, "JP (Japan)"}, {0, "US (USA)"}, {7, "KR (Korea)"}, {4, "AU (Australia)"}, {1, "EU (Europe)"}}; static const std::map tesla_chassis = {{0, "Model S"}, {1, "Model X"}, {2, "Model 3"}, {3, "Model Y"}}; static const std::map tesla_pack = {{0, "50 kWh"}, {2, "62 kWh"}, {1, "74 kWh"}, {3, "100 kWh"}}; 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 == "HOSTNAME") { return settings.getString("HOSTNAME"); } 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 == "INVBIDCLASS") { if (!inverter || !inverter->supports_battery_id()) { return "hidden"; } } if (var == "INVBID") { if (inverter && inverter->supports_battery_id()) { return String(datalayer.battery.settings.sofar_user_specified_battery_id); } } if (var == "INVINTF") { if (inverter) { return inverter->interface_name(); } } 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 == "SHUNTTYPE") { return options_for_enum_with_none((ShuntType)settings.getUInt("SHUNTTYPE", (int)ShuntType::None), name_for_shunt_type, ShuntType::None); } if (var == "SHUNTCOMM") { return options_for_enum((comm_interface)settings.getUInt("SHUNTCOMM", (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", wifiap_enabled) ? "checked" : ""; } if (var == "CANLOGUSB") { return settings.getBool("CANLOGUSB") ? "checked" : ""; } if (var == "USBENABLED") { return settings.getBool("USBENABLED") ? "checked" : ""; } if (var == "WEBENABLED") { return settings.getBool("WEBENABLED") ? "checked" : ""; } if (var == "CANLOGSD") { return settings.getBool("CANLOGSD") ? "checked" : ""; } if (var == "SDLOGENABLED") { return settings.getBool("SDLOGENABLED") ? "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 == "MANUAL_BAL_CLASS") { if (battery && battery->supports_manual_balancing()) { return ""; } else { return "hidden"; } } if (var == "BATTPVMAX") { return String(static_cast(settings.getUInt("BATTPVMAX", 0)) / 10.0, 1); } if (var == "BATTPVMIN") { return String(static_cast(settings.getUInt("BATTPVMIN", 0)) / 10.0, 1); } if (var == "BATTCVMAX") { return String(settings.getUInt("BATTCVMAX", 0)); } if (var == "BATTCVMIN") { return String(settings.getUInt("BATTCVMIN", 0)); } 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 == "BMS_RESET_DURATION") { return String(datalayer.battery.settings.user_set_bms_reset_duration_ms / 1000.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); } if (var == "SOFAR_ID") { return String(settings.getUInt("SOFAR_ID", 0)); } if (var == "INVCELLS") { return String(settings.getUInt("INVCELLS", 0)); } if (var == "INVMODULES") { return String(settings.getUInt("INVMODULES", 0)); } if (var == "INVCELLSPER") { return String(settings.getUInt("INVCELLSPER", 0)); } if (var == "INVVLEVEL") { return String(settings.getUInt("INVVLEVEL", 0)); } if (var == "INVCAPACITY") { return String(settings.getUInt("INVCAPACITY", 0)); } if (var == "INVBTYPE") { return String(settings.getUInt("INVBTYPE", 0)); } if (var == "INVICNT") { return settings.getBool("INVICNT") ? "checked" : ""; } if (var == "DIGITALHVIL") { return settings.getBool("DIGITALHVIL") ? "checked" : ""; } if (var == "GTWCOUNTRY") { return options_from_map(settings.getUInt("GTWCOUNTRY", 0), tesla_countries); } if (var == "GTWRHD") { return settings.getBool("GTWRHD") ? "checked" : ""; } if (var == "GTWMAPREG") { return options_from_map(settings.getUInt("GTWMAPREG", 0), tesla_mapregion); } if (var == "GTWCHASSIS") { return options_from_map(settings.getUInt("GTWCHASSIS", 0), tesla_chassis); } if (var == "GTWPACK") { return options_from_map(settings.getUInt("GTWPACK", 0), tesla_pack); } if (var == "LEDMODE") { return options_from_map(settings.getUInt("LEDMODE", 0), led_modes); } 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( Back to main page SSID: %SSID%Edit Password: ######## Edit Battery: %BATTTYPE% Digital HVIL (2024+): Right hand drive: Country code: %GTWCOUNTRY% Map region: %GTWMAPREG% Chassis type: %GTWCHASSIS% Pack type: %GTWPACK% Battery comm I/F: %BATTCOMM% Battery chemistry: %BATTCHEM% Battery max design voltage (V): Battery min design voltage (V): Cell max design voltage (mV): Cell min design voltage (mV): Inverter protocol: %INVTYPE% Inverter comm I/F: %INVCOMM% Sofar Battery ID (0-15): Reported cell count (0 for default): Reported module count (0 for default): Reported cells per module (0 for default): Reported voltage level (0 for default): Reported Ah capacity (0 for default): Reported battery type (in decimal): Inverter should ignore contactors: Charger: %CHGTYPE% Charger comm I/F: %CHGCOMM% Shunt: %SHUNTTYPE% Shunt comm I/F: %SHUNTCOMM% Equipment stop button: %EQSTOP% Double battery: Battery 2 comm I/F: %BATT2COMM% Contactor control: Contactor control double battery: PWM contactor control: Periodic BMS reset: Remote BMS reset: Use CanFD as classic CAN: Enable WiFi AP: Custom hostname: Enable CAN logging via USB serial: Enable logging via USB serial: Enable logging via Webserver: Enable CAN logging via SD card: Enable logging via SD card: Status LED pattern: %LEDMODE% Enable MQTT: MQTT server: MQTT port: MQTT user: MQTT password: Customized MQTT topics: MQTT topic name: Prefix for MQTT object ID: HA device name: HA device ID: Enable Home Assistant auto discovery: Save Settings saved. Reboot to take the settings into use. Reboot Battery interface: %BATTERYINTF% Battery interface: %BATTERY2INTF% Inverter interface: %INVINTF% Shunt interface: %SHUNTINTF% Battery capacity: %BATTERY_WH_MAX% Wh Edit Rescale SOC: %SOC_SCALING% Edit SOC max percentage: %SOC_MAX_PERCENTAGE% Edit SOC min percentage: %SOC_MIN_PERCENTAGE% Edit Max charge speed: %MAX_CHARGE_SPEED% A Edit Max discharge speed: %MAX_DISCHARGE_SPEED% A Edit Manual charge voltage limits: %VOLTAGE_LIMITS% Edit Target charge voltage: %CHARGE_VOLTAGE% V Edit Target discharge voltage: %DISCHARGE_VOLTAGE% V Edit Periodic BMS reset off time: %BMS_RESET_DURATION% s Edit Fake battery voltage: %BATTERY_VOLTAGE% V Edit Manual LFP balancing: %MANUAL_BALANCING% Edit Balancing max time: %BAL_MAX_TIME% Minutes Edit Balancing float power: %BAL_POWER% W Edit Max battery voltage: %BAL_MAX_PACK_VOLTAGE% V Edit Max cell voltage: %BAL_MAX_CELL_VOLTAGE% mV Edit Max cell voltage deviation: %BAL_MAX_DEV_CELL_VOLTAGE% mV Edit Charger HVDC Enabled: %CHG_HV% Edit Charger Aux12VDC Enabled: %CHG_AUX12V% Edit Charger Voltage Setpoint: %CHG_VOLTAGE_SETPOINT% V Edit Charger Current Setpoint: %CHG_CURRENT_SETPOINT% A Edit Factory reset )rawliteral" const char settings_html[] = INDEX_HTML_HEADER COMMON_JAVASCRIPT SETTINGS_STYLE SETTINGS_HTML_BODY SETTINGS_HTML_SCRIPTS INDEX_HTML_FOOTER;
Settings saved. Reboot to take the settings into use.
Reboot