diff --git a/docs/DeviceProfiles/wt32-eth01.json b/docs/DeviceProfiles/wt32-eth01.json index 8af11283..b7f5cbdd 100644 --- a/docs/DeviceProfiles/wt32-eth01.json +++ b/docs/DeviceProfiles/wt32-eth01.json @@ -22,6 +22,34 @@ "clk_mode": 0 } }, + { + "name": "WT32-ETH01 with SH1106", + "links": [ + {"name": "Datasheet", "url": "http://www.wireless-tag.com/portfolio/wt32-eth01/"} + ], + "nrf24": { + "miso": 4, + "mosi": 2, + "clk": 32, + "irq": 33, + "en": 14, + "cs": 15 + }, + "eth": { + "enabled": true, + "phy_addr": 1, + "power": 16, + "mdc": 23, + "mdio": 18, + "type": 0, + "clk_mode": 0 + }, + "display": { + "type": 3, + "data": 5, + "clk": 17 + } + }, { "name": "WT32-ETH01 with SSD1306", "links": [ @@ -78,4 +106,4 @@ "clk": 17 } } -] \ No newline at end of file +] diff --git a/include/Configuration.h b/include/Configuration.h index 0b3d48a0..05cd1aea 100644 --- a/include/Configuration.h +++ b/include/Configuration.h @@ -16,6 +16,7 @@ #define NTP_MAX_TIMEZONEDESCR_STRLEN 50 #define MQTT_MAX_HOSTNAME_STRLEN 128 +#define MQTT_MAX_CLIENTID_STRLEN 64 #define MQTT_MAX_USERNAME_STRLEN 64 #define MQTT_MAX_PASSWORD_STRLEN 64 #define MQTT_MAX_TOPIC_STRLEN 32 @@ -90,6 +91,7 @@ struct CONFIG_T { bool Enabled; char Hostname[MQTT_MAX_HOSTNAME_STRLEN + 1]; uint32_t Port; + char ClientId[MQTT_MAX_CLIENTID_STRLEN + 1]; char Username[MQTT_MAX_USERNAME_STRLEN + 1]; char Password[MQTT_MAX_PASSWORD_STRLEN + 1]; char Topic[MQTT_MAX_TOPIC_STRLEN + 1]; diff --git a/include/MqttHandleHass.h b/include/MqttHandleHass.h index 8e09b7d3..66f4e83d 100644 --- a/include/MqttHandleHass.h +++ b/include/MqttHandleHass.h @@ -64,7 +64,7 @@ private: void publishDtuBinarySensor(const char* name, const char* device_class, const char* category, const char* payload_on, const char* payload_off, const char* subTopic = ""); void publishInverterField(std::shared_ptr inv, const ChannelType_t type, const ChannelNum_t channel, const byteAssign_fieldDeviceClass_t fieldType, const bool clear = false); void publishInverterButton(std::shared_ptr inv, const char* caption, const char* icon, const char* category, const char* deviceClass, const char* subTopic, const char* payload); - void publishInverterNumber(std::shared_ptr inv, const char* caption, const char* icon, const char* category, const char* commandTopic, const char* stateTopic, const char* unitOfMeasure, const int16_t min = 1, const int16_t max = 100); + void publishInverterNumber(std::shared_ptr inv, const char* caption, const char* icon, const char* category, const char* commandTopic, const char* stateTopic, const char* unitOfMeasure, const int16_t min = 1, const int16_t max = 100, float step = 1.0); void publishInverterBinarySensor(std::shared_ptr inv, const char* caption, const char* subTopic, const char* payload_on, const char* payload_off); void publishPowerMeterField(size_t channel, JsyMkClass::Field_t fieldId, const bool clear = false); diff --git a/include/MqttSettings.h b/include/MqttSettings.h index c21f14b8..41b0e6ec 100644 --- a/include/MqttSettings.h +++ b/include/MqttSettings.h @@ -20,6 +20,7 @@ public: void unsubscribe(const String& topic); String getPrefix() const; + String getClientId(); private: void NetworkEvent(network_event event); @@ -39,4 +40,4 @@ private: std::mutex _clientLock; }; -extern MqttSettingsClass MqttSettings; \ No newline at end of file +extern MqttSettingsClass MqttSettings; diff --git a/include/WebApi_errors.h b/include/WebApi_errors.h index 97d61b22..0da8d3d9 100644 --- a/include/WebApi_errors.h +++ b/include/WebApi_errors.h @@ -60,6 +60,7 @@ enum WebApiError { MqttHassTopicLength, MqttHassTopicCharacter, MqttLwtQos, + MqttClientIdLength, NetworkBase = 8000, NetworkIpInvalid, diff --git a/include/defaults.h b/include/defaults.h index 21d0a44b..d6bc387f 100644 --- a/include/defaults.h +++ b/include/defaults.h @@ -9,7 +9,7 @@ #define ACCESS_POINT_NAME "OpenDTU-" #define ACCESS_POINT_PASSWORD "openDTU42" -#define ACCESS_POINT_TIMEOUT 3; +#define ACCESS_POINT_TIMEOUT 3 #define AUTH_USERNAME "admin" #define SECURITY_ALLOW_READONLY true diff --git a/lib/Hoymiles/src/commands/ActivePowerControlCommand.cpp b/lib/Hoymiles/src/commands/ActivePowerControlCommand.cpp index b9e8eea2..dcd2370c 100644 --- a/lib/Hoymiles/src/commands/ActivePowerControlCommand.cpp +++ b/lib/Hoymiles/src/commands/ActivePowerControlCommand.cpp @@ -85,7 +85,7 @@ bool ActivePowerControlCommand::handleResponse(const fragment_t fragment[], cons float ActivePowerControlCommand::getLimit() const { - const uint16_t l = (((uint16_t)_payload[12] << 8) | _payload[13]); + const float l = (((uint16_t)_payload[12] << 8) | _payload[13]); return l / 10; } diff --git a/lib/Hoymiles/src/inverters/HMS_2CH.cpp b/lib/Hoymiles/src/inverters/HMS_2CH.cpp index 4a700a9a..4ad0157f 100644 --- a/lib/Hoymiles/src/inverters/HMS_2CH.cpp +++ b/lib/Hoymiles/src/inverters/HMS_2CH.cpp @@ -42,7 +42,7 @@ bool HMS_2CH::isValidSerial(const uint64_t serial) { // serial >= 0x114400000000 && serial <= 0x1144ffffffff uint16_t preSerial = (serial >> 32) & 0xffff; - return preSerial == 0x1144; + return preSerial == 0x1144 || preSerial == 0x1143; } String HMS_2CH::typeName() const diff --git a/lib/Hoymiles/src/inverters/README.md b/lib/Hoymiles/src/inverters/README.md index 6d6104a2..8d913deb 100644 --- a/lib/Hoymiles/src/inverters/README.md +++ b/lib/Hoymiles/src/inverters/README.md @@ -7,7 +7,7 @@ | HM_4CH | HM-1000/1200/1500-4T | 1161 | | HMS_1CH | HMS-300/350/400/450/500-1T | 1124 | | HMS_1CHv2 | HMS-500-1T v2 | 1125 | -| HMS_2CH | HMS-600/700/800/900/1000-2T | 1144 | +| HMS_2CH | HMS-600/700/800/900/1000-2T | 1143, 1144 | | HMS_4CH | HMS-1600/1800/2000-4T | 1164 | | HMT_4CH | HMT-1600/1800/2000-4T | 1361 | | HMT_6CH | HMT-1800/2250-6T | 1382 | diff --git a/lib/Hoymiles/src/parser/AlarmLogParser.cpp b/lib/Hoymiles/src/parser/AlarmLogParser.cpp index e08baf05..30b81364 100644 --- a/lib/Hoymiles/src/parser/AlarmLogParser.cpp +++ b/lib/Hoymiles/src/parser/AlarmLogParser.cpp @@ -28,15 +28,15 @@ ID Source Addr Target Addr Idx ? wcode ? Start End ? const std::array AlarmLogParser::_alarmMessages = { { { AlarmMessageType_t::ALL, 1, "Inverter start", "Wechselrichter gestartet", "L'onduleur a démarré" }, - { AlarmMessageType_t::ALL, 2, "Time calibration", "", "" }, + { AlarmMessageType_t::ALL, 2, "Time calibration", "Zeitabgleich", "" }, { AlarmMessageType_t::ALL, 3, "EEPROM reading and writing error during operation", "", "" }, { AlarmMessageType_t::ALL, 4, "Offline", "Offline", "Non connecté" }, - { AlarmMessageType_t::ALL, 11, "Grid voltage surge", "", "" }, - { AlarmMessageType_t::ALL, 12, "Grid voltage sharp drop", "", "" }, - { AlarmMessageType_t::ALL, 13, "Grid frequency mutation", "", "" }, - { AlarmMessageType_t::ALL, 14, "Grid phase mutation", "", "" }, - { AlarmMessageType_t::ALL, 15, "Grid transient fluctuation", "", "" }, + { AlarmMessageType_t::ALL, 11, "Grid voltage surge", "Netz: Überspannungsimpuls", "" }, + { AlarmMessageType_t::ALL, 12, "Grid voltage sharp drop", "Netz: Spannungseinbruch", "" }, + { AlarmMessageType_t::ALL, 13, "Grid frequency mutation", "Netz: Frequenzänderung", "" }, + { AlarmMessageType_t::ALL, 14, "Grid phase mutation", "Netz: Phasenänderung", "" }, + { AlarmMessageType_t::ALL, 15, "Grid transient fluctuation", "Netz: vorübergehende Schwankung", "" }, { AlarmMessageType_t::ALL, 36, "INV overvoltage or overcurrent", "", "" }, diff --git a/lib/Hoymiles/src/parser/DevInfoParser.cpp b/lib/Hoymiles/src/parser/DevInfoParser.cpp index c34ecb3f..0c2e15e8 100644 --- a/lib/Hoymiles/src/parser/DevInfoParser.cpp +++ b/lib/Hoymiles/src/parser/DevInfoParser.cpp @@ -62,6 +62,7 @@ const devInfo_t devInfo[] = { { { 0x10, 0x20, 0x71, ALL }, 500, "HMS-500-1T v2" }, // 02 { { 0x10, 0x21, 0x11, ALL }, 600, "HMS-600-2T" }, // 01 { { 0x10, 0x21, 0x41, ALL }, 800, "HMS-800-2T" }, // 00 + { { 0x10, 0x11, 0x41, ALL }, 800, "HMS-800-2T-LV" }, // 00 { { 0x10, 0x11, 0x51, ALL }, 900, "HMS-900-2T" }, // 01 { { 0x10, 0x21, 0x51, ALL }, 900, "HMS-900-2T" }, // 03 { { 0x10, 0x21, 0x71, ALL }, 1000, "HMS-1000-2T" }, // 05 diff --git a/pio-scripts/auto_firmware_version.py b/pio-scripts/auto_firmware_version.py index f169f617..26e1bd65 100644 --- a/pio-scripts/auto_firmware_version.py +++ b/pio-scripts/auto_firmware_version.py @@ -3,33 +3,27 @@ # Copyright (C) 2022 Thomas Basler and others # import os -import pkg_resources Import("env") -required_pkgs = {'dulwich'} -installed_pkgs = {pkg.key for pkg in pkg_resources.working_set} -missing_pkgs = required_pkgs - installed_pkgs - -if missing_pkgs: +try: + from dulwich import porcelain +except ModuleNotFoundError: env.Execute('"$PYTHONEXE" -m pip install dulwich') - -from dulwich import porcelain + from dulwich import porcelain def updateFileIfChanged(filename, content): mustUpdate = True try: - fp = open(filename, "rb") - if fp.read() == content: - mustUpdate = False - fp.close() + with open(filename, "rb") as fp: + if fp.read() == content: + mustUpdate = False except: pass if mustUpdate: - fp = open(filename, "wb") - fp.write(content) - fp.close() + with open(filename, "wb") as fp: + fp.write(content) return mustUpdate diff --git a/platformio.ini b/platformio.ini index 5abf332d..472b965a 100644 --- a/platformio.ini +++ b/platformio.ini @@ -19,7 +19,7 @@ extra_configs = custom_ci_action = generic,generic_esp32,generic_esp32s3,generic_esp32s3_usb framework = arduino -platform = espressif32@6.7.0 +platform = espressif32@6.8.1 build_flags = -DPIOENV=\"$PIOENV\" @@ -27,6 +27,7 @@ build_flags = -D_TASK_THREAD_SAFE=1 -DCONFIG_ASYNC_TCP_EVENT_QUEUE_SIZE=128 -DCONFIG_ASYNC_TCP_QUEUE_SIZE=128 + -DEMC_TASK_STACK_SIZE=6400 -Wall -Wextra -Wunused -Wmisleading-indentation -Wduplicated-cond -Wlogical-op -Wnull-dereference ; Have to remove -Werror because of ; https://github.com/espressif/arduino-esp32/issues/9044 and @@ -38,10 +39,10 @@ build_unflags = -std=gnu++11 lib_deps = - mathieucarbou/ESP Async WebServer @ 2.10.8 - bblanchon/ArduinoJson @ 7.0.4 + mathieucarbou/ESPAsyncWebServer @ 3.1.2 + bblanchon/ArduinoJson @ 7.1.0 https://github.com/bertmelis/espMqttClient.git#v1.7.0 - nrf24/RF24 @ 1.4.8 + nrf24/RF24 @ 1.4.9 olikraus/U8g2 @ 2.35.19 buelowp/sunset @ 1.1.7 https://github.com/arkhipenko/TaskScheduler#testing diff --git a/src/Configuration.cpp b/src/Configuration.cpp index 2504c383..ee0d0263 100644 --- a/src/Configuration.cpp +++ b/src/Configuration.cpp @@ -4,6 +4,7 @@ */ #include "Configuration.h" #include "MessageOutput.h" +#include "NetworkSettings.h" #include "Utils.h" #include "defaults.h" #include @@ -58,6 +59,7 @@ bool ConfigurationClass::write() mqtt["enabled"] = config.Mqtt.Enabled; mqtt["hostname"] = config.Mqtt.Hostname; mqtt["port"] = config.Mqtt.Port; + mqtt["clientid"] = config.Mqtt.ClientId; mqtt["username"] = config.Mqtt.Username; mqtt["password"] = config.Mqtt.Password; mqtt["topic"] = config.Mqtt.Topic; @@ -244,6 +246,7 @@ bool ConfigurationClass::read() config.Mqtt.Enabled = mqtt["enabled"] | MQTT_ENABLED; strlcpy(config.Mqtt.Hostname, mqtt["hostname"] | MQTT_HOST, sizeof(config.Mqtt.Hostname)); config.Mqtt.Port = mqtt["port"] | MQTT_PORT; + strlcpy(config.Mqtt.ClientId, mqtt["clientid"] | NetworkSettings.getApName().c_str(), sizeof(config.Mqtt.ClientId)); strlcpy(config.Mqtt.Username, mqtt["username"] | MQTT_USER, sizeof(config.Mqtt.Username)); strlcpy(config.Mqtt.Password, mqtt["password"] | MQTT_PASSWORD, sizeof(config.Mqtt.Password)); strlcpy(config.Mqtt.Topic, mqtt["topic"] | MQTT_TOPIC, sizeof(config.Mqtt.Topic)); diff --git a/src/MqttHandleHass.cpp b/src/MqttHandleHass.cpp index 21b79f38..84edf6e4 100644 --- a/src/MqttHandleHass.cpp +++ b/src/MqttHandleHass.cpp @@ -73,8 +73,8 @@ void MqttHandleHassClass::publishConfig() publishInverterButton(inv, "Turn Inverter On", "mdi:power-plug", "config", "", "cmd/power", "1"); publishInverterButton(inv, "Restart Inverter", "", "config", "restart", "cmd/restart", "1"); - publishInverterNumber(inv, "Limit NonPersistent Relative", "mdi:speedometer", "config", "cmd/limit_nonpersistent_relative", "status/limit_relative", "%", 0, 100); - publishInverterNumber(inv, "Limit Persistent Relative", "mdi:speedometer", "config", "cmd/limit_persistent_relative", "status/limit_relative", "%", 0, 100); + publishInverterNumber(inv, "Limit NonPersistent Relative", "mdi:speedometer", "config", "cmd/limit_nonpersistent_relative", "status/limit_relative", "%", 0, 100, 0.1); + publishInverterNumber(inv, "Limit Persistent Relative", "mdi:speedometer", "config", "cmd/limit_persistent_relative", "status/limit_relative", "%", 0, 100, 0.1); publishInverterNumber(inv, "Limit NonPersistent Absolute", "mdi:speedometer", "config", "cmd/limit_nonpersistent_absolute", "status/limit_absolute", "W", 0, MAX_INVERTER_LIMIT); publishInverterNumber(inv, "Limit Persistent Absolute", "mdi:speedometer", "config", "cmd/limit_persistent_absolute", "status/limit_absolute", "W", 0, MAX_INVERTER_LIMIT); @@ -236,7 +236,7 @@ void MqttHandleHassClass::publishInverterButton(std::shared_ptr inv, const char* caption, const char* icon, const char* category, const char* commandTopic, const char* stateTopic, const char* unitOfMeasure, - const int16_t min, const int16_t max) + const int16_t min, const int16_t max, float step) { const String serial = inv->serialString(); @@ -264,6 +264,7 @@ void MqttHandleHassClass::publishInverterNumber( root["unit_of_meas"] = unitOfMeasure; root["min"] = min; root["max"] = max; + root["step"] = step; createInverterInfo(root, inv); diff --git a/src/MqttHandleInverter.cpp b/src/MqttHandleInverter.cpp index 624033e1..d099b444 100644 --- a/src/MqttHandleInverter.cpp +++ b/src/MqttHandleInverter.cpp @@ -183,7 +183,7 @@ void MqttHandleInverterClass::onMqttMessage(const espMqttClientTypes::MessagePro char* strlimit = new char[len + 1]; memcpy(strlimit, payload, len); strlimit[len] = '\0'; - const int32_t payload_val = strtol(strlimit, NULL, 10); + const float payload_val = strtof(strlimit, NULL); delete[] strlimit; if (payload_val < 0) { @@ -193,17 +193,17 @@ void MqttHandleInverterClass::onMqttMessage(const espMqttClientTypes::MessagePro if (!strcmp(setting, TOPIC_SUB_LIMIT_PERSISTENT_RELATIVE)) { // Set inverter limit relative persistent - MessageOutput.printf("Limit Persistent: %d %%\r\n", payload_val); + MessageOutput.printf("Limit Persistent: %.1f %%\r\n", payload_val); inv->sendActivePowerControlRequest(payload_val, PowerLimitControlType::RelativPersistent); } else if (!strcmp(setting, TOPIC_SUB_LIMIT_PERSISTENT_ABSOLUTE)) { // Set inverter limit absolute persistent - MessageOutput.printf("Limit Persistent: %d W\r\n", payload_val); + MessageOutput.printf("Limit Persistent: %.1f W\r\n", payload_val); inv->sendActivePowerControlRequest(payload_val, PowerLimitControlType::AbsolutPersistent); } else if (!strcmp(setting, TOPIC_SUB_LIMIT_NONPERSISTENT_RELATIVE)) { // Set inverter limit relative non persistent - MessageOutput.printf("Limit Non-Persistent: %d %%\r\n", payload_val); + MessageOutput.printf("Limit Non-Persistent: %.1f %%\r\n", payload_val); if (!properties.retain) { inv->sendActivePowerControlRequest(payload_val, PowerLimitControlType::RelativNonPersistent); } else { @@ -212,7 +212,7 @@ void MqttHandleInverterClass::onMqttMessage(const espMqttClientTypes::MessagePro } else if (!strcmp(setting, TOPIC_SUB_LIMIT_NONPERSISTENT_ABSOLUTE)) { // Set inverter limit absolute non persistent - MessageOutput.printf("Limit Non-Persistent: %d W\r\n", payload_val); + MessageOutput.printf("Limit Non-Persistent: %.1f W\r\n", payload_val); if (!properties.retain) { inv->sendActivePowerControlRequest(payload_val, PowerLimitControlType::AbsolutNonPersistent); } else { @@ -221,8 +221,8 @@ void MqttHandleInverterClass::onMqttMessage(const espMqttClientTypes::MessagePro } else if (!strcmp(setting, TOPIC_SUB_POWER)) { // Turn inverter on or off - MessageOutput.printf("Set inverter power to: %d\r\n", payload_val); - inv->sendPowerControlRequest(payload_val > 0); + MessageOutput.printf("Set inverter power to: %d\r\n", static_cast(payload_val)); + inv->sendPowerControlRequest(static_cast(payload_val) > 0); } else if (!strcmp(setting, TOPIC_SUB_RESTART)) { // Restart inverter @@ -230,7 +230,7 @@ void MqttHandleInverterClass::onMqttMessage(const espMqttClientTypes::MessagePro if (!properties.retain && payload_val == 1) { inv->sendRestartControlRequest(); } else { - MessageOutput.println("Ignored because retained"); + MessageOutput.println("Ignored because retained or numeric value not '1'"); } } } diff --git a/src/MqttSettings.cpp b/src/MqttSettings.cpp index 82d0540b..b86c51f5 100644 --- a/src/MqttSettings.cpp +++ b/src/MqttSettings.cpp @@ -115,7 +115,7 @@ void MqttSettingsClass::performConnect() MessageOutput.println("Connecting to MQTT..."); const CONFIG_T& config = Configuration.get(); const String willTopic = getPrefix() + config.Mqtt.Lwt.Topic; - const String clientId = NetworkSettings.getApName(); + String clientId = getClientId(); if (config.Mqtt.Tls.Enabled) { static_cast(_mqttClient)->setCACert(config.Mqtt.Tls.RootCaCert); static_cast(_mqttClient)->setServer(config.Mqtt.Hostname, config.Mqtt.Port); @@ -180,6 +180,15 @@ String MqttSettingsClass::getPrefix() const return Configuration.get().Mqtt.Topic; } +String MqttSettingsClass::getClientId() +{ + String clientId = Configuration.get().Mqtt.ClientId; + if (clientId == "") { + clientId = NetworkSettings.getApName(); + } + return clientId; +} + void MqttSettingsClass::publish(const String& subtopic, const String& payload) { String topic = getPrefix(); diff --git a/src/NetworkSettings.cpp b/src/NetworkSettings.cpp index dac3ecb0..8ae1c909 100644 --- a/src/NetworkSettings.cpp +++ b/src/NetworkSettings.cpp @@ -27,6 +27,8 @@ void NetworkSettingsClass::init(Scheduler& scheduler) WiFi.setScanMethod(WIFI_ALL_CHANNEL_SCAN); WiFi.setSortMethod(WIFI_CONNECT_AP_BY_SIGNAL); + WiFi.disconnect(true, true); + WiFi.onEvent(std::bind(&NetworkSettingsClass::NetworkEvent, this, _1)); setupMode(); @@ -77,6 +79,7 @@ void NetworkSettingsClass::NetworkEvent(const WiFiEvent_t event) MessageOutput.println("WiFi disconnected"); if (_networkMode == network_mode::WiFi) { MessageOutput.println("Try reconnecting"); + WiFi.disconnect(true, true); WiFi.reconnect(); raiseEvent(network_event::NETWORK_DISCONNECTED); } diff --git a/src/WebApi_limit.cpp b/src/WebApi_limit.cpp index 9a622dea..6a6c90ca 100644 --- a/src/WebApi_limit.cpp +++ b/src/WebApi_limit.cpp @@ -83,7 +83,7 @@ void WebApiLimitClass::onLimitPost(AsyncWebServerRequest* request) return; } - if (root["limit_value"].as() > MAX_INVERTER_LIMIT) { + if (root["limit_value"].as() > MAX_INVERTER_LIMIT) { retMsg["message"] = "Limit must between 0 and " STR(MAX_INVERTER_LIMIT) "!"; retMsg["code"] = WebApiError::LimitInvalidLimit; retMsg["param"]["max"] = MAX_INVERTER_LIMIT; @@ -102,7 +102,7 @@ void WebApiLimitClass::onLimitPost(AsyncWebServerRequest* request) return; } - uint16_t limit = root["limit_value"].as(); + float limit = root["limit_value"].as(); PowerLimitControlType type = root["limit_type"].as(); auto inv = Hoymiles.getInverterBySerial(serial); diff --git a/src/WebApi_mqtt.cpp b/src/WebApi_mqtt.cpp index 9d1a7dbe..af40643e 100644 --- a/src/WebApi_mqtt.cpp +++ b/src/WebApi_mqtt.cpp @@ -34,6 +34,7 @@ void WebApiMqttClass::onMqttStatus(AsyncWebServerRequest* request) root["mqtt_enabled"] = config.Mqtt.Enabled; root["mqtt_hostname"] = config.Mqtt.Hostname; root["mqtt_port"] = config.Mqtt.Port; + root["mqtt_clientid"] = MqttSettings.getClientId(); root["mqtt_username"] = config.Mqtt.Username; root["mqtt_topic"] = config.Mqtt.Topic; root["mqtt_connected"] = MqttSettings.getConnected(); @@ -67,6 +68,7 @@ void WebApiMqttClass::onMqttAdminGet(AsyncWebServerRequest* request) root["mqtt_enabled"] = config.Mqtt.Enabled; root["mqtt_hostname"] = config.Mqtt.Hostname; root["mqtt_port"] = config.Mqtt.Port; + root["mqtt_clientid"] = config.Mqtt.ClientId; root["mqtt_username"] = config.Mqtt.Username; root["mqtt_password"] = config.Mqtt.Password; root["mqtt_topic"] = config.Mqtt.Topic; @@ -108,6 +110,7 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request) if (!(root.containsKey("mqtt_enabled") && root.containsKey("mqtt_hostname") && root.containsKey("mqtt_port") + && root.containsKey("mqtt_clientid") && root.containsKey("mqtt_username") && root.containsKey("mqtt_password") && root.containsKey("mqtt_topic") @@ -142,6 +145,13 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request) return; } + if (root["mqtt_clientid"].as().length() > MQTT_MAX_CLIENTID_STRLEN) { + retMsg["message"] = "Client ID must not be longer than " STR(MQTT_MAX_CLIENTID_STRLEN) " characters!"; + retMsg["code"] = WebApiError::MqttClientIdLength; + retMsg["param"]["max"] = MQTT_MAX_CLIENTID_STRLEN; + WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__); + return; + } if (root["mqtt_username"].as().length() > MQTT_MAX_USERNAME_STRLEN) { retMsg["message"] = "Username must not be longer than " STR(MQTT_MAX_USERNAME_STRLEN) " characters!"; retMsg["code"] = WebApiError::MqttUsernameLength; @@ -271,6 +281,7 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request) strlcpy(config.Mqtt.Tls.ClientKey, root["mqtt_client_key"].as().c_str(), sizeof(config.Mqtt.Tls.ClientKey)); config.Mqtt.Port = root["mqtt_port"].as(); strlcpy(config.Mqtt.Hostname, root["mqtt_hostname"].as().c_str(), sizeof(config.Mqtt.Hostname)); + strlcpy(config.Mqtt.ClientId, root["mqtt_clientid"].as().c_str(), sizeof(config.Mqtt.ClientId)); strlcpy(config.Mqtt.Username, root["mqtt_username"].as().c_str(), sizeof(config.Mqtt.Username)); strlcpy(config.Mqtt.Password, root["mqtt_password"].as().c_str(), sizeof(config.Mqtt.Password)); strlcpy(config.Mqtt.Lwt.Topic, root["mqtt_lwt_topic"].as().c_str(), sizeof(config.Mqtt.Lwt.Topic)); diff --git a/src/WebApi_webapp.cpp b/src/WebApi_webapp.cpp index b8b81385..b5335b02 100644 --- a/src/WebApi_webapp.cpp +++ b/src/WebApi_webapp.cpp @@ -42,7 +42,7 @@ void WebApiWebappClass::responseBinaryDataWithETagCache(AsyncWebServerRequest *r if (eTagMatch) { response = request->beginResponse(304); } else { - response = request->beginResponse_P(200, contentType, content, len); + response = request->beginResponse(200, contentType, content, len); if (contentEncoding.length() > 0) { response->addHeader("Content-Encoding", contentEncoding); } diff --git a/webapp/.prettierrc.json b/webapp/.prettierrc.json new file mode 100644 index 00000000..678c251b --- /dev/null +++ b/webapp/.prettierrc.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://json.schemastore.org/prettierrc", + "semi": true, + "tabWidth": 4, + "singleQuote": true, + "printWidth": 120, + "trailingComma": "es5" +} diff --git a/webapp/env.d.ts b/webapp/env.d.ts index 11f02fe2..038f2927 100644 --- a/webapp/env.d.ts +++ b/webapp/env.d.ts @@ -1 +1,9 @@ /// + +import { Router, Route } from 'vue-router' +declare module '@vue/runtime-core' { + interface ComponentCustomProperties { + $router: Router + $route: Route + } +} diff --git a/webapp/eslint.config.js b/webapp/eslint.config.js index 91657b98..9a2aaecb 100644 --- a/webapp/eslint.config.js +++ b/webapp/eslint.config.js @@ -30,7 +30,7 @@ export default [ "**/*.mts", ], languageOptions: { - ecmaVersion: 'latest' + ecmaVersion: 2022 }, } ] diff --git a/webapp/package.json b/webapp/package.json index bb66b4ac..19608f39 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -9,7 +9,8 @@ "preview": "vite preview --port 4173", "build-only": "vite build", "type-check": "vue-tsc --noEmit", - "lint": "eslint ." + "lint": "eslint .", + "format": "prettier --write src/" }, "dependencies": { "@popperjs/core": "^2.11.8", @@ -18,31 +19,32 @@ "mitt": "^3.0.1", "sortablejs": "^1.15.2", "spark-md5": "^3.0.2", - "vue": "^3.4.27", + "vue": "^3.4.35", "vue-i18n": "^9.13.1", - "vue-router": "^4.3.3" + "vue-router": "^4.4.2" }, "devDependencies": { "@intlify/unplugin-vue-i18n": "^4.0.0", "@tsconfig/node18": "^18.2.4", "@types/bootstrap": "^5.2.10", - "@types/node": "^20.14.2", + "@types/node": "^22.0.2", "@types/pulltorefreshjs": "^0.1.7", "@types/sortablejs": "^1.15.8", "@types/spark-md5": "^3.0.4", - "@vitejs/plugin-vue": "^5.0.5", + "@vitejs/plugin-vue": "^5.1.2", "@vue/eslint-config-typescript": "^13.0.0", "@vue/tsconfig": "^0.5.1", - "eslint": "^9.4.0", - "eslint-plugin-vue": "^9.26.0", + "eslint": "^9.8.0", + "eslint-plugin-vue": "^9.27.0", "npm-run-all": "^4.1.5", + "prettier": "^3.3.3", "pulltorefreshjs": "^0.1.22", - "sass": "^1.77.4", - "terser": "^5.31.1", - "typescript": "^5.4.5", - "vite": "^5.2.13", + "sass": "^1.77.6", + "terser": "^5.31.3", + "typescript": "^5.5.4", + "vite": "^5.3.5", "vite-plugin-compression": "^0.5.1", "vite-plugin-css-injected-by-js": "^3.5.1", - "vue-tsc": "^2.0.21" + "vue-tsc": "^2.0.29" } } diff --git a/webapp/src/App.vue b/webapp/src/App.vue index 6fa6eeaa..200ce669 100644 --- a/webapp/src/App.vue +++ b/webapp/src/App.vue @@ -5,10 +5,10 @@ diff --git a/webapp/src/components/DevInfo.vue b/webapp/src/components/DevInfo.vue index 024566ad..7a998e1c 100644 --- a/webapp/src/components/DevInfo.vue +++ b/webapp/src/components/DevInfo.vue @@ -1,8 +1,7 @@