diff --git a/boards/esp32_4M.json b/boards/esp32_4M.json index c2704e19ca..d4b4e90b77 100644 --- a/boards/esp32_4M.json +++ b/boards/esp32_4M.json @@ -5,7 +5,7 @@ "memory_type": "dio_qspi" }, "core": "esp32", - "extra_flags": "-DARDUINO_ESP32_DEV -DBOARD_HAS_PSRAM -DARDUINO_USB_CDC_ON_BOOT=0 -DESP32_4M -DESP32_CLASSIC", + "extra_flags": "-DARDUINO_ESP32_DEV -DARDUINO_USB_CDC_ON_BOOT=0 -DESP32_4M -DESP32_CLASSIC", "f_cpu": "240000000L", "f_flash": "40000000L", "flash_mode": "dio", diff --git a/docs/source/ESPEasy/ESPchips.rst b/docs/source/ESPEasy/ESPchips.rst index 76d9dbe001..4b0754c137 100644 --- a/docs/source/ESPEasy/ESPchips.rst +++ b/docs/source/ESPEasy/ESPchips.rst @@ -651,6 +651,8 @@ To support all modes, we simply need to make several versions - 2 MB (Quad SPI) - ``qio_qspi`` +`Table Source `_ + Build versions: diff --git a/docs/source/Plugin/P077_commands.repl b/docs/source/Plugin/P077_commands.repl index a38b65a56d..db813007e4 100644 --- a/docs/source/Plugin/P077_commands.repl +++ b/docs/source/Plugin/P077_commands.repl @@ -24,3 +24,10 @@ "," Will reset the calibration values to the default values, causing auto-calibration. " + " + ``cseclearpulses`` + + "," + (Added: 2024-01-16) + Will reset the CF-pulse counter. + " diff --git a/lib/ESPEasySerial/Port_ESPEasySerial_USB_HWCDC.cpp b/lib/ESPEasySerial/Port_ESPEasySerial_USB_HWCDC.cpp index 2fded8da43..9d4870f52e 100644 --- a/lib/ESPEasySerial/Port_ESPEasySerial_USB_HWCDC.cpp +++ b/lib/ESPEasySerial/Port_ESPEasySerial_USB_HWCDC.cpp @@ -60,6 +60,10 @@ Port_ESPEasySerial_USB_HWCDC_t::Port_ESPEasySerial_USB_HWCDC_t(const ESPEasySeri if (_hwcdc_serial != nullptr) { _config.rxBuffSize = _hwcdc_serial->setRxBufferSize(_config.rxBuffSize); _config.txBuffSize = _hwcdc_serial->setRxBufferSize(_config.txBuffSize); + + // See: https://github.com/espressif/arduino-esp32/issues/9043 + _hwcdc_serial->setTxTimeoutMs(0); // sets no timeout when trying to write to USB HW CDC + _hwcdc_serial->begin(); // _hwcdc_serial->onEvent(hwcdcEventCallback); diff --git a/platformio_core_defs.ini b/platformio_core_defs.ini index f32d85020f..e0c91185ef 100644 --- a/platformio_core_defs.ini +++ b/platformio_core_defs.ini @@ -208,15 +208,9 @@ lib_ignore = ; ESP_IDF 5.1 [core_esp32_IDF5_1__3_0_0] -;platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.10.12/platform-espressif32.zip -;platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/1787/framework-arduinoespressif32-release_v5.1-f61c914469.zip -;platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.11.11/platform-espressif32.zip -;platform_packages = -;platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/1818/framework-arduinoespressif32-release_v5.1-e5ff26581f.zip -;platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/1846/framework-arduinoespressif32-release_v5.1-29db12e.zip -;platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/1847/framework-arduinoespressif32-release_v5.1-29db12e.zip -platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.12.10/platform-espressif32.zip -platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/1877/framework-arduinoespressif32-release_v5.1-88d1438.zip +platform = https://github.com/tasmota/platform-espressif32/releases/download/2024.01.11/platform-espressif32.zip +platform_packages = +;platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/1917/framework-arduinoespressif32-release_v5.1-d4d5f8a.zip build_flags = -DESP32_STAGE -DESP_IDF_VERSION_MAJOR=5 -DLIBRARIES_NO_LOG=1 diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index 7dc7ea0146..cb558c0f08 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -60,7 +60,9 @@ build_flags = ${core_esp32_IDF5_1__3_0_0.build_flags} -flto=auto -Wswitch -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_NONE - -DLWIP_IPV6 +; -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_INFO +; -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_VERBOSE + -DLWIP_IPV6=1 monitor_filters = esp32_exception_decoder lib_ignore = ${core_esp32_IDF5_1__3_0_0.lib_ignore} diff --git a/platformio_esp32_solo1.ini b/platformio_esp32_solo1.ini index ce4168c35f..2b22fa358d 100644 --- a/platformio_esp32_solo1.ini +++ b/platformio_esp32_solo1.ini @@ -18,7 +18,7 @@ build_unflags = ${esp32_base.build_unflags} ; IDF 5.1.2 [esp32_solo1_common_LittleFS] extends = esp32_base_idf5 -platform_packages = framework-arduino-solo1 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/1878/framework-arduinoespressif32-solo1-release_v5.1-88d1438.zip +platform_packages = framework-arduino-solo1 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/1918/framework-arduinoespressif32-solo1-release_v5.1-d4d5f8a.zip build_flags = ${esp32_base_idf5.build_flags} -DFEATURE_ARDUINO_OTA=1 -DUSE_LITTLEFS diff --git a/platformio_esp32c3_envs.ini b/platformio_esp32c3_envs.ini index bb8162a039..64978d9d35 100644 --- a/platformio_esp32c3_envs.ini +++ b/platformio_esp32c3_envs.ini @@ -47,6 +47,14 @@ extra_scripts = ${esp32c3_common.extra_scripts} pre:tools/pio/pre_custom_esp32.py pre:tools/pio/ir_build_check.py +[env:custom_ESP32c3_4M316k_LittleFS_CDC] +extends = esp32c3_common_LittleFS +board = esp32c3cdc +build_flags = ${esp32c3_common_LittleFS.build_flags} + -DPLUGIN_BUILD_CUSTOM +extra_scripts = ${esp32c3_common_LittleFS.extra_scripts} + pre:tools/pio/pre_custom_esp32.py + [env:normal_ESP32c3_4M316k_CDC] diff --git a/platformio_esp32s2_envs.ini b/platformio_esp32s2_envs.ini index d7ea411f9e..20fdaf4689 100644 --- a/platformio_esp32s2_envs.ini +++ b/platformio_esp32s2_envs.ini @@ -73,6 +73,18 @@ board = esp32s2cdc lib_ignore = ${esp32s2_common.lib_ignore} ${no_ir.lib_ignore} +[env:custom_ESP32s2_4M316k_LittleFS_CDC] +extends = esp32s2_common_LittleFS +board = esp32s2cdc +lib_ignore = ${esp32s2_common_LittleFS.lib_ignore} + ${no_ir.lib_ignore} +build_flags = ${esp32s2_common_LittleFS.build_flags} + -DPLUGIN_BUILD_CUSTOM + -DESP_CONSOLE_USB_CDC=y +extra_scripts = ${esp32s2_common_LittleFS.extra_scripts} + pre:tools/pio/pre_custom_esp32.py + + [env:normal_ESP32s2_4M316k_LittleFS_CDC] extends = esp32s2_common_LittleFS diff --git a/platformio_esp32s3_envs.ini b/platformio_esp32s3_envs.ini index 6ff40a7f29..6267c2ab88 100644 --- a/platformio_esp32s3_envs.ini +++ b/platformio_esp32s3_envs.ini @@ -145,7 +145,7 @@ build_flags = ${esp32s3_common_LittleFS.build_flags} -DFEATURE_ARDUINO_OTA=1 -DPLUGIN_BUILD_CUSTOM -DFEATURE_SD=1 -extra_scripts = ${esp32s3_common.extra_scripts} +extra_scripts = ${esp32s3_common_LittleFS.extra_scripts} pre:tools/pio/pre_custom_esp32.py [env:custom_ESP32s3_8M1M_LittleFS_OPI_PSRAM_CDC] @@ -160,7 +160,7 @@ build_flags = ${esp32s3_common_LittleFS.build_flags} -DFEATURE_ARDUINO_OTA=1 -DPLUGIN_BUILD_MAX_ESP32 -DPLUGIN_BUILD_IR_EXTENDED -extra_scripts = ${esp32_common.extra_scripts} +extra_scripts = ${esp32s3_common_LittleFS.extra_scripts} [env:max_ESP32s3_8M1M_LittleFS_OPI_PSRAM_CDC] @@ -176,7 +176,7 @@ build_flags = ${esp32s3_common_LittleFS.build_flags} -DPLUGIN_BUILD_CUSTOM -DPLUGIN_BUILD_IR_EXTENDED -DFEATURE_SD=1 -extra_scripts = ${esp32s3_common.extra_scripts} +extra_scripts = ${esp32s3_common_LittleFS.extra_scripts} pre:tools/pio/pre_custom_esp32.py [env:custom_ESP32s3_16M8M_LittleFS_OPI_PSRAM_CDC] @@ -192,7 +192,7 @@ build_flags = ${esp32s3_common_LittleFS.build_flags} -DFEATURE_ARDUINO_OTA=1 -DPLUGIN_BUILD_MAX_ESP32 -DPLUGIN_BUILD_IR_EXTENDED -extra_scripts = ${esp32_common.extra_scripts} +extra_scripts = ${esp32s3_common_LittleFS.extra_scripts} [env:max_ESP32s3_16M8M_LittleFS_OPI_PSRAM_CDC] @@ -203,6 +203,6 @@ build_flags = ${esp32s3_common_LittleFS.build_flags} -DFEATURE_ARDUINO_OTA=1 -DPLUGIN_BUILD_MAX_ESP32 -DPLUGIN_BUILD_IR_EXTENDED -extra_scripts = ${esp32_common.extra_scripts} +extra_scripts = ${esp32s3_common_LittleFS.extra_scripts} diff --git a/src/Custom-sample.h b/src/Custom-sample.h index f0528361c3..828a75163c 100644 --- a/src/Custom-sample.h +++ b/src/Custom-sample.h @@ -165,6 +165,14 @@ #define DEFAULT_SYNC_UDP_PORT 8266 // Used for ESPEasy p2p. (IANA registered port: 8266) +// Factory Reset defaults +#define DEFAULT_FACTORY_RESET_KEEP_UNIT_NAME true +#define DEFAULT_FACTORY_RESET_KEEP_WIFI true +#define DEFAULT_FACTORY_RESET_KEEP_NETWORK true +#define DEFAULT_FACTORY_RESET_KEEP_NTP_DST true +#define DEFAULT_FACTORY_RESET_KEEP_CONSOLE_LOG true + + #define BUILD_NO_DEBUG // Custom built-in url for hosting JavaScript and CSS files. diff --git a/src/_C009.cpp b/src/_C009.cpp index 7171bff335..5fe1bbcd2c 100644 --- a/src/_C009.cpp +++ b/src/_C009.cpp @@ -135,19 +135,19 @@ bool do_process_c009_delay_queue(int controller_number, const Queue_element_base // Create nested objects in "ESP": jsonString += to_json_object_value(F("name"), Settings.getName()); jsonString += ','; - jsonString += to_json_object_value(F("unit"), String(Settings.Unit)); + jsonString += to_json_object_value(F("unit"), static_cast(Settings.Unit)); jsonString += ','; - jsonString += to_json_object_value(F("version"), String(Settings.Version)); + jsonString += to_json_object_value(F("version"), static_cast(Settings.Version)); jsonString += ','; - jsonString += to_json_object_value(F("build"), String(Settings.Build)); + jsonString += to_json_object_value(F("build"), static_cast(Settings.Build)); jsonString += ','; jsonString += to_json_object_value(F("build_notes"), F(BUILD_NOTES)); jsonString += ','; jsonString += to_json_object_value(F("build_git"), getValue(LabelType::GIT_BUILD)); jsonString += ','; - jsonString += to_json_object_value(F("node_type_id"), String(NODE_TYPE_ID)); + jsonString += to_json_object_value(F("node_type_id"), static_cast(NODE_TYPE_ID)); jsonString += ','; - jsonString += to_json_object_value(F("sleep"), String(Settings.deepSleep_wakeTime)); + jsonString += to_json_object_value(F("sleep"), static_cast(Settings.deepSleep_wakeTime)); // embed IP, important if there is NAT/PAT // char ipStr[20]; @@ -180,7 +180,7 @@ bool do_process_c009_delay_queue(int controller_number, const Queue_element_base jsonString += ','; jsonString += to_json_object_value(F("valueName"), getTaskValueName(element._taskIndex, x)); jsonString += ','; - jsonString += to_json_object_value(F("type"), String(static_cast(element.sensorType))); + jsonString += to_json_object_value(F("type"), static_cast(element.sensorType)); jsonString += ','; jsonString += to_json_object_value(F("value"), element.txt[x]); } diff --git a/src/src/Commands/Diagnostic.cpp b/src/src/Commands/Diagnostic.cpp index 8ead051dca..98caceacf7 100644 --- a/src/src/Commands/Diagnostic.cpp +++ b/src/src/Commands/Diagnostic.cpp @@ -179,26 +179,17 @@ const __FlashStringHelper * Command_JSONPortStatus(struct EventStruct *event, co void createLogPortStatus(std::map::iterator it) { if (loglevelActiveFor(LOG_LEVEL_INFO)) { - String log; - log += F("PortStatus detail: Port="); - log += getPortFromKey(it->first); - log += F(" State="); - log += it->second.getValue(); - log += F(" Output="); - log += it->second.output; - log += F(" Mode="); - log += it->second.mode; - log += F(" Task="); - log += it->second.task; - log += F(" Monitor="); - log += it->second.monitor; - log += F(" Command="); - log += it->second.command; - log += F(" Init="); - log += it->second.init; - log += F(" PreviousTask="); - log += it->second.previousTask; - addLogMove(LOG_LEVEL_INFO, log); + addLogMove(LOG_LEVEL_INFO, strformat( + F("PortStatus detail: Port=%u State=%d Output=%d Mode=%u Task=%u Monitor=%u Command=%d Init=%d PreviousTask=%d"), + getPortFromKey(it->first), + it->second.getValue(), + it->second.output, + it->second.mode, + it->second.task, + it->second.monitor, + it->second.command, + it->second.init, + it->second.previousTask)); } } diff --git a/src/src/Commands/InternalCommands.cpp b/src/src/Commands/InternalCommands.cpp index 2c56a5bda9..cdeed40df9 100644 --- a/src/src/Commands/InternalCommands.cpp +++ b/src/src/Commands/InternalCommands.cpp @@ -309,6 +309,9 @@ bool InternalCommands::executeInternalCommand() case ESPEasy_cmd_e::i2cscanner: COMMAND_CASE_R(Command_i2c_Scanner, -1); // i2c.h case ESPEasy_cmd_e::inc: COMMAND_CASE_A(Command_Rules_Inc, -1); // Rules.h case ESPEasy_cmd_e::ip: COMMAND_CASE_R(Command_IP, 1); // Network Command +#if FEATURE_USE_IPV6 + case ESPEasy_cmd_e::ip6: COMMAND_CASE_A(Command_show_all_IP6, 0); // Network Command +#endif #ifndef BUILD_NO_DIAGNOSTIC_COMMANDS case ESPEasy_cmd_e::jsonportstatus: COMMAND_CASE_A(Command_JSONPortStatus, -1); // Diagnostic.h #endif // ifndef BUILD_NO_DIAGNOSTIC_COMMANDS diff --git a/src/src/Commands/InternalCommands_decoder.cpp b/src/src/Commands/InternalCommands_decoder.cpp index daf935aaac..b3bdb9e266 100644 --- a/src/src/Commands/InternalCommands_decoder.cpp +++ b/src/src/Commands/InternalCommands_decoder.cpp @@ -87,6 +87,9 @@ const char Internal_commands_ghij[] PROGMEM = "i2cscanner|" "inc|" "ip|" +#if FEATURE_USE_IPV6 + "ip6|" +#endif #ifndef BUILD_NO_DIAGNOSTIC_COMMANDS "jsonportstatus|" #endif // ifndef BUILD_NO_DIAGNOSTIC_COMMANDS diff --git a/src/src/Commands/InternalCommands_decoder.h b/src/src/Commands/InternalCommands_decoder.h index 40504720d1..b052fd0290 100644 --- a/src/src/Commands/InternalCommands_decoder.h +++ b/src/src/Commands/InternalCommands_decoder.h @@ -67,6 +67,9 @@ enum class ESPEasy_cmd_e : uint8_t { i2cscanner, inc, ip, +#if FEATURE_USE_IPV6 + ip6, +#endif #ifndef BUILD_NO_DIAGNOSTIC_COMMANDS jsonportstatus, #endif // ifndef BUILD_NO_DIAGNOSTIC_COMMANDS diff --git a/src/src/Commands/Networks.cpp b/src/src/Commands/Networks.cpp index c5aa1db21b..46c7a8d7e9 100644 --- a/src/src/Commands/Networks.cpp +++ b/src/src/Commands/Networks.cpp @@ -40,6 +40,29 @@ String Command_IP (struct EventStruct *event, const char* Line) return Command_GetORSetIP(event, F("IP:"), Line, Settings.IP, NetworkLocalIP(),1); } +#if FEATURE_USE_IPV6 +String Command_show_all_IP6 (struct EventStruct *event, const char* Line) +{ + // Only get all IPv6 addresses + IP6Addresses_t addresses = NetworkAllIPv6(); + String res; + res += '['; + bool first = true; + for (auto it = addresses.begin(); it != addresses.end(); ++it) + { + if (first) { + first = false; + } else { + res += ','; + } + res += wrap_String(it->toString(true), '"'); + } + res += ']'; + return res; +} +#endif + + String Command_Subnet (struct EventStruct *event, const char* Line) { return Command_GetORSetIP(event, F("Subnet:"), Line, Settings.Subnet, NetworkSubnetMask(), 1); diff --git a/src/src/Commands/Networks.h b/src/src/Commands/Networks.h index f33f045669..3ef0e8302a 100644 --- a/src/src/Commands/Networks.h +++ b/src/src/Commands/Networks.h @@ -9,7 +9,11 @@ String Command_AccessInfo_Clear (struct EventStruct *event, const char* Line); String Command_DNS (struct EventStruct *event, const char* Line); String Command_Gateway (struct EventStruct *event, const char* Line); String Command_IP (struct EventStruct *event, const char* Line); +#if FEATURE_USE_IPV6 +String Command_show_all_IP6 (struct EventStruct *event, const char* Line); +#endif String Command_Subnet (struct EventStruct *event, const char* Line); +#if FEATURE_ETHERNET String Command_ETH_Phy_Addr (struct EventStruct *event, const char* Line); String Command_ETH_Pin_mdc (struct EventStruct *event, const char* Line); String Command_ETH_Pin_mdio (struct EventStruct *event, const char* Line); @@ -22,5 +26,6 @@ String Command_ETH_Subnet (struct EventStruct *event, const char* Line); String Command_ETH_DNS (struct EventStruct *event, const char* Line); String Command_ETH_Wifi_Mode (struct EventStruct *event, const char* Line); String Command_ETH_Disconnect (struct EventStruct *event, const char* Line); +#endif #endif // COMMAND_NETWORKS_H diff --git a/src/src/CustomBuild/ESPEasyDefaults.h b/src/src/CustomBuild/ESPEasyDefaults.h index d4eba68df9..399265aab1 100644 --- a/src/src/CustomBuild/ESPEasyDefaults.h +++ b/src/src/CustomBuild/ESPEasyDefaults.h @@ -374,6 +374,26 @@ #define DEFAULT_SYNC_UDP_PORT 8266 // Used for ESPEasy p2p. (IANA registered port: 8266) #endif + +// Factory Reset defaults +#ifndef DEFAULT_FACTORY_RESET_KEEP_UNIT_NAME +#define DEFAULT_FACTORY_RESET_KEEP_UNIT_NAME true +#endif +#ifndef DEFAULT_FACTORY_RESET_KEEP_WIFI +#define DEFAULT_FACTORY_RESET_KEEP_WIFI true +#endif +#ifndef DEFAULT_FACTORY_RESET_KEEP_NETWORK +#define DEFAULT_FACTORY_RESET_KEEP_NETWORK true +#endif +#ifndef DEFAULT_FACTORY_RESET_KEEP_NTP_DST +#define DEFAULT_FACTORY_RESET_KEEP_NTP_DST true +#endif +#ifndef DEFAULT_FACTORY_RESET_KEEP_CONSOLE_LOG +#define DEFAULT_FACTORY_RESET_KEEP_CONSOLE_LOG true +#endif + + + // --- Defaults to be used for custom automatic provisioning builds ------------------------------------ #if FEATURE_CUSTOM_PROVISIONING #ifndef DEFAULT_FACTORY_DEFAULT_DEVICE_MODEL diff --git a/src/src/CustomBuild/define_plugin_sets.h b/src/src/CustomBuild/define_plugin_sets.h index 26d04c5fc4..01b719a672 100644 --- a/src/src/CustomBuild/define_plugin_sets.h +++ b/src/src/CustomBuild/define_plugin_sets.h @@ -1088,6 +1088,9 @@ To create/register a plugin, you have to : #define PLUGIN_SET_MAX #define CONTROLLER_SET_ALL #define NOTIFIER_SET_ALL + #ifndef TESTING_FEATURE_USE_IPV6 + #define TESTING_FEATURE_USE_IPV6 + #endif #ifndef PLUGIN_ENERGY_COLLECTION #define PLUGIN_ENERGY_COLLECTION #endif @@ -1113,6 +1116,7 @@ To create/register a plugin, you have to : #ifdef FEATURE_CUSTOM_PROVISIONING #undef FEATURE_CUSTOM_PROVISIONING #endif + // FIXME TD-er: Should this be enabled on non-Custom builds??? #define FEATURE_CUSTOM_PROVISIONING 1 @@ -3288,6 +3292,20 @@ To create/register a plugin, you have to : #endif +// Enable dependencies for custom provisioning +// FIXME TD-er: What about using this feature on non-Custom builds???? +#if FEATURE_CUSTOM_PROVISIONING + #ifdef FEATURE_DOWNLOAD + #undef FEATURE_DOWNLOAD + #endif + #define FEATURE_DOWNLOAD 1 + #ifdef FEATURE_SETTINGS_ARCHIVE + #undef FEATURE_SETTINGS_ARCHIVE + #endif + #define FEATURE_SETTINGS_ARCHIVE 1 +#endif + + // TODO TD-er: Test feature, must remove /* diff --git a/src/src/DataStructs/EthernetEventData.cpp b/src/src/DataStructs/EthernetEventData.cpp index 36a40a93fb..1955b8b07b 100644 --- a/src/src/DataStructs/EthernetEventData.cpp +++ b/src/src/DataStructs/EthernetEventData.cpp @@ -169,7 +169,7 @@ void EthernetEventData_t::markConnected() { lastConnectMoment.setNow(); processedConnect = false; #if FEATURE_USE_IPV6 - ETH.enableIpV6(); + ETH.enableIPv6(true); #endif } diff --git a/src/src/DataStructs/FactoryDefaultPref.cpp b/src/src/DataStructs/FactoryDefaultPref.cpp index 8a2d41f8c0..7f79ed2124 100644 --- a/src/src/DataStructs/FactoryDefaultPref.cpp +++ b/src/src/DataStructs/FactoryDefaultPref.cpp @@ -18,12 +18,10 @@ void ResetFactoryDefaultPreference_struct::set(uint32_t preference) // Max. 15 char keys for ESPEasy Factory Default marked keys # define FACTORY_DEFAULT_NVS_PREF_KEY "FacDefPref" -bool ResetFactoryDefaultPreference_struct::init() +bool ResetFactoryDefaultPreference_struct::init(ESPEasy_NVS_Helper& preferences) { - ESPEasy_NVS_Helper nvs_helper; - - if (nvs_helper.begin(F(FACTORY_DEFAULT_NVS_NAMESPACE))) { - return from_NVS(nvs_helper); + if (preferences.begin(F(FACTORY_DEFAULT_NVS_NAMESPACE))) { + return from_NVS(preferences); } return false; } diff --git a/src/src/DataStructs/FactoryDefaultPref.h b/src/src/DataStructs/FactoryDefaultPref.h index 16423d2d41..2188266afe 100644 --- a/src/src/DataStructs/FactoryDefaultPref.h +++ b/src/src/DataStructs/FactoryDefaultPref.h @@ -18,7 +18,7 @@ struct ResetFactoryDefaultPreference_struct { void set(uint32_t preference); #ifdef ESP32 - bool init(); + bool init(ESPEasy_NVS_Helper& preferences); bool from_NVS(ESPEasy_NVS_Helper& preferences); void to_NVS(ESPEasy_NVS_Helper& preferences) const; diff --git a/src/src/DataStructs/FactoryDefault_UnitName_NVS.cpp b/src/src/DataStructs/FactoryDefault_UnitName_NVS.cpp index 08c67b7f94..4068dd1c59 100644 --- a/src/src/DataStructs/FactoryDefault_UnitName_NVS.cpp +++ b/src/src/DataStructs/FactoryDefault_UnitName_NVS.cpp @@ -13,12 +13,16 @@ void FactoryDefault_UnitName_NVS::fromSettings() { bitWrite(data[1], 0, Settings.appendUnitToHostname()); data[0] = Settings.Unit; memcpy((char *)(data + 2), Settings.Name, sizeof(Settings.Name)); + data[2 + sizeof(Settings.Name)] = Settings.UDPPort >> 8; + data[3 + sizeof(Settings.Name)] = Settings.UDPPort & 0xFF; } void FactoryDefault_UnitName_NVS::applyToSettings() const { Settings.appendUnitToHostname(bitRead(data[1], 0)); Settings.Unit = data[0]; memcpy(Settings.Name, (char *)(data + 2), sizeof(Settings.Name)); + + Settings.UDPPort = data[2 + sizeof(Settings.Name)] << 8 | data[3 + sizeof(Settings.Name)]; } bool FactoryDefault_UnitName_NVS::applyToSettings_from_NVS(ESPEasy_NVS_Helper& preferences) { diff --git a/src/src/DataStructs/FactoryDefault_WiFi_NVS.cpp b/src/src/DataStructs/FactoryDefault_WiFi_NVS.cpp index db31abf943..6b40da5175 100644 --- a/src/src/DataStructs/FactoryDefault_WiFi_NVS.cpp +++ b/src/src/DataStructs/FactoryDefault_WiFi_NVS.cpp @@ -45,30 +45,36 @@ void FactoryDefault_WiFi_NVS::applyToSettings() const { Settings.HiddenSSID_SlowConnectPerBSSID(bits.HiddenSSID_SlowConnectPerBSSID); } -bool FactoryDefault_WiFi_NVS::applyToSettings_from_NVS(ESPEasy_NVS_Helper& preferences) { - String tmp; - - if (preferences.getPreference(F(FACTORY_DEFAULT_NVS_SSID1_KEY), tmp)) { - safe_strncpy(SecuritySettings.WifiSSID, tmp, sizeof(SecuritySettings.WifiSSID)); - } +struct FactoryDefault_WiFi_NVS_securityPrefs { + FactoryDefault_WiFi_NVS_securityPrefs(const __FlashStringHelper *pref, + char *dest, + size_t size) + : _pref(pref), _dest(dest), _size(size) {} + + const __FlashStringHelper *_pref; + char *_dest; + size_t _size; +}; + +const FactoryDefault_WiFi_NVS_securityPrefs _WiFi_NVS_securityPrefs_values[] = { + { F(FACTORY_DEFAULT_NVS_SSID1_KEY), SecuritySettings.WifiSSID, sizeof(SecuritySettings.WifiSSID) }, + { F(FACTORY_DEFAULT_NVS_WPA_PASS1_KEY), SecuritySettings.WifiKey, sizeof(SecuritySettings.WifiKey) }, + { F(FACTORY_DEFAULT_NVS_SSID2_KEY), SecuritySettings.WifiSSID2, sizeof(SecuritySettings.WifiSSID2) }, + { F(FACTORY_DEFAULT_NVS_WPA_PASS2_KEY), SecuritySettings.WifiKey2, sizeof(SecuritySettings.WifiKey2) }, + { F(FACTORY_DEFAULT_NVS_AP_PASS_KEY), SecuritySettings.WifiAPKey, sizeof(SecuritySettings.WifiAPKey) } +}; - if (preferences.getPreference(F(FACTORY_DEFAULT_NVS_WPA_PASS1_KEY), tmp)) { - safe_strncpy(SecuritySettings.WifiKey, tmp, sizeof(SecuritySettings.WifiKey)); - } - - if (preferences.getPreference(F(FACTORY_DEFAULT_NVS_SSID2_KEY), tmp)) { - safe_strncpy(SecuritySettings.WifiSSID2, tmp, sizeof(SecuritySettings.WifiSSID2)); - } - if (preferences.getPreference(F(FACTORY_DEFAULT_NVS_WPA_PASS2_KEY), tmp)) { - safe_strncpy(SecuritySettings.WifiKey2, tmp, sizeof(SecuritySettings.WifiKey2)); - } +bool FactoryDefault_WiFi_NVS::applyToSettings_from_NVS(ESPEasy_NVS_Helper& preferences) { + String tmp; + constexpr unsigned nr__WiFi_NVS_securityPrefs_values = NR_ELEMENTS(_WiFi_NVS_securityPrefs_values); - if (preferences.getPreference(F(FACTORY_DEFAULT_NVS_AP_PASS_KEY), tmp)) { - safe_strncpy(SecuritySettings.WifiAPKey, tmp, sizeof(SecuritySettings.WifiAPKey)); + for (unsigned i = 0; i < nr__WiFi_NVS_securityPrefs_values; ++i) { + if (preferences.getPreference(_WiFi_NVS_securityPrefs_values[i]._pref, tmp)) { + safe_strncpy(_WiFi_NVS_securityPrefs_values[i]._dest, tmp, _WiFi_NVS_securityPrefs_values[i]._size); + } } - if (!preferences.getPreference(F(FACTORY_DEFAULT_NVS_WIFI_FLAGS_KEY), data)) { return false; } @@ -82,19 +88,19 @@ void FactoryDefault_WiFi_NVS::fromSettings_to_NVS(ESPEasy_NVS_Helper& preference preferences.setPreference(F(FACTORY_DEFAULT_NVS_WIFI_FLAGS_KEY), data); // Store WiFi credentials - preferences.setPreference(F(FACTORY_DEFAULT_NVS_SSID1_KEY), String(SecuritySettings.WifiSSID)); - preferences.setPreference(F(FACTORY_DEFAULT_NVS_WPA_PASS1_KEY), String(SecuritySettings.WifiKey)); - preferences.setPreference(F(FACTORY_DEFAULT_NVS_SSID2_KEY), String(SecuritySettings.WifiSSID2)); - preferences.setPreference(F(FACTORY_DEFAULT_NVS_WPA_PASS2_KEY), String(SecuritySettings.WifiKey2)); - preferences.setPreference(F(FACTORY_DEFAULT_NVS_AP_PASS_KEY), String(SecuritySettings.WifiAPKey)); + constexpr unsigned nr__WiFi_NVS_securityPrefs_values = NR_ELEMENTS(_WiFi_NVS_securityPrefs_values); + + for (unsigned i = 0; i < nr__WiFi_NVS_securityPrefs_values; ++i) { + preferences.setPreference(_WiFi_NVS_securityPrefs_values[i]._pref, String(_WiFi_NVS_securityPrefs_values[i]._dest)); + } } void FactoryDefault_WiFi_NVS::clear_from_NVS(ESPEasy_NVS_Helper& preferences) { - preferences.remove(F(FACTORY_DEFAULT_NVS_SSID1_KEY)); - preferences.remove(F(FACTORY_DEFAULT_NVS_WPA_PASS1_KEY)); - preferences.remove(F(FACTORY_DEFAULT_NVS_SSID2_KEY)); - preferences.remove(F(FACTORY_DEFAULT_NVS_WPA_PASS2_KEY)); - preferences.remove(F(FACTORY_DEFAULT_NVS_AP_PASS_KEY)); + constexpr unsigned nr__WiFi_NVS_securityPrefs_values = NR_ELEMENTS(_WiFi_NVS_securityPrefs_values); + + for (unsigned i = 0; i < nr__WiFi_NVS_securityPrefs_values; ++i) { + preferences.remove(_WiFi_NVS_securityPrefs_values[i]._pref); + } preferences.remove(F(FACTORY_DEFAULT_NVS_WIFI_FLAGS_KEY)); } diff --git a/src/src/ESPEasyCore/ESPEasyEth.cpp b/src/src/ESPEasyCore/ESPEasyEth.cpp index 66c257a2af..cd2b3eeeeb 100644 --- a/src/src/ESPEasyCore/ESPEasyEth.cpp +++ b/src/src/ESPEasyCore/ESPEasyEth.cpp @@ -162,6 +162,10 @@ bool ETHConnectRelaxed() { (eth_phy_type_t)Settings.ETH_Phy_Type, (eth_clock_mode_t)Settings.ETH_Clock_Mode); #else + #if FEATURE_USE_IPV6 + ETH.enableIPv6(true); + #endif + EthEventData.ethInitSuccess = ETH.begin( (eth_phy_type_t)Settings.ETH_Phy_Type, Settings.ETH_Phy_Addr, diff --git a/src/src/ESPEasyCore/ESPEasyEthEvent.cpp b/src/src/ESPEasyCore/ESPEasyEthEvent.cpp index 149f0ce365..86f7008cc7 100644 --- a/src/src/ESPEasyCore/ESPEasyEthEvent.cpp +++ b/src/src/ESPEasyCore/ESPEasyEthEvent.cpp @@ -52,7 +52,7 @@ void EthEvent(WiFiEvent_t event, arduino_event_info_t info) { ip_event_got_ip6_t * event = static_cast(&info.got_ip6); IPAddress ip(IPv6, (const uint8_t*)event->ip6_info.ip.addr, event->ip6_info.ip.zone); EthEventData.markGotIPv6(ip); - addLog(LOG_LEVEL_INFO, String(F("ETH event: Got IP6")) + ip.toString()); + addLog(LOG_LEVEL_INFO, String(F("ETH event: Got IP6")) + ip.toString(true)); } #else addLog(LOG_LEVEL_INFO, F("ETH event: Got IP6")); diff --git a/src/src/ESPEasyCore/ESPEasyNetwork.cpp b/src/src/ESPEasyCore/ESPEasyNetwork.cpp index 07a8767a31..846cd128f7 100644 --- a/src/src/ESPEasyCore/ESPEasyNetwork.cpp +++ b/src/src/ESPEasyCore/ESPEasyNetwork.cpp @@ -239,7 +239,7 @@ bool IPv6_from_MAC(const MAC_address& mac, IPAddress& ipv6) addLog(LOG_LEVEL_INFO, strformat( F("IPv6_from_MAC: Mac %s IP %s"), mac.toString().c_str(), - ipv6.toString().c_str() + ipv6.toString(true).c_str() )); */ return true; diff --git a/src/src/ESPEasyCore/ESPEasyWiFiEvent.cpp b/src/src/ESPEasyCore/ESPEasyWiFiEvent.cpp index c344bf39e4..95fad54deb 100644 --- a/src/src/ESPEasyCore/ESPEasyWiFiEvent.cpp +++ b/src/src/ESPEasyCore/ESPEasyWiFiEvent.cpp @@ -135,7 +135,7 @@ void WiFiEvent(WiFiEvent_t event, arduino_event_info_t info) { WiFiEventData.markConnected((const char*) ssid_copy, info.connected.bssid, info.connected.channel); #endif #if FEATURE_USE_IPV6 - WiFi.enableIpV6(); + WiFi.enableIPv6(true); #endif break; } @@ -169,7 +169,7 @@ void WiFiEvent(WiFiEvent_t event, arduino_event_info_t info) { ip_event_got_ip6_t * event = static_cast(&info.got_ip6); IPAddress ip(IPv6, (const uint8_t*)event->ip6_info.ip.addr, event->ip6_info.ip.zone); WiFiEventData.markGotIPv6(ip); - addLog(LOG_LEVEL_INFO, String(F("WIFI : STA got IP6 ")) + ip.toString()); + addLog(LOG_LEVEL_INFO, String(F("WIFI : STA got IP6 ")) + ip.toString(true)); break; } case ARDUINO_EVENT_WIFI_AP_GOT_IP6: diff --git a/src/src/ESPEasyCore/ESPEasyWiFiEvent.h b/src/src/ESPEasyCore/ESPEasyWiFiEvent.h index 3625b215b8..dec7dc5d08 100644 --- a/src/src/ESPEasyCore/ESPEasyWiFiEvent.h +++ b/src/src/ESPEasyCore/ESPEasyWiFiEvent.h @@ -11,7 +11,7 @@ // See reported issue: https://github.com/esp8266/Arduino/issues/4114 // ******************************************************************************** #ifdef ESP32 -#include +#include #include #include class WiFi_Access_Static_IP : public WiFiSTAClass { diff --git a/src/src/ESPEasyCore/ESPEasyWifi.cpp b/src/src/ESPEasyCore/ESPEasyWifi.cpp index a4e49d7a06..d41160bd88 100644 --- a/src/src/ESPEasyCore/ESPEasyWifi.cpp +++ b/src/src/ESPEasyCore/ESPEasyWifi.cpp @@ -315,6 +315,7 @@ bool WiFiConnected() { if (!WiFiEventData.wifiConnectAttemptNeeded) { addLog(LOG_LEVEL_INFO, F("WiFi : WiFiConnected(), start AP")); WifiScan(false); + setSTA(false); // Force reset WiFi + reduce power consumption setAP(true); } } @@ -487,7 +488,7 @@ void AttemptWiFiConnect() { const String key = WiFi_AP_CandidatesList::get_key(candidate.index); #if FEATURE_USE_IPV6 - WiFi.IPv6(true); + WiFi.enableIPv6(true); #endif if ((Settings.HiddenSSID_SlowConnectPerBSSID() || !candidate.isHidden) @@ -1217,7 +1218,13 @@ void setAPinternal(bool enable) IPAddress subnet(DEFAULT_AP_SUBNET); if (!WiFi.softAPConfig(apIP, apIP, subnet)) { - addLog(LOG_LEVEL_ERROR, F("WIFI : [AP] softAPConfig failed!")); + addLog(LOG_LEVEL_ERROR, strformat( + ("WIFI : [AP] softAPConfig failed! IP: %s, GW: %s, SN: %s"), + apIP.toString().c_str(), + apIP.toString().c_str(), + subnet.toString().c_str() + ) + ); } int channel = 1; diff --git a/src/src/ESPEasyCore/ESPEasy_backgroundtasks.cpp b/src/src/ESPEasyCore/ESPEasy_backgroundtasks.cpp index 331a6b66d3..f87d942809 100644 --- a/src/src/ESPEasyCore/ESPEasy_backgroundtasks.cpp +++ b/src/src/ESPEasyCore/ESPEasy_backgroundtasks.cpp @@ -56,7 +56,7 @@ void backgroundtasks() lastRunBackgroundTasks = millis(); START_TIMER - #if FEATURE_MDNS + #if FEATURE_MDNS || FEATURE_ESPEASY_P2P const bool networkConnected = NetworkConnected(); #else NetworkConnected(); @@ -82,7 +82,9 @@ void backgroundtasks() web_server.handleClient(); // } #if FEATURE_ESPEASY_P2P - checkUDP(); + if (networkConnected) { + checkUDP(); + } #endif } diff --git a/src/src/ESPEasyCore/ESPEasy_setup.cpp b/src/src/ESPEasyCore/ESPEasy_setup.cpp index 4edc06fc04..c3f3c41e66 100644 --- a/src/src/ESPEasyCore/ESPEasy_setup.cpp +++ b/src/src/ESPEasyCore/ESPEasy_setup.cpp @@ -162,10 +162,14 @@ void ESPEasy_setup() lowestRAM = FreeMem(); #endif // ifndef BUILD_NO_RAM_TRACKER +/* #ifdef ESP32 - ResetFactoryDefaultPreference.init(); +{ + ESPEasy_NVS_Helper preferences; + ResetFactoryDefaultPreference.init(preferences); +} #endif - +*/ #ifndef BUILD_NO_DEBUG // checkAll_internalCommands(); #endif @@ -485,9 +489,7 @@ void ESPEasy_setup() #endif if (loglevelActiveFor(LOG_LEVEL_INFO)) { - String log = F("INIT : Free RAM:"); - log += FreeMem(); - addLogMove(LOG_LEVEL_INFO, log); + addLogMove(LOG_LEVEL_INFO, concat(F("INIT : Free RAM:"), FreeMem())); } # ifndef BUILD_NO_DEBUG diff --git a/src/src/Helpers/ESPEasy_FactoryDefault.cpp b/src/src/Helpers/ESPEasy_FactoryDefault.cpp index 0d9da42b56..3577ca3d6a 100644 --- a/src/src/Helpers/ESPEasy_FactoryDefault.cpp +++ b/src/src/Helpers/ESPEasy_FactoryDefault.cpp @@ -27,21 +27,21 @@ #ifdef ESP32 // Store in NVS partition -#include "../Helpers/ESPEasy_NVS_Helper.h" +# include "../Helpers/ESPEasy_NVS_Helper.h" // Max. 15 char namespace for ESPEasy Factory Default settings # define FACTORY_DEFAULT_NVS_NAMESPACE "ESPEasyFacDef" # include "../Helpers/StringConverter.h" -#include "../DataStructs/FactoryDefaultPref.h" -#include "../DataStructs/FactoryDefault_UnitName_NVS.h" -#include "../DataStructs/FactoryDefault_WiFi_NVS.h" -#include "../DataStructs/FactoryDefault_Network_NVS.h" -#include "../DataStructs/FactoryDefault_LogConsoleSettings_NVS.h" +# include "../DataStructs/FactoryDefaultPref.h" +# include "../DataStructs/FactoryDefault_UnitName_NVS.h" +# include "../DataStructs/FactoryDefault_WiFi_NVS.h" +# include "../DataStructs/FactoryDefault_Network_NVS.h" +# include "../DataStructs/FactoryDefault_LogConsoleSettings_NVS.h" # if FEATURE_ALTERNATIVE_CDN_URL -#include "../DataStructs/FactoryDefault_CDN_customurl_NVS.h" -#endif +# include "../DataStructs/FactoryDefault_CDN_customurl_NVS.h" +# endif // if FEATURE_ALTERNATIVE_CDN_URL #endif // ifdef ESP32 @@ -52,37 +52,72 @@ \*********************************************************************************************/ void ResetFactory(bool formatFS) { - #ifdef ESP32 - ResetFactoryDefaultPreference.init(); - #endif bool mustApplySafebootDefaults = false; + #ifdef ESP32 + ESPEasy_NVS_Helper preferences; + + if (!ResetFactoryDefaultPreference.init(preferences)) { + mustApplySafebootDefaults = true; + } + #endif // ifdef ESP32 + if (ResetFactoryDefaultPreference.getPreference() == 0) { #if FEATURE_CUSTOM_PROVISIONING ResetFactoryDefaultPreference.setDeviceModel(static_cast(DEFAULT_FACTORY_DEFAULT_DEVICE_MODEL)); - ResetFactoryDefaultPreference.fetchRulesTXT(0, DEFAULT_PROVISIONING_FETCH_RULES1); - ResetFactoryDefaultPreference.fetchRulesTXT(1, DEFAULT_PROVISIONING_FETCH_RULES2); - ResetFactoryDefaultPreference.fetchRulesTXT(2, DEFAULT_PROVISIONING_FETCH_RULES3); - ResetFactoryDefaultPreference.fetchRulesTXT(3, DEFAULT_PROVISIONING_FETCH_RULES4); - ResetFactoryDefaultPreference.fetchNotificationDat(DEFAULT_PROVISIONING_FETCH_NOTIFICATIONS); - ResetFactoryDefaultPreference.fetchSecurityDat(DEFAULT_PROVISIONING_FETCH_SECURITY); - ResetFactoryDefaultPreference.fetchConfigDat(DEFAULT_PROVISIONING_FETCH_CONFIG); - ResetFactoryDefaultPreference.fetchProvisioningDat(DEFAULT_PROVISIONING_FETCH_PROVISIONING); - ResetFactoryDefaultPreference.saveURL(DEFAULT_PROVISIONING_SAVE_URL); - ResetFactoryDefaultPreference.storeCredentials(DEFAULT_PROVISIONING_SAVE_CREDENTIALS); -#endif // if FEATURE_CUSTOM_PROVISIONING -#ifdef PLUGIN_BUILD_SAFEBOOT - mustApplySafebootDefaults = true; + #if DEFAULT_PROVISIONING_FETCH_RULES1 + ResetFactoryDefaultPreference.fetchRulesTXT(0, true); + #endif + #if DEFAULT_PROVISIONING_FETCH_RULES2 + ResetFactoryDefaultPreference.fetchRulesTXT(1, true); + #endif + #if DEFAULT_PROVISIONING_FETCH_RULES3 + ResetFactoryDefaultPreference.fetchRulesTXT(2, true); + #endif + #if DEFAULT_PROVISIONING_FETCH_RULES4 + ResetFactoryDefaultPreference.fetchRulesTXT(3, true); + #endif + #if DEFAULT_PROVISIONING_FETCH_NOTIFICATIONS + ResetFactoryDefaultPreference.fetchNotificationDat(true); + #endif + #if DEFAULT_PROVISIONING_FETCH_SECURITY + ResetFactoryDefaultPreference.fetchSecurityDat(true); + #endif + #if DEFAULT_PROVISIONING_FETCH_CONFIG + ResetFactoryDefaultPreference.fetchConfigDat(true); + #endif + #if DEFAULT_PROVISIONING_FETCH_PROVISIONING + ResetFactoryDefaultPreference.fetchProvisioningDat(true); + #endif + #if DEFAULT_PROVISIONING_SAVE_URL + ResetFactoryDefaultPreference.saveURL(true); + #endif + #if DEFAULT_PROVISIONING_SAVE_CREDENTIALS + ResetFactoryDefaultPreference.storeCredentials(true); + #endif + #endif + #if DEFAULT_FACTORY_RESET_KEEP_UNIT_NAME + ResetFactoryDefaultPreference.keepUnitName(true); + #endif + #if DEFAULT_FACTORY_RESET_KEEP_WIFI ResetFactoryDefaultPreference.keepWiFi(true); + #endif + #if DEFAULT_FACTORY_RESET_KEEP_NETWORK ResetFactoryDefaultPreference.keepNetwork(true); - ResetFactoryDefaultPreference.keepUnitName(true); + #endif + #if DEFAULT_FACTORY_RESET_KEEP_NTP_DST + ResetFactoryDefaultPreference.keepNTP(true); + #endif + #if DEFAULT_FACTORY_RESET_KEEP_CONSOLE_LOG ResetFactoryDefaultPreference.keepLogConsoleSettings(true); + #endif +#ifdef PLUGIN_BUILD_SAFEBOOT + mustApplySafebootDefaults = true; ResetFactoryDefaultPreference.keepCustomCdnUrl(true); Settings.UseLastWiFiFromRTC(true); #endif // ifdef PLUGIN_BUILD_SAFEBOOT - } @@ -204,13 +239,13 @@ void ResetFactory(bool formatFS) Settings.UseValueLogger = DEFAULT_USE_SD_LOG; // FIXME TD-er: Must also keep console settings. - Settings.console_serial_port = DEFAULT_CONSOLE_PORT; - Settings.console_serial_rxpin = DEFAULT_CONSOLE_PORT_RXPIN; - Settings.console_serial_txpin = DEFAULT_CONSOLE_PORT_TXPIN; + Settings.console_serial_port = DEFAULT_CONSOLE_PORT; + Settings.console_serial_rxpin = DEFAULT_CONSOLE_PORT_RXPIN; + Settings.console_serial_txpin = DEFAULT_CONSOLE_PORT_TXPIN; Settings.console_serial0_fallback = DEFAULT_CONSOLE_SER0_FALLBACK; - Settings.UseSerial = DEFAULT_USE_SERIAL; - Settings.BaudRate = DEFAULT_SERIAL_BAUD; -} + Settings.UseSerial = DEFAULT_USE_SERIAL; + Settings.BaudRate = DEFAULT_SERIAL_BAUD; + } if (!ResetFactoryDefaultPreference.keepUnitName() || mustApplySafebootDefaults) { Settings.clearUnitNameSettings(); @@ -330,31 +365,30 @@ void ResetFactory(bool formatFS) } #endif // if DEFAULT_CONTROLLER -#ifdef ESP32 +#ifdef ESP32 + if (!mustApplySafebootDefaults) { - ESPEasy_NVS_Helper preferences; - preferences.begin(F(FACTORY_DEFAULT_NVS_NAMESPACE), true); - - if (ResetFactoryDefaultPreference.from_NVS(preferences)) { - Settings.ResetFactoryDefaultPreference = ResetFactoryDefaultPreference.getPreference(); - } + Settings.ResetFactoryDefaultPreference = ResetFactoryDefaultPreference.getPreference(); if (ResetFactoryDefaultPreference.keepUnitName()) { FactoryDefault_UnitName_NVS unitNameNVS{}; unitNameNVS.applyToSettings_from_NVS(preferences); } - if (ResetFactoryDefaultPreference.keepWiFi()) + + if (ResetFactoryDefaultPreference.keepWiFi()) { FactoryDefault_WiFi_NVS wifiNVS{}; wifiNVS.applyToSettings_from_NVS(preferences); } + if (ResetFactoryDefaultPreference.keepNetwork()) { // Restore Network IP settings FactoryDefault_Network_NVS network_nvs; network_nvs.applyToSettings_from_NVS(preferences); } + if (ResetFactoryDefaultPreference.keepLogConsoleSettings()) { // Restore Log and Console settings @@ -362,13 +396,15 @@ void ResetFactory(bool formatFS) log_console_nvs.applyToSettings_from_NVS(preferences); } -#if FEATURE_ALTERNATIVE_CDN_URL +# if FEATURE_ALTERNATIVE_CDN_URL + if (ResetFactoryDefaultPreference.keepCustomCdnUrl()) { FactoryDefault_CDN_customurl_NVS::applyToSettings_from_NVS(preferences); } -#endif +# endif // if FEATURE_ALTERNATIVE_CDN_URL + preferences.end(); } -#endif +#endif // ifdef ESP32 const bool forFactoryReset = true; SaveSettings(forFactoryReset); @@ -399,6 +435,7 @@ void applyFactoryDefaultPref() { ResetFactoryDefaultPreference.to_NVS(preferences); { FactoryDefault_UnitName_NVS unitNameNVS{}; + if (ResetFactoryDefaultPreference.keepUnitName()) { // Store Unit nr and hostname @@ -409,6 +446,7 @@ void applyFactoryDefaultPref() { } { FactoryDefault_WiFi_NVS wifiNVS{}; + if (ResetFactoryDefaultPreference.keepWiFi()) { // Store WiFi credentials @@ -419,6 +457,7 @@ void applyFactoryDefaultPref() { } { FactoryDefault_Network_NVS network_nvs{}; + if (ResetFactoryDefaultPreference.keepNetwork()) { // Store Network IP settings @@ -429,6 +468,7 @@ void applyFactoryDefaultPref() { } { FactoryDefault_LogConsoleSettings_NVS log_console_nvs{}; + if (ResetFactoryDefaultPreference.keepLogConsoleSettings()) { // Store Log and Console settings diff --git a/src/src/Helpers/ESPEasy_NVS_Helper.cpp b/src/src/Helpers/ESPEasy_NVS_Helper.cpp index 8d5df247b5..7b3eecdd55 100644 --- a/src/src/Helpers/ESPEasy_NVS_Helper.cpp +++ b/src/src/Helpers/ESPEasy_NVS_Helper.cpp @@ -29,6 +29,10 @@ void ESPEasy_NVS_Helper::remove(const String& key) bool ESPEasy_NVS_Helper::getPreference(const String& key, String& value) { + if (!_preferences.isKey(key.c_str())) { + return false; + } + value = _preferences.getString(key.c_str()); const bool res = !value.isEmpty(); @@ -50,6 +54,9 @@ void ESPEasy_NVS_Helper::setPreference(const String& key, const String& value) bool ESPEasy_NVS_Helper::getPreference(const String& key, uint32_t& value) { + if (!_preferences.isKey(key.c_str())) { + return false; + } constexpr uint32_t defaultValue = std::numeric_limits::max(); value = _preferences.getUInt(key.c_str(), defaultValue); @@ -75,7 +82,13 @@ void ESPEasy_NVS_Helper::setPreference(const String& key, const uint32_t& value) bool ESPEasy_NVS_Helper::getPreference(const String& key, uint64_t& value) { - constexpr uint64_t defaultValue = std::numeric_limits::max(); + if (!_preferences.isKey(key.c_str())) { + return false; + } + + // Make this a signed value as an erased flash chip only has 0xFF's + // and max uint64 also contains only 0xFF bytes. + constexpr uint64_t defaultValue = std::numeric_limits::max(); value = _preferences.getULong64(key.c_str(), defaultValue); @@ -100,6 +113,10 @@ void ESPEasy_NVS_Helper::setPreference(const String& key, const uint64_t& value) bool ESPEasy_NVS_Helper::getPreference(const String& key, uint8_t *data, size_t length) { + if (!_preferences.isKey(key.c_str())) { + return false; + } + const bool res = _preferences.getBytes(key.c_str(), data, length) == length; addLog(res ? LOG_LEVEL_INFO : LOG_LEVEL_ERROR, concat(F("NVS : Load "), key)); diff --git a/src/src/Helpers/ESPEasy_Storage.cpp b/src/src/Helpers/ESPEasy_Storage.cpp index 1ef649299b..78fef1e533 100644 --- a/src/src/Helpers/ESPEasy_Storage.cpp +++ b/src/src/Helpers/ESPEasy_Storage.cpp @@ -439,11 +439,15 @@ void fileSystemCheck() { clearAllCaches(); if (loglevelActiveFor(LOG_LEVEL_INFO)) { - String log = F("FS : Mount successful, used "); - log += SpiffsUsedBytes(); - log += F(" bytes of "); - log += SpiffsTotalBytes(); - addLogMove(LOG_LEVEL_INFO, log); + addLogMove(LOG_LEVEL_INFO, strformat( + F("FS : " +#ifdef USE_LITTLEFS + "LittleFS" +#else + "SPIFFS" +#endif + " mount successful, used %u bytes of %u"), + SpiffsUsedBytes(), SpiffsTotalBytes())); } // Run garbage collection before any file is open. @@ -654,6 +658,10 @@ String SaveSecuritySettings(bool forFactoryReset) { void afterloadSettings() { ExtraTaskSettings.clear(); // make sure these will not contain old settings. + if ((Settings.Version != VERSION) || (Settings.PID != ESP_PROJECT_PID)) { + // Not valid settings, so do not continue + return; + } // Load ResetFactoryDefaultPreference from provisioning.dat if available. // FIXME TD-er: Must actually move content of Provisioning.dat to NVS and then delete file @@ -662,7 +670,8 @@ void afterloadSettings() { if (pref_temp == 0) { if (ResetFactoryDefaultPreference.getPreference() == 0) { // Try loading from NVS - ResetFactoryDefaultPreference.init(); + ESPEasy_NVS_Helper preferences; + ResetFactoryDefaultPreference.init(preferences); pref_temp = ResetFactoryDefaultPreference.getPreference(); } } @@ -731,9 +740,9 @@ String LoadSettings() #ifndef BUILD_NO_DEBUG if (COMPUTE_STRUCT_CHECKSUM(SettingsStruct, Settings)) { - addLog(LOG_LEVEL_INFO, F("CRC : Settings CRC ...OK")); + addLog(LOG_LEVEL_INFO, concat(F("CRC : Settings CRC"), F("...OK"))); } else{ - addLog(LOG_LEVEL_ERROR, F("CRC : Settings CRC ...FAIL")); + addLog(LOG_LEVEL_ERROR, concat(F("CRC : Settings CRC"), F("...FAIL"))); } #endif } @@ -745,14 +754,14 @@ String LoadSettings() #ifndef BUILD_NO_DEBUG if (SecuritySettings.checksumMatch()) { - addLog(LOG_LEVEL_INFO, F("CRC : SecuritySettings CRC ...OK ")); + addLog(LOG_LEVEL_INFO, concat(F("CRC : SecuritySettings CRC"), F("...OK "))); if (memcmp(SecuritySettings.ProgmemMd5, CRCValues.runTimeMD5, 16) != 0) { addLog(LOG_LEVEL_INFO, F("CRC : binary has changed since last save of Settings")); } } else { - addLog(LOG_LEVEL_ERROR, F("CRC : SecuritySettings CRC ...FAIL")); + addLog(LOG_LEVEL_ERROR, concat(F("CRC : SecuritySettings CRC"), F("...FAIL"))); } #endif @@ -1526,9 +1535,7 @@ String doSaveToFile(const char *fname, int index, const uint8_t *memAddress, int #ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { - String log = F("SaveToFile: free stack: "); - log += getCurrentFreeStack(); - addLogMove(LOG_LEVEL_INFO, log); + addLog(LOG_LEVEL_INFO, concat(F("SaveToFile: free stack: "), getCurrentFreeStack())); } #endif delay(1); diff --git a/src/src/Helpers/Networking.cpp b/src/src/Helpers/Networking.cpp index e6d8eaa997..ec78c72137 100644 --- a/src/src/Helpers/Networking.cpp +++ b/src/src/Helpers/Networking.cpp @@ -305,7 +305,7 @@ void checkUDP() if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { addLogMove(LOG_LEVEL_DEBUG, strformat(F("UDP : %s Command: %s"), - formatIP(remoteIP).c_str(), + formatIP(remoteIP, true).c_str(), wrapWithQuotesIfContainsParameterSeparatorChar(String(&packetBuffer[0])).c_str() )); } @@ -346,7 +346,7 @@ void checkUDP() formatIP(remoteIP).c_str(), received.unit, received.STA_MAC().toString().c_str(), - formatIP(received.IP()).c_str(), + formatIP(received.IP(), true).c_str(), received.unit)); } @@ -1313,6 +1313,7 @@ String getCNonce(const int len) { String s; for (int i = 0; i < len; ++i) { + // FIXME TD-er: Is this "-1" correct? The mod operator makes sure we never reach the sizeof index s += alphanum[rand() % (sizeof(alphanum) - 1)]; } @@ -1350,21 +1351,22 @@ String getDigestAuth(const String& authReq, md5.begin(); md5.add(h1 + ':' + nonce + ':' + String(nc) + ':' + cNonce + F(":auth:") + h2); md5.calculate(); - const String response = md5.toString(); - const String authorization = - String(F("Digest username=\"")) + username + - F("\", realm=\"") + realm + - F("\", nonce=\"") + nonce + - F("\", uri=\"") + uri + - F("\", algorithm=\"MD5\", qop=auth, nc=") + String(nc) + - F(", cnonce=\"") + cNonce + - F("\", response=\"") + response + - '"'; - - // ESPEASY_SERIAL_CONSOLE_PORT.println(authorization); - - return authorization; + // return authorization + return strformat( + F("Digest username=\"%s\"" + ", realm=\"%s\"" + ", nonce=\"%s\"" + ", uri=\"%s\"" + ", algorithm=\"MD5\", qop=auth, nc=%s, cnonce=\"%s\"" + ", response=\"%s\""), + username.c_str(), + realm.c_str(), + nonce.c_str(), + uri.c_str(), + nc, + cNonce.c_str(), + md5.toString().c_str()); // response } #ifndef BUILD_NO_DEBUG @@ -1558,11 +1560,7 @@ int http_authenticate(const String& logIdentifier, if (Settings.UseRules) { // Generate event with the HTTP return code // e.g. http#hostname=401 - String event = F("http#"); - event += host; - event += '='; - event += httpCode; - eventQueue.addMove(std::move(event)); + eventQueue.addMove(strformat(F("http#%s=%d"), host.c_str(), httpCode)); } #ifndef BUILD_NO_DEBUG log_http_result(http, logIdentifier, host + ':' + port, HttpMethod, httpCode, EMPTY_STRING); @@ -1804,6 +1802,7 @@ bool downloadFirmware(const String& url, String& file_save, String& user, String { WiFiClient client; HTTPClient http; + error.clear(); if (!start_downloadFile(client, http, url, file_save, user, pass, error)) { return false; @@ -1820,7 +1819,7 @@ bool downloadFirmware(const String& url, String& file_save, String& user, String // get tcp stream WiFiClient *stream = &client; - while (http.connected() && (len > 0 || len == -1)) { + while (error.isEmpty() && http.connected() && (len > 0 || len == -1)) { // read up to downloadBuffSize at a time. size_t bytes_to_read = downloadBuffSize; @@ -1835,11 +1834,7 @@ bool downloadFirmware(const String& url, String& file_save, String& user, String if (Update.write(buff, c) != c) { error = strformat(F("Error saving firmware update: %s %d Bytes written"), file_save.c_str(), bytesWritten); - addLog(LOG_LEVEL_ERROR, error); - Update.end(); - http.end(); - client.stop(); - return false; + break; } bytesWritten += c; @@ -1848,12 +1843,7 @@ bool downloadFirmware(const String& url, String& file_save, String& user, String if (timeOutReached(timeout)) { error = concat(F("Timeout: "), file_save); - addLog(LOG_LEVEL_ERROR, error); - delay(0); - Update.end(); - http.end(); - client.stop(); - return false; + break; } if (!UseRTOSMultitasking) { @@ -1862,30 +1852,52 @@ bool downloadFirmware(const String& url, String& file_save, String& user, String } backgroundtasks(); } - http.end(); - client.stop(); + } + http.end(); + client.stop(); - if (loglevelActiveFor(LOG_LEVEL_INFO)) { - addLog(LOG_LEVEL_INFO, strformat(F("downloadFile: %s Success"), file_save.c_str())); - } + if (error.isEmpty() && loglevelActiveFor(LOG_LEVEL_INFO)) { + addLog(LOG_LEVEL_INFO, strformat(F("downloadFile: %s Success"), file_save.c_str())); + } - if (Update.end()) { - if (Settings.UseRules) { - eventQueue.addMove(concat(F("ProvisionFirmware#success="), file_save)); - } + uint8_t errorcode = 0; + if (!Update.end()) { + errorcode = Update.getError(); +#ifdef ESP32 + const __FlashStringHelper * err_fstr = F("Unknown"); + switch (errorcode) { + case UPDATE_ERROR_OK: err_fstr = F("OK"); break; + case UPDATE_ERROR_WRITE: err_fstr = F("WRITE"); break; + case UPDATE_ERROR_ERASE: err_fstr = F("ERASE"); break; + case UPDATE_ERROR_READ: err_fstr = F("READ"); break; + case UPDATE_ERROR_SPACE: err_fstr = F("SPACE"); break; + case UPDATE_ERROR_SIZE: err_fstr = F("SIZE"); break; + case UPDATE_ERROR_STREAM: err_fstr = F("STREAM"); break; + case UPDATE_ERROR_MD5: err_fstr = F("MD5"); break; + case UPDATE_ERROR_MAGIC_BYTE: err_fstr = F("MAGIC_BYTE"); break; + case UPDATE_ERROR_ACTIVATE: err_fstr = F("ACTIVATE"); break; + case UPDATE_ERROR_NO_PARTITION: err_fstr = F("NO_PARTITION"); break; + case UPDATE_ERROR_BAD_ARGUMENT: err_fstr = F("BAD_ARGUMENT"); break; + case UPDATE_ERROR_ABORT: err_fstr = F("ABORT"); break; + } + error += concat(F(" Error: "), err_fstr); +#else + error += concat(F(" Error: "), errorcode); +#endif + } else { + if (Settings.UseRules) { + eventQueue.addMove(concat(F("ProvisionFirmware#success="), file_save)); } return true; } - http.end(); - client.stop(); - Update.end(); - error = concat(F("Failed update firmware: "), file_save); - addLog(LOG_LEVEL_ERROR, error); + + backgroundtasks(); + if (loglevelActiveFor(LOG_LEVEL_ERROR)) { + addLog(LOG_LEVEL_ERROR, concat(F("Failed update firmware: "), error)); + } if (Settings.UseRules) { - String event = F("ProvisionFirmware#failed="); - event += file_save; - eventQueue.addMove(std::move(event)); + eventQueue.addMove(concat(F("ProvisionFirmware#failed="), file_save)); } return false; } diff --git a/src/src/Helpers/StringConverter.cpp b/src/src/Helpers/StringConverter.cpp index 3bb0694245..b90229535a 100644 --- a/src/src/Helpers/StringConverter.cpp +++ b/src/src/Helpers/StringConverter.cpp @@ -59,7 +59,7 @@ bool equals(const String& str, const __FlashStringHelper * f_str) { } bool equals(const String& str, const char& c) { - return str.equals(String(c)); + return str.length() == 1 && str[0] == c; } void move_special(String& dest, String&& source) { @@ -199,7 +199,7 @@ bool str2ip(const char *string, uint8_t *IP) return false; } -String formatIP(const IPAddress& ip) { +String formatIP(const IPAddress& ip, bool includeZone) { #ifdef ESP8266 #if defined(ARDUINO_ESP8266_RELEASE_2_3_0) IPAddress tmp(ip); @@ -218,8 +218,12 @@ String formatIP(const IPAddress& ip) { } #endif */ +#if FEATURE_USE_IPV6 + return ip.toString(includeZone); +#else return ip.toString(); #endif +#endif } @@ -630,10 +634,34 @@ String to_json_object_value(const __FlashStringHelper * object, return to_json_object_value(String(object), value, wrapInQuotes); } +String to_json_object_value(const __FlashStringHelper * object, + int value, + bool wrapInQuotes) +{ + return to_json_object_value(String(object), value, wrapInQuotes); +} + +String to_json_object_value(const String& object, + int value, + bool wrapInQuotes) +{ + if (wrapInQuotes) { + return strformat( + F("\"%s\":\"%d\""), + object.c_str(), + value); + } + + return strformat( + F("\"%s\":%d"), + object.c_str(), + value); +} + String to_json_object_value(const String& object, const String& value, bool wrapInQuotes) { return strformat( - F("%s:%s"), - wrap_String(object, '"').c_str(), + F("\"%s\":%s"), + object.c_str(), to_json_value(value, wrapInQuotes).c_str()); } @@ -642,6 +670,18 @@ String to_json_value(const String& value, bool wrapInQuotes) { // Empty string return F("\"\""); } + if (value.length() > 2) { + // Check for JSON objects or arrays + const char firstchar = value[0]; + const char lastchar = value[value.length() - 1]; + if ((firstchar == '[' && lastchar == ']') || + (firstchar == '{' && lastchar == '}')) + { + return value; + } + } + + if (wrapInQuotes || mustConsiderAsJSONString(value)) { // Is not a numerical value, or BIN/HEX notation, thus wrap with quotes @@ -1464,6 +1504,9 @@ bool GetArgv(const char *string, String& argvString, unsigned int argc, char sep bool GetArgvBeginEnd(const char *string, const unsigned int argc, int& pos_begin, int& pos_end, char separator) { pos_begin = -1; pos_end = -1; + if (string == nullptr) { + return false; + } size_t string_len = strlen(string); unsigned int string_pos = 0, argc_pos = 0; bool parenthesis = false; diff --git a/src/src/Helpers/StringConverter.h b/src/src/Helpers/StringConverter.h index 72fdaf18a4..6e7d86d09d 100644 --- a/src/src/Helpers/StringConverter.h +++ b/src/src/Helpers/StringConverter.h @@ -82,7 +82,7 @@ bool str2ip(const String& string, bool str2ip(const char *string, uint8_t *IP); -String formatIP(const IPAddress& ip); +String formatIP(const IPAddress& ip, bool includeZone = false); /********************************************************************************************\ @@ -223,6 +223,14 @@ String to_json_object_value(const String& object, const String& value, bool wrapInQuotes = false); +String to_json_object_value(const __FlashStringHelper * object, + int value, + bool wrapInQuotes = false); + +String to_json_object_value(const String& object, + int value, + bool wrapInQuotes = false); + String to_json_value(const String& value, bool wrapInQuotes = false); diff --git a/src/src/Helpers/StringProvider.cpp b/src/src/Helpers/StringProvider.cpp index 2aeb6e1e24..96c3aa4d42 100644 --- a/src/src/Helpers/StringProvider.cpp +++ b/src/src/Helpers/StringProvider.cpp @@ -436,10 +436,10 @@ String getValue(LabelType::Enum label) { case LabelType::IP_ADDRESS_SUBNET: return strformat(F("%s / %s"), getValue(LabelType::IP_ADDRESS).c_str(), getValue(LabelType::IP_SUBNET).c_str()); case LabelType::GATEWAY: return formatIP(NetworkGatewayIP()); #if FEATURE_USE_IPV6 - case LabelType::IP6_LOCAL: return formatIP(NetworkLocalIP6()); + case LabelType::IP6_LOCAL: return formatIP(NetworkLocalIP6(), true); case LabelType::IP6_GLOBAL: return formatIP(NetworkGlobalIP6()); #if FEATURE_ETHERNET - case LabelType::ETH_IP6_LOCAL: return formatIP(NetworkLocalIP6()); + case LabelType::ETH_IP6_LOCAL: return formatIP(NetworkLocalIP6(), true); #endif /* case LabelType::IP6_ALL_ADDRESSES: @@ -457,7 +457,7 @@ String getValue(LabelType::Enum label) { } */ #endif - case LabelType::CLIENT_IP: return formatIP(web_server.client().remoteIP()); + case LabelType::CLIENT_IP: return formatIP(web_server.client().remoteIP(), true); #if FEATURE_INTERNAL_TEMPERATURE case LabelType::INTERNAL_TEMPERATURE: return toString(getInternalTemperature()); #endif // if FEATURE_INTERNAL_TEMPERATURE @@ -507,7 +507,7 @@ String getValue(LabelType::Enum label) { case LabelType::WAIT_WIFI_CONNECT: return jsonBool(Settings.WaitWiFiConnect()); case LabelType::CONNECT_HIDDEN_SSID: return jsonBool(Settings.IncludeHiddenSSID()); case LabelType::HIDDEN_SSID_SLOW_CONNECT: return jsonBool(Settings.HiddenSSID_SlowConnectPerBSSID()); - case LabelType::SDK_WIFI_AUTORECONNECT: return jsonBool(Settings.WifiNoneSleep()); + case LabelType::SDK_WIFI_AUTORECONNECT: return jsonBool(Settings.SDK_WiFi_autoreconnect()); case LabelType::BUILD_DESC: return getSystemBuildString(); case LabelType::GIT_BUILD: diff --git a/src/src/Helpers/WebServer_commandHelper.cpp b/src/src/Helpers/WebServer_commandHelper.cpp index 2afdd63b08..abf7cd2e8b 100644 --- a/src/src/Helpers/WebServer_commandHelper.cpp +++ b/src/src/Helpers/WebServer_commandHelper.cpp @@ -43,27 +43,41 @@ HandledWebCommand_result handle_command_from_web(EventValueSource::Enum source, eventQueue.addMove(parseStringToEndKeepCase(webrequest, 2)); handledCmd = true; sendOK = true; - } else if (command_e == ESPEasy_cmd_e::taskrun || - command_e == ESPEasy_cmd_e::taskrunat || - command_e == ESPEasy_cmd_e::scheduletaskrun || - command_e == ESPEasy_cmd_e::taskvalueset || - command_e == ESPEasy_cmd_e::taskvaluesetandrun || - command_e == ESPEasy_cmd_e::taskvaluetoggle || - command_e == ESPEasy_cmd_e::let || + } else { + switch (command_e) { + case ESPEasy_cmd_e::taskrun: + case ESPEasy_cmd_e::taskrunat: + case ESPEasy_cmd_e::scheduletaskrun: + case ESPEasy_cmd_e::taskvalueset: + case ESPEasy_cmd_e::taskvaluesetandrun: + case ESPEasy_cmd_e::taskvaluetoggle: + case ESPEasy_cmd_e::let: #ifndef BUILD_NO_DIAGNOSTIC_COMMANDS - command_e == ESPEasy_cmd_e::logportstatus || + case ESPEasy_cmd_e::logportstatus: #endif - command_e == ESPEasy_cmd_e::logentry || + case ESPEasy_cmd_e::logentry: + case ESPEasy_cmd_e::rules: + sendOK = true; + break; #ifndef BUILD_NO_DIAGNOSTIC_COMMANDS - command_e == ESPEasy_cmd_e::jsonportstatus || + case ESPEasy_cmd_e::jsonportstatus: + sendOK = true; + printToWebJSON = true; + break; #endif - command_e == ESPEasy_cmd_e::rules) { - sendOK = true; +#if FEATURE_USE_IPV6 + case ESPEasy_cmd_e::ip6: + sendOK = true; + printToWebJSON = true; + break; +#endif + default: + sendOK = false; + break; + } // handledCmd = true; - } else { - sendOK = false; - } + } if (!handledCmd) { printToWeb = true; handledCmd = ExecuteCommand_internal(source, webrequest.c_str()); @@ -73,11 +87,11 @@ HandledWebCommand_result handle_command_from_web(EventValueSource::Enum source, if (handledCmd) { if (sendOK) { String reply = printWebString.isEmpty() ? F("OK") : printWebString; - removeChar(reply, '\n'); // Don't use newline in JSON. if (printToWebJSON) { + removeChar(reply, '\n'); // Don't use newline in JSON. // Format return string of command to JSON format printWebString = strformat( - F("{\"return\": \"%s\",\"command\": \"%s\"}"), + F("{\"return\": %s,\"command\": %s}"), to_json_value(reply).c_str(), to_json_value(webrequest).c_str()); } else { diff --git a/src/src/Helpers/_CPlugin_DomoticzHelper.cpp b/src/src/Helpers/_CPlugin_DomoticzHelper.cpp index 449c9f6c1e..3413f65067 100644 --- a/src/src/Helpers/_CPlugin_DomoticzHelper.cpp +++ b/src/src/Helpers/_CPlugin_DomoticzHelper.cpp @@ -217,12 +217,12 @@ String serializeDomoticzJson(struct EventStruct *event) String json; { json += '{'; - json += to_json_object_value(F("idx"), String(event->idx)); + json += to_json_object_value(F("idx"), static_cast(event->idx)); json += ','; - json += to_json_object_value(F("RSSI"), String(mapRSSItoDomoticz())); + json += to_json_object_value(F("RSSI"), mapRSSItoDomoticz()); # if FEATURE_ADC_VCC json += ','; - json += to_json_object_value(F("Battery"), String(mapVccToDomoticz())); + json += to_json_object_value(F("Battery"), mapVccToDomoticz()); # endif // if FEATURE_ADC_VCC const Sensor_VType sensorType = event->getSensorType(); @@ -242,7 +242,7 @@ String serializeDomoticzJson(struct EventStruct *event) } } else { json += ','; - json += to_json_object_value(F("nvalue"), F("0")); + json += to_json_object_value(F("nvalue"), 0); json += ','; json += to_json_object_value(F("svalue"), formatDomoticzSensorType(event), true); } diff --git a/src/src/PluginStructs/P077_data_struct.cpp b/src/src/PluginStructs/P077_data_struct.cpp index 6a3bf60bcc..abec1013f4 100644 --- a/src/src/PluginStructs/P077_data_struct.cpp +++ b/src/src/PluginStructs/P077_data_struct.cpp @@ -353,6 +353,10 @@ bool P077_data_struct::plugin_write(struct EventStruct *event, P077_PREF = CSE_PREF_PULSE; success = true; changed = true; + } else if (equals(cmd, F("cseclearpulses"))) { + // Clear the pulses count + last_cf_pulses = 0; + cf_pulses = 0; } else if (equals(cmd, F("csecalibrate"))) { // Set 1 or more calibration values, 0 will skip that value success = true; float CalibVolt = 0.0f; diff --git a/src/src/PluginStructs/P104_data_struct.cpp b/src/src/PluginStructs/P104_data_struct.cpp index 08d8ebafaf..23c3ccfd54 100644 --- a/src/src/PluginStructs/P104_data_struct.cpp +++ b/src/src/PluginStructs/P104_data_struct.cpp @@ -207,63 +207,14 @@ void P104_data_struct::loadSettings() { tmp_int = 0; // WARNING: Order of parsing these values should match the numeric order of P104_OFFSET_* values - - if (validIntFromString(parseString(tmp, 1 + P104_OFFSET_SIZE, P104_FIELD_SEP), tmp_int)) { - zones[zoneIndex].size = tmp_int; - } - - zones[zoneIndex].text = parseStringKeepCaseNoTrim(tmp, 1 + P104_OFFSET_TEXT, P104_FIELD_SEP); - - if (validIntFromString(parseString(tmp, 1 + P104_OFFSET_ALIGNMENT, P104_FIELD_SEP), tmp_int)) { - zones[zoneIndex].alignment = tmp_int; - } - - if (validIntFromString(parseString(tmp, 1 + P104_OFFSET_ANIM_IN, P104_FIELD_SEP), tmp_int)) { - zones[zoneIndex].animationIn = tmp_int; - } - - if (validIntFromString(parseString(tmp, 1 + P104_OFFSET_SPEED, P104_FIELD_SEP), tmp_int)) { - zones[zoneIndex].speed = tmp_int; - } - - if (validIntFromString(parseString(tmp, 1 + P104_OFFSET_ANIM_OUT, P104_FIELD_SEP), tmp_int)) { - zones[zoneIndex].animationOut = tmp_int; - } - - if (validIntFromString(parseString(tmp, 1 + P104_OFFSET_PAUSE, P104_FIELD_SEP), tmp_int)) { - zones[zoneIndex].pause = tmp_int; - } - - if (validIntFromString(parseString(tmp, 1 + P104_OFFSET_FONT, P104_FIELD_SEP), tmp_int)) { - zones[zoneIndex].font = tmp_int; - } - - if (validIntFromString(parseString(tmp, 1 + P104_OFFSET_CONTENT, P104_FIELD_SEP), tmp_int)) { - zones[zoneIndex].content = tmp_int; - } - - if (validIntFromString(parseString(tmp, 1 + P104_OFFSET_LAYOUT, P104_FIELD_SEP), tmp_int)) { - zones[zoneIndex].layout = tmp_int; - } - - if (validIntFromString(parseString(tmp, 1 + P104_OFFSET_SPEC_EFFECT, P104_FIELD_SEP), tmp_int)) { - zones[zoneIndex].specialEffect = tmp_int; - } - - if (validIntFromString(parseString(tmp, 1 + P104_OFFSET_OFFSET, P104_FIELD_SEP), tmp_int)) { - zones[zoneIndex].offset = tmp_int; - } - - if (validIntFromString(parseString(tmp, 1 + P104_OFFSET_BRIGHTNESS, P104_FIELD_SEP), tmp_int)) { - zones[zoneIndex].brightness = tmp_int; - } - - if (validIntFromString(parseString(tmp, 1 + P104_OFFSET_REPEATDELAY, P104_FIELD_SEP), tmp_int)) { - zones[zoneIndex].repeatDelay = tmp_int; - } - - if (validIntFromString(parseString(tmp, 1 + P104_OFFSET_INVERTED, P104_FIELD_SEP), tmp_int)) { - zones[zoneIndex].inverted = tmp_int; + for (uint8_t i = 0; i < P104_OFFSET_COUNT; ++i) { + if (i == P104_OFFSET_TEXT) { + zones[zoneIndex].text = parseStringKeepCaseNoTrim(tmp, 1 + P104_OFFSET_TEXT, P104_FIELD_SEP); + } else { + if (validIntFromString(parseString(tmp, 1 + i, P104_FIELD_SEP), tmp_int)) { + zones[zoneIndex].setIntValue(i, tmp_int); + } + } } delay(0); @@ -750,10 +701,10 @@ void P104_data_struct::displayBarGraph(uint8_t zone, P->setIntensity(zstruct.zone - 1, zstruct.brightness); // don't forget to set the brightness uint8_t row = 0; - if ((barGraphs.size() == 3) || (barGraphs.size() == 5) || (barGraphs.size() == 6)) { // Center within the rows a bit + if ((barGraphs.size() == 3) || (barGraphs.size() == 5) || (barGraphs.size() == 6)) { // Center within the rows a bit for (; row < (barGraphs.size() == 5 ? 2 : 1); row++) { for (uint8_t col = zstruct._lower; col <= zstruct._upper; col++) { - pM->setPoint(row, col, false); // all off + pM->setPoint(row, col, false); // all off if (col % 16 == 0) { delay(0); } } @@ -831,8 +782,6 @@ void P104_data_struct::displayDots(uint8_t zone, if ((nullptr == P) || (nullptr == pM) || dots.isEmpty()) { return; } { uint8_t idx = 0; - int32_t row; - int32_t col; String sRow; String sCol; String sOn_off; @@ -846,6 +795,8 @@ void P104_data_struct::displayDots(uint8_t zone, while (!sRow.isEmpty() && !sCol.isEmpty()) { on_off = true; // Default On + int32_t row; + int32_t col; if (validIntFromString(sRow, row) && validIntFromString(sCol, col) && (row > 0) && ((row - 1) < 8) && @@ -952,6 +903,79 @@ bool isAnimationAvailable(uint8_t animation, bool noneIsAllowed = false) { } } +const char p104_subcommands[] PROGMEM = + "clear" + "|update" + + "|txt" + "|settxt" + +# ifdef P104_USE_BAR_GRAPH + "|bar" + "|setbar" +# endif // ifdef P104_USE_BAR_GRAPH + +# ifdef P104_USE_DOT_SET + "|dot" +# endif // ifdef P104_USE_DOT_SET + +# ifdef P104_USE_COMMANDS + "|alignment" + "|anim.in" + "|anim.out" + "|brightness" + "|content" + "|font" + "|inverted" +# if defined(P104_USE_NUMERIC_DOUBLEHEIGHT_FONT) || defined(P104_USE_FULL_DOUBLEHEIGHT_FONT) + "|layout" +# endif // if defined(P104_USE_NUMERIC_DOUBLEHEIGHT_FONT) || defined(P104_USE_FULL_DOUBLEHEIGHT_FONT) + "|offset" + "|pause" + "|repeat" + "|size" + "|specialeffect" + "|speed" +# endif // ifdef P104_USE_COMMANDS +; + +// Subcommands prefixed by "dotmatrix," +enum class p104_subcommands_e { + clear, // subcommand: clear, / clear[,all] + update, // subcommand: update, / update[,all] + + txt, // subcommand: [set]txt,, (only + settxt, // subcommand: settxt,, (stores + +# ifdef P104_USE_BAR_GRAPH + bar, // subcommand: [set]bar,, (only allowed for zones + setbar, // subcommand: setbar,, (stores the graph-string +# endif // ifdef P104_USE_BAR_GRAPH + +# ifdef P104_USE_DOT_SET + dot, // subcommand: dot,,,[,0][,,[,0]...] to draw +# endif // ifdef P104_USE_DOT_SET + +# ifdef P104_USE_COMMANDS + alignment, // subcommand: alignment,, (0..3) + anim_in, // subcommand: anim.in,, (1..) + anim_out, // subcommand: anim.out,, (0..) + brightness, // subcommand: brightness,, (0..15) + content, // subcommand: content,, (0..-1) + font, // subcommand: font,, (only for incuded font id's) + inverted, // subcommand: inverted,, (disable/enable) +# if defined(P104_USE_NUMERIC_DOUBLEHEIGHT_FONT) || defined(P104_USE_FULL_DOUBLEHEIGHT_FONT) + layout, // subcommand: layout,, (0..2), only when double-height font is available +# endif // if defined(P104_USE_NUMERIC_DOUBLEHEIGHT_FONT) || defined(P104_USE_FULL_DOUBLEHEIGHT_FONT) + offset, // subcommand: offset,, (0..-1) + pause, // subcommand: pause,, (0..P104_MAX_SPEED_PAUSE_VALUE) + repeat, // subcommand: repeat,, (-1..86400 = 24h) + size, // subcommand: size,, (1..) + specialeffect, // subcommand: specialeffect,, (0..3) + speed, // subcommand: speed,, (0..P104_MAX_SPEED_PAUSE_VALUE) +# endif // ifdef P104_USE_COMMANDS +}; + /******************************************************* * handlePluginWrite : process commands ******************************************************/ @@ -964,245 +988,323 @@ bool P104_data_struct::handlePluginWrite(taskIndex_t taskIndex, const String command = parseString(string, 1); if ((nullptr != P) && equals(command, F("dotmatrix"))) { // main command: dotmatrix - const String sub = parseString(string, 2); + const String subCommand = parseString(string, 2); + const int subCommand_i = GetCommandCode(subCommand.c_str(), p104_subcommands); + + if (subCommand_i != -1) { + const p104_subcommands_e subcommands_e = static_cast(subCommand_i); - int32_t zoneIndex{}; - const String string4 = parseStringKeepCaseNoTrim(string, 4); + int32_t zoneIndex{}; + const String string4 = parseStringKeepCaseNoTrim(string, 4); # ifdef P104_USE_COMMANDS - int32_t value4{}; - validIntFromString(string4, value4); + int32_t value4{}; + validIntFromString(string4, value4); # endif // ifdef P104_USE_COMMANDS - // Global subcommands - if (equals(sub, F("clear")) && // subcommand: clear[,all] - (string4.isEmpty() || - string4.equalsIgnoreCase(F("all")))) { - P->displayClear(); - success = true; - } + // Global subcommands - if (equals(sub, F("update")) && // subcommand: update[,all] - (string4.isEmpty() || - string4.equalsIgnoreCase(F("all")))) { - updateZone(0, P104_zone_struct(0)); - success = true; - } + if ((subcommands_e == p104_subcommands_e::clear) && // subcommand: clear[,all] + (string4.isEmpty() || + string4.equalsIgnoreCase(F("all")))) { + P->displayClear(); + success = true; + } else - // Zone-specific subcommands - if (validIntFromString(parseString(string, 3), zoneIndex) && - (zoneIndex > 0) && - (static_cast(zoneIndex) <= zones.size())) { - // subcommands are processed in the same order as they are presented in the UI - for (auto it = zones.begin(); it != zones.end() && !success; ++it) { - if ((it->zone == zoneIndex)) { // This zone - if (equals(sub, F("clear"))) { // subcommand: clear, - P->displayClear(zoneIndex - 1); - success = true; - break; - } + if ((subcommands_e == p104_subcommands_e::update) && // subcommand: update[,all] + (string4.isEmpty() || + string4.equalsIgnoreCase(F("all")))) { + updateZone(0, P104_zone_struct(0)); + success = true; + } - if (equals(sub, F("update"))) { // subcommand: update, - updateZone(zoneIndex, *it); - success = true; - break; - } + // Zone-specific subcommands + if (validIntFromString(parseString(string, 3), zoneIndex) && + (zoneIndex > 0) && + (static_cast(zoneIndex) <= zones.size())) { + // subcommands are processed in the same order as they are presented in the UI + for (auto it = zones.begin(); it != zones.end() && !success; ++it) { + if ((it->zone == zoneIndex)) { // This zone + switch (subcommands_e) { + case p104_subcommands_e::clear: + // subcommand: clear, + { + P->displayClear(zoneIndex - 1); + success = true; + break; + } + + case p104_subcommands_e::update: + // subcommand: update, + { + updateZone(zoneIndex, *it); + success = true; + break; + } # ifdef P104_USE_COMMANDS - if (equals(sub, F("size")) && // subcommand: size,, (1..) - (value4 > 0) && - (value4 <= P104_MAX_MODULES_PER_ZONE)) { - reconfigure = (it->size != value4); - it->size = value4; - success = true; - break; - } + case p104_subcommands_e::size: + // subcommand: size,, (1..) + { + if ((value4 > 0) && + (value4 <= P104_MAX_MODULES_PER_ZONE)) + { + reconfigure = (it->size != value4); + it->size = value4; + success = true; + } + break; + } # endif // ifdef P104_USE_COMMANDS - if ((equals(sub, F("txt")) || // subcommand: [set]txt,, (only - equals(sub, F("settxt"))) && // allowed for zones with Text content) - ((it->content == P104_CONTENT_TEXT) || (it->content == P104_CONTENT_TEXT_REV))) { // no length check, so longer than the UI - // allows is made possible - if (equals(sub, F("settxt")) && // subcommand: settxt,, (stores - (string4.length() <= P104_MAX_TEXT_LENGTH_PER_ZONE)) { // the text in the settings, is not saved) - it->text = string4; // Only if not too long, could 'blow up' the - } // settings when saved - displayOneZoneText(zoneIndex - 1, *it, string4); - success = true; - break; - } + case p104_subcommands_e::txt: // subcommand: [set]txt,, (only + case p104_subcommands_e::settxt: // allowed for zones with Text content) + { + if ((it->content == P104_CONTENT_TEXT) || + (it->content == P104_CONTENT_TEXT_REV)) { // no length check, so longer than the UI allows is made + // possible + if ((subcommands_e == p104_subcommands_e::settxt) && // subcommand: settxt,, (stores + (string4.length() <= P104_MAX_TEXT_LENGTH_PER_ZONE)) { // the text in the settings, is not saved) + it->text = string4; // Only if not too long, could 'blow up' the + } // settings when saved + displayOneZoneText(zoneIndex - 1, *it, string4); + success = true; + } + + break; + } # ifdef P104_USE_COMMANDS - if (equals(sub, F("content")) && // subcommand: content,, (0..-1) - (value4 >= 0) && - (value4 < P104_CONTENT_count)) { - reconfigure = (it->content != value4); - it->content = value4; - success = true; - break; - } - - if (equals(sub, F("alignment")) && // subcommand: alignment,, (0..3) - (value4 >= 0) && - (value4 <= static_cast(textPosition_t::PA_RIGHT))) { // last item in the enum - it->alignment = value4; - success = true; - break; - } - - if (equals(sub, F("anim.in")) && // subcommand: anim.in,, (1..) - isAnimationAvailable(value4)) { - it->animationIn = value4; - success = true; - break; - } - - if (equals(sub, F("speed")) && // subcommand: speed,, (0..P104_MAX_SPEED_PAUSE_VALUE) - (value4 >= 0) && - (value4 <= P104_MAX_SPEED_PAUSE_VALUE)) { - it->speed = value4; - success = true; - break; - } - - if (equals(sub, F("anim.out")) && // subcommand: anim.out,, (0..) - isAnimationAvailable(value4, true)) { - it->animationOut = value4; - success = true; - break; - } - - if (equals(sub, F("pause")) && // subcommand: pause,, (0..P104_MAX_SPEED_PAUSE_VALUE) - (value4 >= 0) && - (value4 <= P104_MAX_SPEED_PAUSE_VALUE)) { - it->pause = value4; - success = true; - break; - } - - if (equals(sub, F("font")) && // subcommand: font,, (only for incuded font id's) - ( - (value4 == 0) + case p104_subcommands_e::content: + // subcommand: content,, (0..-1) + { + if ((value4 >= 0) && + (value4 < P104_CONTENT_count)) + { + reconfigure = (it->content != value4); + it->content = value4; + success = true; + } + break; + } + + case p104_subcommands_e::alignment: + // subcommand: alignment,, (0..3) + { + if ((value4 >= 0) && + (value4 <= static_cast(textPosition_t::PA_RIGHT))) // last item in the enum + { + it->alignment = value4; + success = true; + } + break; + } + + case p104_subcommands_e::anim_in: + // subcommand: anim.in,, (1..) + { + if (isAnimationAvailable(value4)) { + it->animationIn = value4; + success = true; + } + break; + } + + case p104_subcommands_e::speed: + // subcommand: speed,, (0..P104_MAX_SPEED_PAUSE_VALUE) + { + if ((value4 >= 0) && + (value4 <= P104_MAX_SPEED_PAUSE_VALUE)) + { + it->speed = value4; + success = true; + } + break; + } + + case p104_subcommands_e::anim_out: + // subcommand: anim.out,, (0..) + { + if (isAnimationAvailable(value4, true)) + { + it->animationOut = value4; + success = true; + } + break; + } + + case p104_subcommands_e::pause: + // subcommand: pause,, (0..P104_MAX_SPEED_PAUSE_VALUE) + { + if ((value4 >= 0) && + (value4 <= P104_MAX_SPEED_PAUSE_VALUE)) + { + it->pause = value4; + success = true; + } + break; + } + + case p104_subcommands_e::font: + // subcommand: font,, (only for incuded font id's) + { + if ( + (value4 == 0) # ifdef P104_USE_NUMERIC_DOUBLEHEIGHT_FONT - || (value4 == P104_DOUBLE_HEIGHT_FONT_ID) + || (value4 == P104_DOUBLE_HEIGHT_FONT_ID) # endif // ifdef P104_USE_NUMERIC_DOUBLEHEIGHT_FONT # ifdef P104_USE_FULL_DOUBLEHEIGHT_FONT - || (value4 == P104_FULL_DOUBLEHEIGHT_FONT_ID) + || (value4 == P104_FULL_DOUBLEHEIGHT_FONT_ID) # endif // ifdef P104_USE_FULL_DOUBLEHEIGHT_FONT # ifdef P104_USE_VERTICAL_FONT - || (value4 == P104_VERTICAL_FONT_ID) + || (value4 == P104_VERTICAL_FONT_ID) # endif // ifdef P104_USE_VERTICAL_FONT # ifdef P104_USE_EXT_ASCII_FONT - || (value4 == P104_EXT_ASCII_FONT_ID) + || (value4 == P104_EXT_ASCII_FONT_ID) # endif // ifdef P104_USE_EXT_ASCII_FONT # ifdef P104_USE_ARABIC_FONT - || (value4 == P104_ARABIC_FONT_ID) + || (value4 == P104_ARABIC_FONT_ID) # endif // ifdef P104_USE_ARABIC_FONT # ifdef P104_USE_GREEK_FONT - || (value4 == P104_GREEK_FONT_ID) + || (value4 == P104_GREEK_FONT_ID) # endif // ifdef P104_USE_GREEK_FONT # ifdef P104_USE_KATAKANA_FONT - || (value4 == P104_KATAKANA_FONT_ID) + || (value4 == P104_KATAKANA_FONT_ID) # endif // ifdef P104_USE_KATAKANA_FONT - ) - ) { - reconfigure = (it->font != value4); - it->font = value4; - success = true; - break; - } - - if (equals(sub, F("inverted")) && // subcommand: inverted,, (disable/enable) - (value4 >= 0) && - (value4 <= 1)) { - reconfigure = (it->inverted != value4); - it->inverted = value4; - success = true; - break; - } + ) + { + reconfigure = (it->font != value4); + it->font = value4; + success = true; + } + break; + } + + case p104_subcommands_e::inverted: + // subcommand: inverted,, (disable/enable) + { + if ((value4 >= 0) && + (value4 <= 1)) + { + reconfigure = (it->inverted != value4); + it->inverted = value4; + success = true; + } + break; + } # if defined(P104_USE_NUMERIC_DOUBLEHEIGHT_FONT) || defined(P104_USE_FULL_DOUBLEHEIGHT_FONT) - if (equals(sub, F("layout")) && // subcommand: layout,, (0..2), only when double-height font is available - (value4 >= 0) && - (value4 <= P104_LAYOUT_DOUBLE_LOWER)) { - reconfigure = (it->layout != value4); - it->layout = value4; - success = true; - break; - } + case p104_subcommands_e::layout: + // subcommand: layout,, (0..2), only when double-height font is available + { + if ((value4 >= 0) && + (value4 <= P104_LAYOUT_DOUBLE_LOWER)) + { + reconfigure = (it->layout != value4); + it->layout = value4; + success = true; + } + break; + } # endif // if defined(P104_USE_NUMERIC_DOUBLEHEIGHT_FONT) || defined(P104_USE_FULL_DOUBLEHEIGHT_FONT) - if (equals(sub, F("specialeffect")) && // subcommand: specialeffect,, (0..3) - (value4 >= 0) && - (value4 <= P104_SPECIAL_EFFECT_BOTH)) { - reconfigure = (it->specialEffect != value4); - it->specialEffect = value4; - success = true; - break; - } - - if (equals(sub, F("offset")) && // subcommand: offset,, (0..-1) - (value4 >= 0) && - (value4 < P104_MAX_MODULES_PER_ZONE) && - (value4 < it->size)) { - reconfigure = (it->offset != value4); - it->offset = value4; - success = true; - break; - } - - if (equals(sub, F("brightness")) && // subcommand: brightness,, (0..15) - (value4 >= 0) && - (value4 <= P104_BRIGHTNESS_MAX)) { - it->brightness = value4; - P->setIntensity(zoneIndex - 1, it->brightness); // Change brightness immediately - success = true; - break; - } - - if (equals(sub, F("repeat")) && // subcommand: repeat,, (-1..86400 = 24h) - (value4 >= -1) && - (value4 <= P104_MAX_REPEATDELAY_VALUE)) { - it->repeatDelay = value4; - success = true; - - if (it->repeatDelay > -1) { - it->_repeatTimer = millis(); - } - break; - } + case p104_subcommands_e::specialeffect: + // subcommand: specialeffect,, (0..3) + { + if ((value4 >= 0) && + (value4 <= P104_SPECIAL_EFFECT_BOTH)) + { + reconfigure = (it->specialEffect != value4); + it->specialEffect = value4; + success = true; + } + break; + } + + case p104_subcommands_e::offset: + // subcommand: offset,, (0..-1) + { + if ((value4 >= 0) && + (value4 < P104_MAX_MODULES_PER_ZONE) && + (value4 < it->size)) + { + reconfigure = (it->offset != value4); + it->offset = value4; + success = true; + } + break; + } + + case p104_subcommands_e::brightness: + // subcommand: brightness,, (0..15) + { + if ((value4 >= 0) && + (value4 <= P104_BRIGHTNESS_MAX)) + { + it->brightness = value4; + P->setIntensity(zoneIndex - 1, it->brightness); // Change brightness immediately + success = true; + } + break; + } + + case p104_subcommands_e::repeat: + // subcommand: repeat,, (-1..86400 = 24h) + { + if ((value4 >= -1) && + (value4 <= P104_MAX_REPEATDELAY_VALUE)) + { + it->repeatDelay = value4; + success = true; + + if (it->repeatDelay > -1) { + it->_repeatTimer = millis(); + } + } + break; + } # endif // ifdef P104_USE_COMMANDS # ifdef P104_USE_BAR_GRAPH - if ((equals(sub, F("bar")) || // subcommand: [set]bar,, (only allowed for zones - equals(sub, F("setbar"))) && // with Bargraph content) no length check, so longer than the UI - (it->content == P104_CONTENT_BAR_GRAPH)) { // allows is made possible - if (equals(sub, F("setbar")) && // subcommand: setbar,, (stores the graph-string - (string4.length() <= P104_MAX_TEXT_LENGTH_PER_ZONE)) { // in the settings, is not saved) - it->text = string4; // Only if not too long, could 'blow up' the settings when saved - } - displayBarGraph(zoneIndex - 1, *it, string4); - success = true; - break; - } + case p104_subcommands_e::bar: // subcommand: [set]bar,, (only allowed for + // zones + case p104_subcommands_e::setbar: // with Bargraph content) no length check, so longer than the + // UI allows is made possible + { + if (it->content == P104_CONTENT_BAR_GRAPH) { + if ((subcommands_e == p104_subcommands_e::setbar) && // subcommand: setbar,, (stores the + // graph-string + (string4.length() <= P104_MAX_TEXT_LENGTH_PER_ZONE)) { // in the settings, is not saved) + it->text = string4; // Only if not too long, could 'blow up' the settings when + // saved + } + displayBarGraph(zoneIndex - 1, *it, string4); + success = true; + } + break; + } # endif // ifdef P104_USE_BAR_GRAPH # ifdef P104_USE_DOT_SET - if (equals(sub, F("dot"))) { // subcommand: dot,,,[,0][,,[,0]...] to draw - displayDots(zoneIndex - 1, *it, parseStringToEnd(string, 4)); // dots at row/column, add ,0 to turn a dot off - success = true; - break; - } + case p104_subcommands_e::dot: + // subcommand: dot,,,[,0][,,[,0]...] to draw + { + displayDots(zoneIndex - 1, *it, parseStringToEnd(string, 4)); // dots at row/column, add ,0 to turn a dot off + success = true; + break; + } # endif // ifdef P104_USE_DOT_SET + } - // FIXME TD-er: success is always false here. Maybe this must be done outside the for-loop? - if (success) { // Reset the repeat timer - if (it->repeatDelay > -1) { - it->_repeatTimer = millis(); + // FIXME TD-er: success is always false here. Maybe this must be done outside the for-loop? + if (success) { // Reset the repeat timer + if (it->repeatDelay > -1) { + it->_repeatTimer = millis(); + } } } } @@ -1600,23 +1702,18 @@ bool P104_data_struct::saveSettings() { # endif // ifdef P104_DEBUG_DEV zones.push_back(P104_zone_struct(zoneIndex + 1)); - zones[zoneIndex].size = getFormItemIntCustomArgName(index + P104_OFFSET_SIZE); - zones[zoneIndex].text = wrapWithQuotes(webArg(getPluginCustomArgName(index + P104_OFFSET_TEXT))); - zones[zoneIndex].content = getFormItemIntCustomArgName(index + P104_OFFSET_CONTENT); - zones[zoneIndex].alignment = getFormItemIntCustomArgName(index + P104_OFFSET_ALIGNMENT); - zones[zoneIndex].animationIn = getFormItemIntCustomArgName(index + P104_OFFSET_ANIM_IN); - zones[zoneIndex].speed = getFormItemIntCustomArgName(index + P104_OFFSET_SPEED); - zones[zoneIndex].animationOut = getFormItemIntCustomArgName(index + P104_OFFSET_ANIM_OUT); - zones[zoneIndex].pause = getFormItemIntCustomArgName(index + P104_OFFSET_PAUSE); - zones[zoneIndex].font = getFormItemIntCustomArgName(index + P104_OFFSET_FONT); - zones[zoneIndex].layout = getFormItemIntCustomArgName(index + P104_OFFSET_LAYOUT); - zones[zoneIndex].specialEffect = getFormItemIntCustomArgName(index + P104_OFFSET_SPEC_EFFECT); - zones[zoneIndex].offset = getFormItemIntCustomArgName(index + P104_OFFSET_OFFSET); - zones[zoneIndex].inverted = getFormItemIntCustomArgName(index + P104_OFFSET_INVERTED); - - if (zones[zoneIndex].size != 0) { // for newly added zone, use defaults - zones[zoneIndex].brightness = getFormItemIntCustomArgName(index + P104_OFFSET_BRIGHTNESS); - zones[zoneIndex].repeatDelay = getFormItemIntCustomArgName(index + P104_OFFSET_REPEATDELAY); + for (uint8_t i = 0; i < P104_OFFSET_COUNT; ++i) { + // for newly added zone, use defaults + const bool mustCheckSize = + (i == P104_OFFSET_BRIGHTNESS) || + (i == P104_OFFSET_REPEATDELAY); + if (!mustCheckSize || zones[zoneIndex].size != 0) { + if (i == P104_OFFSET_TEXT) { + zones[zoneIndex].text = wrapWithQuotes(webArg(getPluginCustomArgName(index + P104_OFFSET_TEXT))); + } else { + zones[zoneIndex].setIntValue(i, getFormItemIntCustomArgName(index + i)); + } + } } } # ifdef P104_DEBUG_DEV @@ -1656,29 +1753,25 @@ bool P104_data_struct::saveSettings() { saveOffset += sizeof(bufferSize); String zbuffer; + + // 47 total + (max) 100 characters for it->text requires a buffer of ~150 (P104_SETTINGS_BUFFER_V2), but only the required length is + // stored with the length prefixed if (zbuffer.reserve(P104_SETTINGS_BUFFER_V2 + 2)) { for (auto it = zones.begin(); it != zones.end() && error.length() == 0; ++it) { // WARNING: Order of values should match the numeric order of P104_OFFSET_* values - zbuffer = strformat( - F("%u\x01%s\x01%u\x01%u\x01%u\x01%u\x01%u\x01%u\x01%u\x01%u\x01%u\x01%u\x01%d\x01%d\x01%d\x01"), - it->size, // 2 - it->text.c_str(), // 2 + ~15 - it->content, // 1 - it->alignment, // 1 - it->animationIn, // 2 - it->speed, // 5 - it->animationOut, // 2 - it->pause, // 5 - it->font, // 1 - it->layout, // 1 - it->specialEffect, // 1 - it->offset, // 2 - it->brightness, // 2 - it->repeatDelay, // 4 - it->inverted); // 1 - - // 47 total + (max) 100 characters for it->text requires a buffer of ~150 (P104_SETTINGS_BUFFER_V2), but only the required length is - // stored with the length prefixed + zbuffer.clear(); + for (uint8_t i = 0; i < P104_OFFSET_COUNT; ++i) { + if (i == P104_OFFSET_TEXT) { + zbuffer += it->text; + zbuffer += '\x01'; + } else { + int32_t value{}; + if (it->getIntValue(i, value)) { + zbuffer += value; + zbuffer += '\x01'; + } + } + } numDevices += (it->size != 0 ? it->size : 1) + it->offset; // Count corrected for newly added zones @@ -1849,9 +1942,9 @@ bool P104_data_struct::webform_load(struct EventStruct *event) { const String zonetip = F("Select between 1 and " STRINGIFY(P104_MAX_ZONES) " zones, changing" # ifdef P104_USE_ZONE_ORDERING - " Zones or Zone order" + " Zones or Zone order" # endif // ifdef P104_USE_ZONE_ORDERING - " will save and reload the page."); + " will save and reload the page."); # endif // if defined(P104_USE_TOOLTIPS) || defined(P104_ADD_SETTINGS_NOTES) addFormSelector(F("Zones"), F("zonecnt"), P104_MAX_ZONES, zonesList, zonesOptions, nullptr, P104_CONFIG_ZONE_COUNT, true # ifdef P104_USE_TOOLTIPS @@ -1893,9 +1986,8 @@ bool P104_data_struct::webform_load(struct EventStruct *event) { }; - // Append the numeric value as a reference for the 'anim.in' and 'anim.out' subcommands - const __FlashStringHelper * animationTypes[] { + const __FlashStringHelper *animationTypes[] { F("None (0)") , F("Print (1)") , F("Scroll up (2)") @@ -1995,49 +2087,49 @@ bool P104_data_struct::webform_load(struct EventStruct *event) { F("Default (0)") # ifdef P104_USE_NUMERIC_DOUBLEHEIGHT_FONT , F("Numeric, double height (1)") - # endif // ifdef P104_USE_NUMERIC_DOUBLEHEIGHT_FONT + # endif // ifdef P104_USE_NUMERIC_DOUBLEHEIGHT_FONT # ifdef P104_USE_FULL_DOUBLEHEIGHT_FONT , F("Full, double height (2)") - # endif // ifdef P104_USE_FULL_DOUBLEHEIGHT_FONT + # endif // ifdef P104_USE_FULL_DOUBLEHEIGHT_FONT # ifdef P104_USE_VERTICAL_FONT , F("Vertical (3)") - # endif // ifdef P104_USE_VERTICAL_FONT + # endif // ifdef P104_USE_VERTICAL_FONT # ifdef P104_USE_EXT_ASCII_FONT , F("Extended ASCII (4)") # endif // ifdef P104_USE_EXT_ASCII_FONT # ifdef P104_USE_ARABIC_FONT , F("Arabic (5)") - # endif // ifdef P104_USE_ARABIC_FONT + # endif // ifdef P104_USE_ARABIC_FONT # ifdef P104_USE_GREEK_FONT , F("Greek (6)") - # endif // ifdef P104_USE_GREEK_FONT + # endif // ifdef P104_USE_GREEK_FONT # ifdef P104_USE_KATAKANA_FONT , F("Katakana (7)") - # endif // ifdef P104_USE_KATAKANA_FONT + # endif // ifdef P104_USE_KATAKANA_FONT }; const int fontOptions[] = { P104_DEFAULT_FONT_ID # ifdef P104_USE_NUMERIC_DOUBLEHEIGHT_FONT , P104_DOUBLE_HEIGHT_FONT_ID - # endif // ifdef P104_USE_NUMERIC_DOUBLEHEIGHT_FONT + # endif // ifdef P104_USE_NUMERIC_DOUBLEHEIGHT_FONT # ifdef P104_USE_FULL_DOUBLEHEIGHT_FONT , P104_FULL_DOUBLEHEIGHT_FONT_ID - # endif // ifdef P104_USE_FULL_DOUBLEHEIGHT_FONT + # endif // ifdef P104_USE_FULL_DOUBLEHEIGHT_FONT # ifdef P104_USE_VERTICAL_FONT , P104_VERTICAL_FONT_ID - # endif // ifdef P104_USE_VERTICAL_FONT + # endif // ifdef P104_USE_VERTICAL_FONT # ifdef P104_USE_EXT_ASCII_FONT , P104_EXT_ASCII_FONT_ID # endif // ifdef P104_USE_EXT_ASCII_FONT # ifdef P104_USE_ARABIC_FONT , P104_ARABIC_FONT_ID - # endif // ifdef P104_USE_ARABIC_FONT + # endif // ifdef P104_USE_ARABIC_FONT # ifdef P104_USE_GREEK_FONT , P104_GREEK_FONT_ID - # endif // ifdef P104_USE_GREEK_FONT + # endif // ifdef P104_USE_GREEK_FONT # ifdef P104_USE_KATAKANA_FONT , P104_KATAKANA_FONT_ID - # endif // ifdef P104_USE_KATAKANA_FONT + # endif // ifdef P104_USE_KATAKANA_FONT }; const __FlashStringHelper *layoutTypes[] = { @@ -2127,18 +2219,31 @@ bool P104_data_struct::webform_load(struct EventStruct *event) { { html_table(EMPTY_STRING); // Sub-table - html_table_header(F("Zone # ")); - html_table_header(F("Modules")); - html_table_header(F("Text"), 180); - html_table_header(F("Content")); - html_table_header(F("Alignment")); - html_table_header(F("Animation In/Out")); // 1st and 2nd row title - html_table_header(F("Speed/Pause")); // 1st and 2nd row title - html_table_header(F("Font/Layout")); // 1st and 2nd row title - html_table_header(F("Inverted/ Special Effects")); // 1st and 2nd row title - html_table_header(F("Offset")); - html_table_header(F("Brightness")); - html_table_header(F("Repeat (sec)")); + + const __FlashStringHelper *headers[] = { + F("Zone # "), + F("Modules"), + F("Text"), + F("Content"), + F("Alignment"), + F("Animation In/Out"), // 1st and 2nd row title + F("Speed/Pause"), // 1st and 2nd row title + F("Font/Layout"), // 1st and 2nd row title + F("Inverted/ Special Effects"), // 1st and 2nd row title + F("Offset"), + F("Brightness"), + F("Repeat (sec)") + }; + + constexpr unsigned nrHeaders = NR_ELEMENTS(headers); + for (unsigned i = 0; i < nrHeaders; ++i) { + int width = 0; + if (i == 2) { + // "Text" needs a width + width = 180; + } + html_table_header(headers[i], width); + } # ifdef P104_USE_ZONE_ACTIONS html_table_header(F(""), 15); // Spacer html_table_header(F("Action"), 45); @@ -2284,10 +2389,10 @@ bool P104_data_struct::webform_load(struct EventStruct *event) { // Split here html_TR_TD(); // Start new row - html_TD(4); // Start with some blank columns + html_TD(4); // Start with some blank columns { - html_TD(); // Animation Out + html_TD(); // Animation Out addSelector(getPluginCustomArgName(index + P104_OFFSET_ANIM_OUT), animationCount, animationTypes, @@ -2432,4 +2537,62 @@ bool P104_data_struct::webform_save(struct EventStruct *event) { return result; } + + + +P104_zone_struct::P104_zone_struct(uint8_t _zone) + : text(F("\"\"")), zone(_zone) {} + + +bool P104_zone_struct::getIntValue(uint8_t offset, int32_t& value) const +{ + switch (offset) { + case P104_OFFSET_SIZE: value = size; break; + case P104_OFFSET_TEXT: return false; + case P104_OFFSET_CONTENT: value = content; break; + case P104_OFFSET_ALIGNMENT: value = alignment; break; + case P104_OFFSET_ANIM_IN: value = animationIn; break; + case P104_OFFSET_SPEED: value = speed; break; + case P104_OFFSET_ANIM_OUT: value = animationOut; break; + case P104_OFFSET_PAUSE: value = pause; break; + case P104_OFFSET_FONT: value = font; break; + case P104_OFFSET_LAYOUT: value = layout; break; + case P104_OFFSET_SPEC_EFFECT: value = specialEffect; break; + case P104_OFFSET_OFFSET: value = offset; break; + case P104_OFFSET_BRIGHTNESS: value = brightness; break; + case P104_OFFSET_REPEATDELAY: value = repeatDelay; break; + case P104_OFFSET_INVERTED: value = inverted; break; + + default: + return false; + } + return true; +} + +bool P104_zone_struct::setIntValue(uint8_t offset, int32_t value) +{ + switch (offset) { + case P104_OFFSET_SIZE: size = value; break; + case P104_OFFSET_TEXT: return false; + case P104_OFFSET_CONTENT: content = value; break; + case P104_OFFSET_ALIGNMENT: alignment = value; break; + case P104_OFFSET_ANIM_IN: animationIn = value; break; + case P104_OFFSET_SPEED: speed = value; break; + case P104_OFFSET_ANIM_OUT: animationOut = value; break; + case P104_OFFSET_PAUSE: pause = value; break; + case P104_OFFSET_FONT: font = value; break; + case P104_OFFSET_LAYOUT: layout = value; break; + case P104_OFFSET_SPEC_EFFECT: specialEffect = value; break; + case P104_OFFSET_OFFSET: offset = value; break; + case P104_OFFSET_BRIGHTNESS: brightness = value; break; + case P104_OFFSET_REPEATDELAY: repeatDelay = value; break; + case P104_OFFSET_INVERTED: inverted = value; break; + + default: + return false; + } + return true; +} + + #endif // ifdef USES_P104 diff --git a/src/src/PluginStructs/P104_data_struct.h b/src/src/PluginStructs/P104_data_struct.h index b36ea6a588..78c7b9a3e9 100644 --- a/src/src/PluginStructs/P104_data_struct.h +++ b/src/src/PluginStructs/P104_data_struct.h @@ -306,7 +306,7 @@ struct P104_zone_struct { P104_zone_struct() = delete; // Not used, so leave out explicitly - P104_zone_struct(uint8_t _zone) : text(F("\"\"")), zone(_zone) {} + P104_zone_struct(uint8_t _zone); String text; int32_t repeatDelay = -1; @@ -331,6 +331,10 @@ struct P104_zone_struct { uint16_t _upper = 0u; // lower and upper pixel numbers uint8_t _startModule = 0u; // starting module, end module is _startModule + size - 1 # endif // if defined(P104_USE_BAR_GRAPH) || defined(P104_USE_DOT_SET) + + // Used to loop over member values + bool getIntValue(uint8_t offset, int32_t& value) const; + bool setIntValue(uint8_t offset, int32_t value); }; # ifdef P104_USE_BAR_GRAPH diff --git a/src/src/PluginStructs/P128_data_struct.cpp b/src/src/PluginStructs/P128_data_struct.cpp index b9533fd24b..65bc1c9c13 100644 --- a/src/src/PluginStructs/P128_data_struct.cpp +++ b/src/src/PluginStructs/P128_data_struct.cpp @@ -43,22 +43,98 @@ bool P128_data_struct::plugin_read(struct EventStruct *event) { # ifndef LIMIT_BUILD_SIZE if (loglevelActiveFor(LOG_LEVEL_INFO)) { - String log; - log.reserve(64); - log = F("Lights: mode: "); - log += P128_modeType_toString(mode); - log += F(" lastmode: "); - log += P128_modeType_toString(savemode); - log += F(" fadetime: "); - log += (int)UserVar[event->BaseVarIndex + 2]; - log += F(" fadedelay: "); - log += (int)UserVar[event->BaseVarIndex + 3]; - addLogMove(LOG_LEVEL_INFO, log); + addLogMove(LOG_LEVEL_INFO, strformat( + F("Lights: mode: %s lastmode: %s fadetime: %d fadedelay: %d"), + String(P128_modeType_toString(mode)).c_str(), + String(P128_modeType_toString(savemode)).c_str(), + (int)UserVar[event->BaseVarIndex + 2], + (int)UserVar[event->BaseVarIndex + 3])); } # endif // ifndef LIMIT_BUILD_SIZE return true; } +const char neopixelfx_subcommands[] PROGMEM = + "all" + "|bgcolor" + "|colorfade" + "|comet" + "|count" + "|dim" + "|dualscan" + "|dualwipe" + "|fade" + "|fadedelay" + "|fadetime" +# if P128_ENABLE_FAKETV + "|faketv" +# endif // if P128_ENABLE_FAKETV + "|fire" + "|fireflicker" + "|hsv" + "|hsvline" + "|hsvone" + "|kitt" + "|line" + "|off" + "|on" + "|one" + "|rainbow" + "|rgb" + "|scan" + "|simpleclock" + "|sparkle" + "|speed" + "|statusrequest" + "|stop" + "|theatre" + "|tick" + "|twinkle" + "|twinklefade" + "|wipe"; + + +enum class neopixelfx_subcommands_e { + all, + bgcolor, + colorfade, + comet, + count, + dim, + dualscan, + dualwipe, + fade, + fadedelay, + fadetime, +# if P128_ENABLE_FAKETV + faketv, +# endif // if P128_ENABLE_FAKETV + fire, + fireflicker, + hsv, + hsvline, + hsvone, + kitt, + line, + off, + on, + one, + rainbow, + rgb, + scan, + simpleclock, + sparkle, + speed, + statusrequest, + stop, + theatre, + tick, + twinkle, + twinklefade, + wipe +}; + + bool P128_data_struct::plugin_write(struct EventStruct *event, const String & string) { bool success = false; @@ -68,632 +144,609 @@ bool P128_data_struct::plugin_write(struct EventStruct *event, if ((equals(command, F("neopixelfx"))) || (equals(command, F("nfx")))) { const String subCommand = parseString(string, 2); - const String str3 = parseString(string, 3); - const int32_t str3i = event->Par2; - const String str4 = parseString(string, 4); - const int32_t str4i = event->Par3; - const String str5 = parseString(string, 5); - const int32_t str5i = event->Par4; - const String str6 = parseString(string, 6); - const int32_t str6i = event->Par5; - const String str7 = parseString(string, 7); - const int32_t str7i = str7.toInt(); - - const bool command_is_on = (equals(subCommand, F("on"))); - const bool command_is_off = (equals(subCommand, F("off"))); - - if (equals(subCommand, F("fadetime"))) { - success = true; - fadetime = str3i; - } + const int subCommand_i = GetCommandCode(subCommand.c_str(), neopixelfx_subcommands); - else if (equals(subCommand, F("fadedelay"))) { - success = true; - fadedelay = str3i; - } + if (subCommand_i != -1) { + const String str3 = parseString(string, 3); + const int32_t str3i = event->Par2; + const String str4 = parseString(string, 4); + const int32_t str4i = event->Par3; + const String str5 = parseString(string, 5); + const int32_t str5i = event->Par4; + const String str6 = parseString(string, 6); + const int32_t str6i = event->Par5; + const String str7 = parseString(string, 7); + const int32_t str7i = str7.toInt(); - else if (equals(subCommand, F("speed"))) { - success = true; - defaultspeed = str3i; - speed = defaultspeed; - } - else if (equals(subCommand, F("bgcolor"))) { - success = true; - hex2rrggbb(str3); - } + const neopixelfx_subcommands_e subcommands_e = static_cast(subCommand_i); - else if (equals(subCommand, F("count"))) { success = true; - count = str3i; - } - else if (command_is_on || command_is_off) { - success = true; - fadetime = str3.isEmpty() + switch (subcommands_e) { + case neopixelfx_subcommands_e::fadetime: + { + fadetime = str3i; + break; + } + + case neopixelfx_subcommands_e::fadedelay: + { + fadedelay = str3i; + break; + } + + case neopixelfx_subcommands_e::speed: + { + defaultspeed = str3i; + speed = defaultspeed; + break; + } + + case neopixelfx_subcommands_e::bgcolor: + { + hex2rrggbb(str3); + break; + } + + case neopixelfx_subcommands_e::count: + { + count = str3i; + break; + } + + case neopixelfx_subcommands_e::on: + case neopixelfx_subcommands_e::off: + { + fadetime = str3.isEmpty() ? 1000 : str3i; - fadedelay = str4.isEmpty() + fadedelay = str4.isEmpty() ? 0 : str4i; - for (int pixel = 0; pixel < pixelCount; pixel++) { - r_pixel = (fadedelay < 0) + for (int pixel = 0; pixel < pixelCount; pixel++) { + r_pixel = (fadedelay < 0) ? pixelCount - pixel - 1 : pixel; - starttime[r_pixel] = counter20ms + (pixel * abs(fadedelay) / 20); - - if ((command_is_on) && (mode == P128_modetype::Off)) { // switch on - rgb_target[pixel] = rgb_old[pixel]; - rgb_old[pixel] = Plugin_128_pixels->GetPixelColor(pixel); - } else if (command_is_off) { // switch off - rgb_old[pixel] = Plugin_128_pixels->GetPixelColor(pixel); - rgb_target[pixel] = RgbColor(0); + starttime[r_pixel] = counter20ms + (pixel * abs(fadedelay) / 20); + + if (subcommands_e == neopixelfx_subcommands_e::off) { + rgb_old[pixel] = Plugin_128_pixels->GetPixelColor(pixel); + rgb_target[pixel] = RgbColor(0); + } else if (mode == P128_modetype::Off) { // switch on + rgb_target[pixel] = rgb_old[pixel]; + rgb_old[pixel] = Plugin_128_pixels->GetPixelColor(pixel); + } + } + + if (subcommands_e == neopixelfx_subcommands_e::off) { + savemode = mode; + mode = P128_modetype::Fade; + } else if (mode == P128_modetype::Off) { + // switch on + mode = (savemode == P128_modetype::On) ? P128_modetype::Fade : savemode; + } + + maxtime = starttime[r_pixel] + (fadetime / 20); + break; } - } - if ((command_is_on) && (mode == P128_modetype::Off)) { // switch on - mode = (savemode == P128_modetype::On) ? P128_modetype::Fade : savemode; - } else if (command_is_off) { // switch off - savemode = mode; - mode = P128_modetype::Fade; - } - - maxtime = starttime[r_pixel] + (fadetime / 20); - } + case neopixelfx_subcommands_e::dim: + { + if ((str3i >= 0) && (str3i <= maxBright)) { // Safety check + Plugin_128_pixels->SetBrightness(str3i); + } else { success = false; } + break; + } - else if (equals(subCommand, F("dim"))) { - if ((str3i >= 0) && (str3i <= maxBright)) { // Safety check - success = true; - Plugin_128_pixels->SetBrightness(str3i); - } - } + case neopixelfx_subcommands_e::line: + { + mode = P128_modetype::On; - else if (equals(subCommand, F("line"))) { - success = true; - mode = P128_modetype::On; + hex2rgb(str5); - hex2rgb(str5); + const int lastPixelIndex = (str4i - str3i + pixelCount) % pixelCount; - for (int i = 0; i <= (str4i - str3i + pixelCount) % pixelCount; i++) { - Plugin_128_pixels->SetPixelColor((i + str3i - 1) % pixelCount, rgb); - } - } + for (int i = 0; i <= lastPixelIndex; ++i) { + Plugin_128_pixels->SetPixelColor((i + str3i - 1) % pixelCount, rgb); + } + break; + } - else if (equals(subCommand, F("tick"))) { - success = true; - mode = P128_modetype::On; + case neopixelfx_subcommands_e::tick: + { + mode = P128_modetype::On; - hex2rgb(str4); + hex2rgb(str4); - // for (int i = 0; i < pixelCount ; i = i + (pixelCount / parseString(string, 3).toInt())) { - for (int i = 0; i < str3i; i++) { - Plugin_128_pixels->SetPixelColor(i * pixelCount / str3i, rgb); - } - } + // for (int i = 0; i < pixelCount ; i = i + (pixelCount / parseString(string, 3).toInt())) { + for (int i = 0; i < str3i; i++) { + Plugin_128_pixels->SetPixelColor(i * pixelCount / str3i, rgb); + } + break; + } - else if (equals(subCommand, F("one"))) { - success = true; - mode = P128_modetype::On; + case neopixelfx_subcommands_e::one: + { + mode = P128_modetype::On; - const uint16_t pixnum = str3i - 1; - hex2rgb(str4); + const uint16_t pixnum = str3i - 1; + hex2rgb(str4); - Plugin_128_pixels->SetPixelColor(pixnum, rgb); - } + Plugin_128_pixels->SetPixelColor(pixnum, rgb); + break; + } - else if ((equals(subCommand, F("fade"))) || (equals(subCommand, F("all"))) || (equals(subCommand, F("rgb")))) { - success = true; - mode = P128_modetype::Fade; + case neopixelfx_subcommands_e::fade: + case neopixelfx_subcommands_e::all: + case neopixelfx_subcommands_e::rgb: + { + mode = P128_modetype::Fade; - if ((equals(subCommand, F("all"))) || (equals(subCommand, F("rgb")))) { - fadedelay = 0; - } + if ((subcommands_e == neopixelfx_subcommands_e::all) || + (subcommands_e == neopixelfx_subcommands_e::rgb)) { + fadedelay = 0; + } - hex2rgb(str3); - hex2rgb_pixel(str3); + hex2rgb(str3); + hex2rgb_pixel(str3); - fadetime = str4.isEmpty() + fadetime = str4.isEmpty() ? fadetime : str4i; - fadedelay = str5.isEmpty() + fadedelay = str5.isEmpty() ? fadedelay : str5i; - for (int pixel = 0; pixel < pixelCount; pixel++) { - r_pixel = (fadedelay < 0) + for (int pixel = 0; pixel < pixelCount; pixel++) { + r_pixel = (fadedelay < 0) ? pixelCount - pixel - 1 : pixel; - starttime[r_pixel] = counter20ms + (pixel * abs(fadedelay) / 20); + starttime[r_pixel] = counter20ms + (pixel * abs(fadedelay) / 20); - rgb_old[pixel] = Plugin_128_pixels->GetPixelColor(pixel); - } - maxtime = starttime[r_pixel] + (fadetime / 20); - } + rgb_old[pixel] = Plugin_128_pixels->GetPixelColor(pixel); + } + maxtime = starttime[r_pixel] + (fadetime / 20); + break; + } - else if (equals(subCommand, F("hsv"))) { - success = true; - mode = P128_modetype::Fade; - fadedelay = 0; - rgb = - RgbColor(HsbColor(str3.toFloat() / 360.0f, str4.toFloat() / 100.0f, - str5.toFloat() / 100.0f)); + case neopixelfx_subcommands_e::hsv: + { + mode = P128_modetype::Fade; + fadedelay = 0; + rgb = + RgbColor(HsbColor(str3.toFloat() / 360.0f, + str4.toFloat() / 100.0f, + str5.toFloat() / 100.0f)); - rgb2colorStr(); + rgb2colorStr(); - hex2rgb_pixel(colorStr); + hex2rgb_pixel(colorStr); - fadetime = str6.isEmpty() + fadetime = str6.isEmpty() ? fadetime : str6i; - fadedelay = str7.isEmpty() + fadedelay = str7.isEmpty() ? fadedelay : str7i; - for (int pixel = 0; pixel < pixelCount; pixel++) { - r_pixel = (fadedelay < 0) + for (int pixel = 0; pixel < pixelCount; pixel++) { + r_pixel = (fadedelay < 0) ? pixelCount - pixel - 1 : pixel; - starttime[r_pixel] = counter20ms + (pixel * abs(fadedelay) / 20); + starttime[r_pixel] = counter20ms + (pixel * abs(fadedelay) / 20); - rgb_old[pixel] = Plugin_128_pixels->GetPixelColor(pixel); - } - maxtime = starttime[r_pixel] + (fadetime / 20); - } + rgb_old[pixel] = Plugin_128_pixels->GetPixelColor(pixel); + } + maxtime = starttime[r_pixel] + (fadetime / 20); + break; + } - else if (equals(subCommand, F("hsvone"))) { - success = true; - mode = P128_modetype::On; - rgb = - RgbColor(HsbColor(str4.toFloat() / 360.0f, str5.toFloat() / 100.0f, - str6.toFloat() / 100.0f)); + case neopixelfx_subcommands_e::hsvone: + { + mode = P128_modetype::On; + rgb = + RgbColor(HsbColor(str4.toFloat() / 360.0f, + str5.toFloat() / 100.0f, + str6.toFloat() / 100.0f)); - rgb2colorStr(); + rgb2colorStr(); - hex2rgb(colorStr); - const uint16_t pixnum = str3i - 1; - Plugin_128_pixels->SetPixelColor(pixnum, rgb); - } + hex2rgb(colorStr); + const uint16_t pixnum = str3i - 1; + Plugin_128_pixels->SetPixelColor(pixnum, rgb); + break; + } - else if (equals(subCommand, F("hsvline"))) { - success = true; - mode = P128_modetype::On; + case neopixelfx_subcommands_e::hsvline: + { + mode = P128_modetype::On; - rgb = - RgbColor(HsbColor(str5.toFloat() / 360.0f, str6.toFloat() / 100.0f, - str7.toFloat() / 100.0f)); + rgb = + RgbColor(HsbColor(str5.toFloat() / 360.0f, + str6.toFloat() / 100.0f, + str7.toFloat() / 100.0f)); - rgb2colorStr(); + rgb2colorStr(); - hex2rgb(colorStr); + hex2rgb(colorStr); - for (int i = 0; i <= (str4i - str3i + pixelCount) % pixelCount; i++) { - Plugin_128_pixels->SetPixelColor((i + str3i - 1) % pixelCount, rgb); - } - } + const int lastPixelIndex = (str4i - str3i + pixelCount) % pixelCount; + + for (int i = 0; i <= lastPixelIndex; ++i) { + Plugin_128_pixels->SetPixelColor((i + str3i - 1) % pixelCount, rgb); + } + break; + } - else if (equals(subCommand, F("rainbow"))) { - success = true; - fadeIn = (mode == P128_modetype::Off) ? true : false; - mode = P128_modetype::Rainbow; - starttimerb = counter20ms; + case neopixelfx_subcommands_e::rainbow: + { + fadeIn = (mode == P128_modetype::Off) ? true : false; + mode = P128_modetype::Rainbow; + starttimerb = counter20ms; - rainbowspeed = str3.isEmpty() + rainbowspeed = str3.isEmpty() ? speed : str3i; - fadetime = str4.isEmpty() + fadetime = str4.isEmpty() ? fadetime : str4i; - } + break; + } - else if (equals(subCommand, F("colorfade"))) { - success = true; - mode = P128_modetype::ColorFade; + case neopixelfx_subcommands_e::colorfade: + { + mode = P128_modetype::ColorFade; - hex2rgb(str3); + hex2rgb(str3); - if (!str4.isEmpty()) { hex2rrggbb(str4); } + if (!str4.isEmpty()) { hex2rrggbb(str4); } - startpixel = str5.isEmpty() + startpixel = str5.isEmpty() ? 0 : str5i - 1; - endpixel = str6.isEmpty() + endpixel = str6.isEmpty() ? pixelCount - 1 : str6i - 1; - } + break; + } - else if (equals(subCommand, F("kitt"))) { - success = true; - mode = P128_modetype::Kitt; + case neopixelfx_subcommands_e::kitt: + { + mode = P128_modetype::Kitt; - _counter_mode_step = 0; + _counter_mode_step = 0; - hex2rgb(str3); + hex2rgb(str3); - speed = str4.isEmpty() + speed = str4.isEmpty() ? defaultspeed : str4i; - } + break; + } - else if (equals(subCommand, F("comet"))) { - success = true; - mode = P128_modetype::Comet; + case neopixelfx_subcommands_e::comet: + { + mode = P128_modetype::Comet; - _counter_mode_step = 0; + _counter_mode_step = 0; - hex2rgb(str3); + hex2rgb(str3); - speed = str4.isEmpty() + speed = str4.isEmpty() ? defaultspeed : str4i; - } + break; + } - else if (equals(subCommand, F("theatre"))) { - success = true; - mode = P128_modetype::Theatre; + case neopixelfx_subcommands_e::theatre: + { + mode = P128_modetype::Theatre; - hex2rgb(str3); + hex2rgb(str3); - if (!str4.isEmpty()) { hex2rrggbb(str4); } + if (!str4.isEmpty()) { hex2rrggbb(str4); } - count = str5.isEmpty() + count = str5.isEmpty() ? count : str5i; - speed = str6.isEmpty() + speed = str6.isEmpty() ? defaultspeed : str6i; - for (int i = 0; i < pixelCount; i++) { - if ((i / count) % 2 == 0) { - Plugin_128_pixels->SetPixelColor(i, rgb); - } else { - Plugin_128_pixels->SetPixelColor(i, rrggbb); + for (int i = 0; i < pixelCount; i++) { + if ((i / count) % 2 == 0) { + Plugin_128_pixels->SetPixelColor(i, rgb); + } else { + Plugin_128_pixels->SetPixelColor(i, rrggbb); + } + } + break; } - } - } - else if (equals(subCommand, F("scan"))) { - success = true; - mode = P128_modetype::Scan; + case neopixelfx_subcommands_e::scan: + { + mode = P128_modetype::Scan; - _counter_mode_step = 0; + _counter_mode_step = 0; - hex2rrggbb(F("000000")); + hex2rrggbb(F("000000")); - /*CLEAN ALL PIXELS */ - for (int i = 0; i < pixelCount; i++) { - Plugin_128_pixels->SetPixelColor(i, rrggbb); - } - hex2rgb(str3); + /*CLEAN ALL PIXELS */ + for (int i = 0; i < pixelCount; i++) { + Plugin_128_pixels->SetPixelColor(i, rrggbb); + } + hex2rgb(str3); - if (!str4.isEmpty()) { hex2rrggbb(str4); } + if (!str4.isEmpty()) { hex2rrggbb(str4); } - speed = str5.isEmpty() + speed = str5.isEmpty() ? defaultspeed : str5i; - ledi = str6.isEmpty() + ledi = str6.isEmpty() ? 1 : str6i; - ledf = str7.isEmpty() + ledf = str7.isEmpty() ? pixelCount : str7i + 1; - } + break; + } - else if (equals(subCommand, F("dualscan"))) { - success = true; - mode = P128_modetype::Dualscan; + case neopixelfx_subcommands_e::dualscan: + { + mode = P128_modetype::Dualscan; - _counter_mode_step = 0; - hex2rrggbb(F("000000")); + _counter_mode_step = 0; + hex2rrggbb(F("000000")); - /*CLEAN ALL PIXELS */ - for (int i = 0; i < pixelCount; i++) { - Plugin_128_pixels->SetPixelColor(i, rrggbb); - } - hex2rgb(str3); + /*CLEAN ALL PIXELS */ + for (int i = 0; i < pixelCount; i++) { + Plugin_128_pixels->SetPixelColor(i, rrggbb); + } + hex2rgb(str3); - if (!str4.isEmpty()) { hex2rrggbb(str4); } + if (!str4.isEmpty()) { hex2rrggbb(str4); } - speed = str5.isEmpty() + speed = str5.isEmpty() ? defaultspeed : str5i; - ledi = str6.isEmpty() + ledi = str6.isEmpty() ? 1 : str6i; - ledf = str7.isEmpty() + ledf = str7.isEmpty() ? pixelCount : str7i + 1; - } + break; + } - else if (equals(subCommand, F("twinkle"))) { - success = true; - mode = P128_modetype::Twinkle; + case neopixelfx_subcommands_e::twinkle: + { + // FIXME TD-er: code duplication in: Twinkle, twinklefade, sparkle + mode = P128_modetype::Twinkle; - _counter_mode_step = 0; + _counter_mode_step = 0; - hex2rgb(str3); + hex2rgb(str3); - if (!str4.isEmpty()) { hex2rrggbb(str4); } + if (!str4.isEmpty()) { hex2rrggbb(str4); } - speed = str5.isEmpty() + speed = str5.isEmpty() ? defaultspeed : str5i; - } + break; + } - else if (equals(subCommand, F("twinklefade"))) { - success = true; - mode = P128_modetype::TwinkleFade; + case neopixelfx_subcommands_e::twinklefade: + { + mode = P128_modetype::TwinkleFade; - hex2rgb(str3); + hex2rgb(str3); - count = str4.isEmpty() + count = str4.isEmpty() ? count : str4i; - speed = str5.isEmpty() - ? defaultspeed - : str5i; - } - - else if (equals(subCommand, F("sparkle"))) { - success = true; - mode = P128_modetype::Sparkle; - - _counter_mode_step = 0; - - hex2rgb(str3); - hex2rrggbb(str4); - - speed = str5.isEmpty() + speed = str5.isEmpty() ? defaultspeed : str5i; - } - - else if (equals(subCommand, F("wipe"))) { - success = true; - mode = P128_modetype::Wipe; + break; + } - _counter_mode_step = 0; + case neopixelfx_subcommands_e::sparkle: + { + mode = P128_modetype::Sparkle; - hex2rgb(str3); + _counter_mode_step = 0; - if (!str4.isEmpty()) { - hex2rrggbb(str4); - } else { - hex2rrggbb(F("000000")); - } + hex2rgb(str3); + hex2rrggbb(str4); - speed = str5.isEmpty() + speed = str5.isEmpty() ? defaultspeed : str5i; - } + break; + } - else if (equals(subCommand, F("dualwipe"))) { - success = true; - mode = P128_modetype::Dualwipe; + case neopixelfx_subcommands_e::wipe: + case neopixelfx_subcommands_e::dualwipe: + { + mode = (subcommands_e == neopixelfx_subcommands_e::wipe) + ? P128_modetype::Wipe + : P128_modetype::Dualwipe; - _counter_mode_step = 0; + _counter_mode_step = 0; - hex2rgb(str3); + hex2rgb(str3); - if (!str4.isEmpty()) { - hex2rrggbb(str4); - } else { - hex2rrggbb(F("000000")); - } + if (!str4.isEmpty()) { + hex2rrggbb(str4); + } else { + hex2rrggbb(F("000000")); + } - speed = str5.isEmpty() + speed = str5.isEmpty() ? defaultspeed : str5i; - } + break; + } # if P128_ENABLE_FAKETV - else if (equals(subCommand, F("faketv"))) { - success = true; - mode = P128_modetype::FakeTV; - _counter_mode_step = 0; + case neopixelfx_subcommands_e::faketv: + { + mode = P128_modetype::FakeTV; + _counter_mode_step = 0; - randomSeed(analogRead(A0)); - pixelNum = HwRandom(NUMPixels); // Begin at random point + randomSeed(analogRead(A0)); + pixelNum = HwRandom(NUMPixels); // Begin at random point - startpixel = str3.isEmpty() + startpixel = str3.isEmpty() ? 0 : str3i - 1; - endpixel = str4.isEmpty() + endpixel = str4.isEmpty() ? pixelCount : str4i; - } + break; + } # endif // if P128_ENABLE_FAKETV - else if (equals(subCommand, F("fire"))) { - success = true; - mode = P128_modetype::Fire; + case neopixelfx_subcommands_e::fire: + { + mode = P128_modetype::Fire; - fps = str3.isEmpty() + fps = str3.isEmpty() ? fps : str3i; - fps = (fps == 0 || fps > 50) ? 50 : fps; + fps = (fps == 0 || fps > 50) ? 50 : fps; - brightness = str4.isEmpty() + brightness = str4.isEmpty() ? brightness : str4.toFloat(); - cooling = str5.isEmpty() + cooling = str5.isEmpty() ? cooling : str5.toFloat(); - sparking = str6.isEmpty() + sparking = str6.isEmpty() ? sparking : str6.toFloat(); - } + break; + } - else if (equals(subCommand, F("fireflicker"))) { - success = true; - mode = P128_modetype::FireFlicker; + case neopixelfx_subcommands_e::fireflicker: + { + mode = P128_modetype::FireFlicker; - rev_intensity = str3.isEmpty() + rev_intensity = str3.isEmpty() ? rev_intensity : str3i; - speed = str4.isEmpty() + speed = str4.isEmpty() ? defaultspeed : str4i; - } + break; + } - else if (equals(subCommand, F("simpleclock"))) { - success = true; - mode = P128_modetype::SimpleClock; + case neopixelfx_subcommands_e::simpleclock: + { + mode = P128_modetype::SimpleClock; # if defined(RGBW) || defined(GRBW) - if (!str3.isEmpty()) { - const uint32_t hcolorui = rgbStr2Num(str3); - - if (str3.length() <= 6) { - rgb_tick_s = RgbwColor(hcolorui >> 16, hcolorui >> 8, - hcolorui); - } else { - rgb_tick_s = RgbwColor(hcolorui >> 24, - hcolorui >> 16, - hcolorui >> 8, - hcolorui); - } - } + if (!str3.isEmpty()) { + rgb_tick_s = rgbStr2RgbWColor(str3); + } - if (!str4.isEmpty()) { - const uint32_t hcolorui = rgbStr2Num(str4); - - if (str4.length() <= 6) { - rgb_tick_b = RgbwColor(hcolorui >> 16, hcolorui >> 8, - hcolorui); - } else { - rgb_tick_b = RgbwColor(hcolorui >> 24, - hcolorui >> 16, - hcolorui >> 8, - hcolorui); - } - } + if (!str4.isEmpty()) { + rgb_tick_b = rgbStr2RgbWColor(str4); + } - if (!str5.isEmpty()) { - const uint32_t hcolorui = rgbStr2Num(str5); - - if (str5.length() <= 6) { - rgb_h = RgbwColor(hcolorui >> 16, hcolorui >> 8, - hcolorui); - } else { - rgb_h = RgbwColor(hcolorui >> 24, - hcolorui >> 16, - hcolorui >> 8, - hcolorui); - } - } + if (!str5.isEmpty()) { + rgb_h = rgbStr2RgbWColor(str5); + } - if (!str6.isEmpty()) { - const uint32_t hcolorui = rgbStr2Num(str6); - - if (str6.length() <= 6) { - rgb_m = RgbwColor(hcolorui >> 16, hcolorui >> 8, - hcolorui); - } else { - rgb_m = RgbwColor(hcolorui >> 24, - hcolorui >> 16, - hcolorui >> 8, - hcolorui); - } - } + if (!str6.isEmpty()) { + rgb_m = rgbStr2RgbWColor(str6); + } - if (!str7.isEmpty()) { - if (equals(str7, F("off"))) { - rgb_s_off = true; - } else if (str7.length() <= 6) { - const uint32_t hcolorui = rgbStr2Num(str7); - rgb_s_off = false; - rgb_s = RgbwColor(hcolorui >> 16, hcolorui >> 8, - hcolorui); - } else { - const uint32_t hcolorui = rgbStr2Num(str7); - rgb_s_off = false; - rgb_s = RgbwColor(hcolorui >> 24, - hcolorui >> 16, - hcolorui >> 8, - hcolorui); - } - } + if (!str7.isEmpty()) { + if (equals(str7, F("off"))) { + rgb_s_off = true; + } else { + rgb_s = rgbStr2RgbWColor(str7); + rgb_s_off = false; + } + } # else // if defined(RGBW) || defined(GRBW) - if (!str3.isEmpty()) { - const uint32_t hcolorui = rgbStr2Num(str3); - rgb_tick_s = RgbColor(hcolorui >> 16, hcolorui >> 8, hcolorui); - } + if (!str3.isEmpty()) { + rgb_tick_s = rgbStr2RgbColor(str3); + } - if (!str4.isEmpty()) { - const uint32_t hcolorui = rgbStr2Num(str4); - rgb_tick_b = RgbColor(hcolorui >> 16, hcolorui >> 8, hcolorui); - } + if (!str4.isEmpty()) { + rgb_tick_b = rgbStr2RgbColor(str4); + } - if (!str5.isEmpty()) { - const uint32_t hcolorui = rgbStr2Num(str5); - rgb_h = RgbColor(hcolorui >> 16, hcolorui >> 8, hcolorui); - } + if (!str5.isEmpty()) { + rgb_h = rgbStr2RgbColor(str5); + } - if (!str6.isEmpty()) { - const uint32_t hcolorui = rgbStr2Num(str6); - rgb_m = RgbColor(hcolorui >> 16, hcolorui >> 8, hcolorui); - } + if (!str6.isEmpty()) { + rgb_m = rgbStr2RgbColor(str6); + } - if (!str7.isEmpty()) { - if (equals(str7, F("off"))) { - rgb_s_off = true; - } else { - const uint32_t hcolorui = rgbStr2Num(str7); - rgb_s_off = false; - rgb_s = RgbColor(hcolorui >> 16, hcolorui >> 8, - hcolorui); - } - } + if (!str7.isEmpty()) { + if (equals(str7, F("off"))) { + rgb_s_off = true; + } else { + rgb_s = rgbStr2RgbColor(str7); + rgb_s_off = false; + } + } # endif // if defined(RGBW) || defined(GRBW) - if (!parseString(string, 8).isEmpty()) { - hex2rrggbb(parseString(string, 8)); - } - } + if (!parseString(string, 8).isEmpty()) { + hex2rrggbb(parseString(string, 8)); + } + break; + } - else if (equals(subCommand, F("stop"))) { - success = true; - mode = P128_modetype::On; - } + case neopixelfx_subcommands_e::stop: + { + mode = P128_modetype::On; + break; + } - else if (equals(subCommand, F("statusrequest"))) { - success = true; + case neopixelfx_subcommands_e::statusrequest: + { + break; + } + } } if (!success) { success = true; // Fake the command to be successful, to get this custom error message out - String log = F("NeoPixelBus: unknown subcommand: "); - log += subCommand; - addLogMove(LOG_LEVEL_INFO, log); - String json; - printToWebJSON = true; + String error(concat(F("NeoPixelBus: unknown subcommand: "), subCommand)); - json += '{'; json += '\n'; - json += to_json_object_value(F("plugin"), F("128")); - json += ','; json += '\n'; - String subjson = F("NeoPixelBus: unknown command: "); - subjson.reserve(subCommand.length() + 30); - subjson += subCommand; - json += to_json_object_value(F("log"), subjson); - json += '\n'; json += '}'; json += '\n'; + printToWebJSON = true; // event->Source=EventValueSource::Enum::VALUE_SOURCE_HTTP; - SendStatus(event, json); // send http response to controller (JSON format) + SendStatus( + event, + strformat( + F("{\n\"plugin\":128,\n\"log\":\"%s\"\n}\n"), + error.c_str()) + ); // send http response to controller (JSON format) printToWeb = false; + + addLogMove(LOG_LEVEL_INFO, error); } NeoPixelSendStatus(event); @@ -715,10 +768,9 @@ bool P128_data_struct::plugin_write(struct EventStruct *event, } void P128_data_struct::rgb2colorStr() { - colorStr.clear(); - colorStr += formatToHex_no_prefix(rgb.R, 2); - colorStr += formatToHex_no_prefix(rgb.G, 2); - colorStr += formatToHex_no_prefix(rgb.B, 2); + colorStr = formatToHex_no_prefix( + (rgb.R << 16) | (rgb.G << 8) | rgb.B, + 6); } bool P128_data_struct::plugin_fifty_per_second(struct EventStruct *event) { @@ -804,9 +856,9 @@ bool P128_data_struct::plugin_fifty_per_second(struct EventStruct *event) { if (mode != lastmode) { if (loglevelActiveFor(LOG_LEVEL_INFO)) { - String log = F("NeoPixelBus: Mode Change: "); - log += P128_modeType_toString(mode); - addLogMove(LOG_LEVEL_INFO, log); + addLogMove(LOG_LEVEL_INFO, concat( + F("NeoPixelBus: Mode Change: "), + P128_modeType_toString(mode))); } NeoPixelSendStatus(event); } @@ -817,8 +869,7 @@ void P128_data_struct::fade(void) { for (int pixel = 0; pixel < pixelCount; pixel++) { long counter = 20 * (counter20ms - starttime[pixel]); float progress = (float)counter / (float)fadetime; - progress = (progress < 0) ? 0 : progress; - progress = (progress > 1) ? 1 : progress; + progress = constrain(progress, 0.0f, 1.0f); # if defined(RGBW) || defined(GRBW) RgbwColor updatedColor = RgbwColor::LinearBlend( @@ -848,8 +899,7 @@ void P128_data_struct::colorfade(void) { for (uint16_t i = 0; i <= difference; i++) { progress = (float)i / (difference - 1); - progress = (progress >= 1) ? 1 : progress; - progress = (progress <= 0) ? 0 : progress; + progress = constrain(progress, 0.0f, 1.0f); # if defined(RGBW) || defined(GRBW) RgbwColor updatedColor = RgbwColor::LinearBlend( @@ -886,8 +936,7 @@ void P128_data_struct::wipe(void) { void P128_data_struct::dualwipe(void) { if (counter20ms % (unsigned long)(SPEED_MAX / abs(speed)) == 0) { if (speed > 0) { - int i = _counter_mode_step - pixelCount; - i = abs(i); + const int i = abs(static_cast(_counter_mode_step - pixelCount)); Plugin_128_pixels->SetPixelColor(_counter_mode_step, rrggbb); Plugin_128_pixels->SetPixelColor(i, rgb); @@ -896,8 +945,7 @@ void P128_data_struct::dualwipe(void) { Plugin_128_pixels->SetPixelColor(i - 1, rrggbb); } } else { - int i = (pixelCount / 2) - _counter_mode_step; - i = abs(i); + const int i = abs(static_cast((pixelCount / 2) - _counter_mode_step)); Plugin_128_pixels->SetPixelColor(_counter_mode_step + (pixelCount / 2), rrggbb); Plugin_128_pixels->SetPixelColor(i, rgb); @@ -984,8 +1032,8 @@ void P128_data_struct::faketv(void) { * Cycles a rainbow over the entire string of LEDs. */ void P128_data_struct::rainbow(void) { - long counter = 20 * (counter20ms - starttimerb); - float progress = (float)counter / (float)fadetime; + const long counter = 20 * (counter20ms - starttimerb); + const float progress = (float)counter / (float)fadetime; if (fadeIn == true) { Plugin_128_pixels->SetBrightness(progress * maxBright); // Safety check @@ -993,10 +1041,12 @@ void P128_data_struct::rainbow(void) { } for (int i = 0; i < pixelCount; i++) { - uint8_t r1 = (Wheel(((i * 256 / pixelCount) + counter20ms * rainbowspeed / 10) & 255) >> 16); - uint8_t g1 = (Wheel(((i * 256 / pixelCount) + counter20ms * rainbowspeed / 10) & 255) >> 8); - uint8_t b1 = (Wheel(((i * 256 / pixelCount) + counter20ms * rainbowspeed / 10) & 255)); - Plugin_128_pixels->SetPixelColor(i, RgbColor(r1, g1, b1)); + const uint32_t color = Wheel(((i * 256 / pixelCount) + counter20ms * rainbowspeed / 10) & 255); + Plugin_128_pixels->SetPixelColor(i, + RgbColor( + (color >> 16), // r + (color >> 8), // g + (color))); // b } mode = (rainbowspeed == 0) ? P128_modetype::On : P128_modetype::Rainbow; } @@ -1028,19 +1078,19 @@ void P128_data_struct::kitt(void) { RgbwColor px_rgb = Plugin_128_pixels->GetPixelColor(i); // fade out (divide by 2) - px_rgb.R = px_rgb.R >> 1; - px_rgb.G = px_rgb.G >> 1; - px_rgb.B = px_rgb.B >> 1; - px_rgb.W = px_rgb.W >> 1; + px_rgb.R >>= 1; + px_rgb.G >>= 1; + px_rgb.B >>= 1; + px_rgb.W >>= 1; # else // if defined(RGBW) || defined(GRBW) RgbColor px_rgb = Plugin_128_pixels->GetPixelColor(i); // fade out (divide by 2) - px_rgb.R = px_rgb.R >> 1; - px_rgb.G = px_rgb.G >> 1; - px_rgb.B = px_rgb.B >> 1; + px_rgb.R >>= 1; + px_rgb.G >>= 1; + px_rgb.B >>= 1; # endif // if defined(RGBW) || defined(GRBW) Plugin_128_pixels->SetPixelColor(i, px_rgb); @@ -1064,55 +1114,32 @@ void P128_data_struct::kitt(void) { void P128_data_struct::comet(void) { if (counter20ms % (unsigned long)(SPEED_MAX / abs(speed)) == 0) { for (uint16_t i = 0; i < pixelCount; i++) { - if (speed > 0) { - # if defined(RGBW) || defined(GRBW) - RgbwColor px_rgb = Plugin_128_pixels->GetPixelColor(i); - - // fade out (divide by 2) - px_rgb.R = px_rgb.R >> 1; - px_rgb.G = px_rgb.G >> 1; - px_rgb.B = px_rgb.B >> 1; - px_rgb.W = px_rgb.W >> 1; - - # else // if defined(RGBW) || defined(GRBW) - - RgbColor px_rgb = Plugin_128_pixels->GetPixelColor(i); - - // fade out (divide by 2) - px_rgb.R = px_rgb.R >> 1; - px_rgb.G = px_rgb.G >> 1; - px_rgb.B = px_rgb.B >> 1; - # endif // if defined(RGBW) || defined(GRBW) - - Plugin_128_pixels->SetPixelColor(i, px_rgb); - } else { - # if defined(RGBW) || defined(GRBW) - RgbwColor px_rgb = Plugin_128_pixels->GetPixelColor(pixelCount - i - 1); + const uint16_t pixelIndex = (speed > 0) ? i : pixelCount - i - 1; + # if defined(RGBW) || defined(GRBW) + RgbwColor px_rgb = Plugin_128_pixels->GetPixelColor(pixelIndex); - // fade out (divide by 2) - px_rgb.R = px_rgb.R >> 1; - px_rgb.G = px_rgb.G >> 1; - px_rgb.B = px_rgb.B >> 1; - px_rgb.W = px_rgb.W >> 1; + // fade out (divide by 2) + px_rgb.R >>= 1; + px_rgb.G >>= 1; + px_rgb.B >>= 1; + px_rgb.W >>= 1; - # else // if defined(RGBW) || defined(GRBW) + # else // if defined(RGBW) || defined(GRBW) - RgbColor px_rgb = Plugin_128_pixels->GetPixelColor(pixelCount - i - 1); + RgbColor px_rgb = Plugin_128_pixels->GetPixelColor(pixelIndex); - // fade out (divide by 2) - px_rgb.R = px_rgb.R >> 1; - px_rgb.G = px_rgb.G >> 1; - px_rgb.B = px_rgb.B >> 1; - # endif // if defined(RGBW) || defined(GRBW) + // fade out (divide by 2) + px_rgb.R >>= 1; + px_rgb.G >>= 1; + px_rgb.B >>= 1; + # endif // if defined(RGBW) || defined(GRBW) - Plugin_128_pixels->SetPixelColor(pixelCount - i - 1, px_rgb); - } + Plugin_128_pixels->SetPixelColor(pixelIndex, px_rgb); } - if (speed > 0) { - Plugin_128_pixels->SetPixelColor(_counter_mode_step, rgb); - } else { - Plugin_128_pixels->SetPixelColor(pixelCount - _counter_mode_step - 1, rgb); + { + const uint16_t pixelIndex = (speed > 0) ? _counter_mode_step : pixelCount - _counter_mode_step - 1; + Plugin_128_pixels->SetPixelColor(pixelIndex, rgb); } _counter_mode_step = (_counter_mode_step + 1) % pixelCount; @@ -1414,24 +1441,21 @@ void P128_data_struct::Plugin_128_simpleclock() { Plugin_128_pixels->ClearTo(rrggbb); for (int i = 0; i < (60 / small_tick); i++) { - if (i % (big_tick / small_tick) == 0) { - Plugin_128_pixels->SetPixelColor((i * pixelCount * small_tick / 60) % pixelCount, rgb_tick_b); - } else { - Plugin_128_pixels->SetPixelColor((i * pixelCount * small_tick / 60) % pixelCount, rgb_tick_s); - } + const bool use_big_tick = i % (big_tick / small_tick) == 0; + Plugin_128_pixels->SetPixelColor((i * pixelCount * small_tick / 60) % pixelCount, use_big_tick ? rgb_tick_b : rgb_tick_s); } for (int i = 0; i < pixelCount; i++) { - if (lround((((float)Seconds + ((float)counter20ms - (float)maxtime) / 50.0) * (float)pixelCount) / 60.0) == i) { + if (lround((((float)Seconds + ((float)counter20ms - (float)maxtime) / 50.0f) * (float)pixelCount) / 60.0f) == i) { if (rgb_s_off == false) { Plugin_128_pixels->SetPixelColor(i, rgb_s); } } - else if (lround((((float)Minutes * 60.0) + (float)Seconds) / 60.0 * (float)pixelCount / 60.0) == i) { + else if (lround((((float)Minutes * 60.0f) + (float)Seconds) / 60.0f * (float)pixelCount / 60.0f) == i) { Plugin_128_pixels->SetPixelColor(i, rgb_m); } - else if (lround(((float)Hours + (float)Minutes / 60) * (float)pixelCount / 12.0) == i) { + else if (lround(((float)Hours + (float)Minutes / 60) * (float)pixelCount / 12.0f) == i) { Plugin_128_pixels->SetPixelColor(i, rgb_h); Plugin_128_pixels->SetPixelColor((i + 1) % pixelCount, rgb_h); Plugin_128_pixels->SetPixelColor((i - 1 + pixelCount) % pixelCount, rgb_h); @@ -1439,10 +1463,32 @@ void P128_data_struct::Plugin_128_simpleclock() { } } -uint32_t P128_data_struct::rgbStr2Num(String rgbStr) { - uint32_t rgbDec = static_cast(strtoul(&rgbStr[0], NULL, 16)); +uint32_t P128_data_struct::rgbStr2Num(const String& rgbStr) { + return static_cast(strtoul(rgbStr.c_str(), NULL, 16)); +} - return rgbDec; +RgbColor P128_data_struct::rgbStr2RgbColor(const String& str) +{ + const uint32_t hcolorui = rgbStr2Num(str); + + return RgbColor(hcolorui >> 16, hcolorui >> 8, hcolorui); +} + +RgbwColor P128_data_struct::rgbStr2RgbWColor(const String& str) +{ + const uint32_t hcolorui = rgbStr2Num(str); + + if (str.length() <= 6) { + // w = 0 + return RgbwColor(hcolorui >> 16, + hcolorui >> 8, + hcolorui); + } + + return RgbwColor(hcolorui >> 24, + hcolorui >> 16, + hcolorui >> 8, + hcolorui); } void P128_data_struct::hex2rgb(const String& hexcolor) { @@ -1490,56 +1536,49 @@ void P128_data_struct::hex2rgb_pixel(const String& hexcolor) { // ------------------------------ JsonResponse ------------------------------------- // --------------------------------------------------------------------------------- void P128_data_struct::NeoPixelSendStatus(struct EventStruct *eventSource) { - String log = F("NeoPixelBusFX: Set "); - - log += rgb.R; - log += '/'; - log += rgb.G; - log += '/'; - log += rgb.B; - - addLogMove(LOG_LEVEL_INFO, log); - - String json; - - json.reserve(285); // Awfully long string :-| + addLogMove(LOG_LEVEL_INFO, strformat( + F("NeoPixelBusFX: Set %u/%u/%u"), + rgb.R, + rgb.G, + rgb.B)); printToWebJSON = true; - json += '{'; json += '\n'; // 2 - json += to_json_object_value(F("plugin"), F("128")); // 12 - json += ','; json += '\n'; - json += to_json_object_value(F("mode"), P128_modeType_toString(mode)); // 14..23 - json += ','; json += '\n'; - json += to_json_object_value(F("lastmode"), P128_modeType_toString(savemode)); // 18..27 - json += ','; json += '\n'; - json += to_json_object_value(F("fadetime"), toString(fadetime, 0)); // 15..19 - json += ','; json += '\n'; - json += to_json_object_value(F("fadedelay"), toString(fadedelay, 0)); // 15..19 - json += ','; json += '\n'; - json += to_json_object_value(F("dim"), toString(Plugin_128_pixels->GetBrightness(), 0)); // 8..10 - json += ','; json += '\n'; - json += to_json_object_value(F("rgb"), colorStr, true); // 15..17 - json += ','; json += '\n'; - - HsbColor hsbColor = HsbColor(RgbColor(rgb.R, rgb.G, rgb.B)); // Calculate only once - - json += to_json_object_value(F("hue"), toString(hsbColor.H * 360.0f, 0)); // 17 - json += ','; json += '\n'; - json += to_json_object_value(F("saturation"), toString(hsbColor.S * 100.0f, 0)); // 26? - json += ','; json += '\n'; - json += to_json_object_value(F("brightness"), toString(hsbColor.B * 100.0f, 0)); // 26? - json += ','; json += '\n'; - json += to_json_object_value(F("bgcolor"), backgroundcolorStr, true); // 21..23 - json += ','; json += '\n'; - json += to_json_object_value(F("count"), toString(count, 0)); // 12..15 - json += ','; json += '\n'; - json += to_json_object_value(F("speed"), toString(speed, 0)); // 12..14 - json += ','; json += '\n'; - json += to_json_object_value(F("pixelcount"), toString(pixelCount, 0)); // 17..19 - json += '\n'; json += '}'; json += '\n'; // 4 - - SendStatus(eventSource, json); // send http response to controller (JSON format) + HsbColor hsbColor = HsbColor(RgbColor(rgb.R, rgb.G, rgb.B)); // Calculate only once + + SendStatus( + eventSource, + strformat( + F("{\n%s" // "plugin" + ",\n%s" // "mode" + ",\n%s" // "lastmode" + ",\n%s" // "fadetime" + ",\n%s" // "fadedelay" + ",\n%s" // "dim" + ",\n%s" // "rgb" + ",\n%s" // "hue" + ",\n%s" // "saturation" + ",\n%s" // "brightness" + ",\n%s" // "bgcolor" + ",\n%s" // "count" + ",\n%s" // "speed" + ",\n%s" // "pixelcount" + "\n}\n"), + to_json_object_value(F("plugin"), 128).c_str(), + to_json_object_value(F("mode"), P128_modeType_toString(mode)).c_str(), + to_json_object_value(F("lastmode"), P128_modeType_toString(savemode)).c_str(), + to_json_object_value(F("fadetime"), static_cast(fadetime)).c_str(), + to_json_object_value(F("fadedelay"), static_cast(fadedelay)).c_str(), + to_json_object_value(F("dim"), static_cast(Plugin_128_pixels->GetBrightness())).c_str(), + to_json_object_value(F("rgb"), colorStr, true).c_str(), + to_json_object_value(F("hue"), static_cast(hsbColor.H * 360.0f)).c_str(), + to_json_object_value(F("saturation"), static_cast(hsbColor.S * 100.0f)).c_str(), + to_json_object_value(F("brightness"), static_cast(hsbColor.B * 100.0f)).c_str(), + to_json_object_value(F("bgcolor"), backgroundcolorStr, true).c_str(), + to_json_object_value(F("count"), static_cast(count)).c_str(), + to_json_object_value(F("speed"), static_cast(speed)).c_str(), + to_json_object_value(F("pixelcount"), static_cast(pixelCount)).c_str() + )); printToWeb = false; } diff --git a/src/src/PluginStructs/P128_data_struct.h b/src/src/PluginStructs/P128_data_struct.h index 36eee877b9..b44c8f5e6c 100644 --- a/src/src/PluginStructs/P128_data_struct.h +++ b/src/src/PluginStructs/P128_data_struct.h @@ -2249,7 +2249,7 @@ const uint8_t PROGMEM ftv_colors[] = { // # define BRG //A three element color in the order of Blue, Red, and then Green. // # define RBG //A three element color in the order of Red, Blue, and then Green. -# define NEOPIXEL_LIB NeoPixelBrightnessBus // Neopixel library type +# define NEOPIXEL_LIB NeoPixelBrightnessBus // Neopixel library type # if defined(ESP32) # define METHOD NeoWs2812xMethod // Automatic method, user selected pin # endif // if defined(ESP32) @@ -2431,27 +2431,29 @@ struct P128_data_struct : public PluginTaskData_base { /// random number seed uint16_t rand16seed; // = RAND16_SEED; // leave uninitialized //-V457 - uint8_t random8(); - uint8_t random8(uint8_t lim); - uint8_t random8(uint8_t min, - uint8_t lim); - uint8_t qsub8(uint8_t i, - uint8_t j); - uint8_t qadd8(uint8_t i, - uint8_t j); - uint8_t scale8_video(uint8_t i, - uint8_t scale); + uint8_t random8(); + uint8_t random8(uint8_t lim); + uint8_t random8(uint8_t min, + uint8_t lim); + static uint8_t qsub8(uint8_t i, + uint8_t j); + static uint8_t qadd8(uint8_t i, + uint8_t j); + static uint8_t scale8_video(uint8_t i, + uint8_t scale); // Fire2012: Array of temperature readings at each simulation cell byte heat[ARRAYSIZE] = { 0 }; - void Fire2012(void); - void fire_flicker(); - void Plugin_128_simpleclock(); - uint32_t rgbStr2Num(String rgbStr); - void hex2rgb(const String& hexcolor); - void hex2rrggbb(const String& hexcolor); - void hex2rgb_pixel(const String& hexcolor); - void NeoPixelSendStatus(struct EventStruct *eventSource); + void Fire2012(void); + void fire_flicker(); + void Plugin_128_simpleclock(); + static uint32_t rgbStr2Num(const String& rgbStr); + static RgbColor rgbStr2RgbColor(const String& str); + static RgbwColor rgbStr2RgbWColor(const String& str); + void hex2rgb(const String& hexcolor); + void hex2rrggbb(const String& hexcolor); + void hex2rgb_pixel(const String& hexcolor); + void NeoPixelSendStatus(struct EventStruct *eventSource); }; #endif // ifdef USES_P128 diff --git a/src/src/WebServer/Chart_JS_scale.cpp b/src/src/WebServer/Chart_JS_scale.cpp index ccb5254ab9..96cc7d6e71 100644 --- a/src/src/WebServer/Chart_JS_scale.cpp +++ b/src/src/WebServer/Chart_JS_scale.cpp @@ -78,6 +78,8 @@ bool ChartJS_options_scale::is_Y_axis() const position == Position::Right; } +ChartJS_options_scales::ChartJS_options_scales() {} + void ChartJS_options_scales::add(const ChartJS_options_scale& scale) { for (auto it = _scales.begin(); it != _scales.end(); ++it) { diff --git a/src/src/WebServer/Chart_JS_scale.h b/src/src/WebServer/Chart_JS_scale.h index 075f5f62e8..e19c90ccbe 100644 --- a/src/src/WebServer/Chart_JS_scale.h +++ b/src/src/WebServer/Chart_JS_scale.h @@ -51,7 +51,7 @@ struct ChartJS_options_scale { }; struct ChartJS_options_scales { - ChartJS_options_scales() = default; + ChartJS_options_scales(); void add(const ChartJS_options_scale& scale); diff --git a/src/src/WebServer/JSON.cpp b/src/src/WebServer/JSON.cpp index c233e7be54..0afcc5c8dc 100644 --- a/src/src/WebServer/JSON.cpp +++ b/src/src/WebServer/JSON.cpp @@ -353,7 +353,7 @@ void handle_json() stream_next_json_object_value(F("ip"), formatIP(it->second.IP())); #if FEATURE_USE_IPV6 if (it->second.hasIPv6_mac_based_link_local) { - stream_next_json_object_value(F("ipv6local"), formatIP(it->second.IPv6_link_local(true))); + stream_next_json_object_value(F("ipv6local"), formatIP(it->second.IPv6_link_local(true), true)); } if (it->second.hasIPv6_mac_based_link_global) { stream_next_json_object_value(F("ipv6global"), formatIP(it->second.IPv6_global())); diff --git a/tools/pio/pre_custom_esp32.py b/tools/pio/pre_custom_esp32.py index 62274cab5a..1ff9c872c7 100644 --- a/tools/pio/pre_custom_esp32.py +++ b/tools/pio/pre_custom_esp32.py @@ -72,7 +72,7 @@ "-DTESTING_FEATURE_USE_IPV6", "-DFEATURE_SETTINGS_ARCHIVE=1", - "-DFEATURE_DEFINE_SERIAL_CONSOLE_PORT=0", + "-DFEATURE_DEFINE_SERIAL_CONSOLE_PORT=1", "-DFEATURE_ESPEASY_P2P=1", "-DFEATURE_CUSTOM_PROVISIONING=1", "-DDISABLE_SC16IS752_SPI" diff --git a/tools/pio/pre_custom_esp32c6.py b/tools/pio/pre_custom_esp32c6.py index 769d161e6f..64d94e8b14 100644 --- a/tools/pio/pre_custom_esp32c6.py +++ b/tools/pio/pre_custom_esp32c6.py @@ -70,6 +70,7 @@ "-DTESTING_FEATURE_USE_IPV6", "-DFEATURE_SETTINGS_ARCHIVE=1", + "-DFEATURE_DEFINE_SERIAL_CONSOLE_PORT=1", "-DFEATURE_ESPEASY_P2P=1", "-DFEATURE_CUSTOM_PROVISIONING=1", "-DDISABLE_SC16IS752_SPI"