diff --git a/interface/package.json b/interface/package.json
index f312babdb..3c5bf6560 100644
--- a/interface/package.json
+++ b/interface/package.json
@@ -30,7 +30,7 @@
"@mui/material": "^5.15.19",
"@table-library/react-table-library": "4.1.7",
"@types/lodash-es": "^4.17.12",
- "@types/node": "^20.12.12",
+ "@types/node": "^20.12.13",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@types/react-router-dom": "^5.3.3",
diff --git a/interface/src/project/Modules.tsx b/interface/src/project/Modules.tsx
index 55affc530..9ca592cc6 100644
--- a/interface/src/project/Modules.tsx
+++ b/interface/src/project/Modules.tsx
@@ -141,6 +141,14 @@ const Modules: FC = () => {
useLayoutTitle('Modules');
+ if (modules.length === 0) {
+ return (
+
+ No modules detected
+
+ );
+ }
+
return (
{
{blocker ? : null}
- Activate or de-activate EMS-ESP library modules (** experimental **)
+ Activate or de-activate EMS-ESP library modules by selecting (**
+ experimental **)
{renderModules()}
diff --git a/interface/yarn.lock b/interface/yarn.lock
index 91ff98290..e9c3fa42e 100644
--- a/interface/yarn.lock
+++ b/interface/yarn.lock
@@ -1738,12 +1738,12 @@ __metadata:
languageName: node
linkType: hard
-"@types/node@npm:^20.12.12":
- version: 20.12.12
- resolution: "@types/node@npm:20.12.12"
+"@types/node@npm:^20.12.13":
+ version: 20.12.13
+ resolution: "@types/node@npm:20.12.13"
dependencies:
undici-types: "npm:~5.26.4"
- checksum: 10c0/f374b763c744e8f16e4f38cf6e2c0eef31781ec9228c9e43a6f267880fea420fab0a238b59f10a7cb3444e49547c5e3785787e371fc242307310995b21988812
+ checksum: 10c0/2ac92bb631dbddfb560eb3ba4eedbb1c688044a0130bc1ef032f5c0f20148ac7c9aa3c5aaa5a9787b6c4c6299847d754b96ee8c9def951481ba6628c46b683f5
languageName: node
linkType: hard
@@ -1974,7 +1974,7 @@ __metadata:
"@trivago/prettier-plugin-sort-imports": "npm:^4.3.0"
"@types/babel__core": "npm:^7"
"@types/lodash-es": "npm:^4.17.12"
- "@types/node": "npm:^20.12.12"
+ "@types/node": "npm:^20.12.13"
"@types/react": "npm:^18.3.3"
"@types/react-dom": "npm:^18.3.0"
"@types/react-router-dom": "npm:^5.3.3"
diff --git a/lib/framework/FSPersistence.h b/lib/framework/FSPersistence.h
index 3ccf53a53..a7ad681a8 100644
--- a/lib/framework/FSPersistence.h
+++ b/lib/framework/FSPersistence.h
@@ -26,6 +26,11 @@ class FSPersistence {
if (error == DeserializationError::Ok && jsonDocument.is()) {
JsonObject jsonObject = jsonDocument.as();
_statefulService->updateWithoutPropagation(jsonObject, _stateUpdater);
+#ifdef EMSESP_DEBUG
+ Serial.printf("Reading settings from %s ", _filePath);
+ serializeJson(jsonDocument, Serial);
+ Serial.println();
+#endif
settingsFile.close();
return;
}
@@ -65,9 +70,11 @@ class FSPersistence {
return false;
}
-// serialize the data to the file
+ // serialize the data to the file
#ifdef EMSESP_DEBUG
- Serial.println("Writing settings to " + String(_filePath));
+ Serial.printf("Writing settings to %s ", _filePath);
+ serializeJson(jsonDocument, Serial);
+ Serial.println();
#endif
serializeJson(jsonDocument, settingsFile);
settingsFile.close();
diff --git a/mock-api/rest_server.ts b/mock-api/rest_server.ts
index e8e0a4abe..8ee5a404a 100644
--- a/mock-api/rest_server.ts
+++ b/mock-api/rest_server.ts
@@ -2130,6 +2130,7 @@ let emsesp_schedule = {
// SCHEDULE
let emsesp_modules = {
+ // 'modules': []
"modules": [
{
id: 1,
@@ -2511,15 +2512,15 @@ router
// Modules
.post(EMSESP_MODULES_ENDPOINT, async (request: any) => {
const content = await request.json();
- // TODO find the one and toggle it
- console.log('modules saved', emsesp_modules);
- /*
- const dd_objIndex = dd.data.findIndex((obj: any) => obj.id.slice(2) === fullname);
- if (dd_objIndex !== -1) {
- let changed = new Boolean(false);
- emsesp_modules = content;
-
- */
+ let modules = content.modules;
+ for (let i = 0; i < modules.length; i++) {
+ const name = modules[i].name;
+ const objIndex = emsesp_modules.modules.findIndex((obj: any) => obj.name === name);
+ if (objIndex !== -1) {
+ emsesp_modules.modules[objIndex].enabled = modules[i].enabled;
+ }
+ }
+ console.log('modules updated', emsesp_modules);
return status(200);
})
.get(EMSESP_MODULES_ENDPOINT, () => emsesp_modules)
diff --git a/src/emsesp.cpp b/src/emsesp.cpp
index 0a1b2706d..01e55dfa4 100644
--- a/src/emsesp.cpp
+++ b/src/emsesp.cpp
@@ -37,12 +37,14 @@ WebSettingsService EMSESP::webSettingsService = WebSettingsService(&we
WebCustomizationService EMSESP::webCustomizationService = WebCustomizationService(&webServer, &dummyFS, EMSESP::esp8266React.getSecurityManager());
WebSchedulerService EMSESP::webSchedulerService = WebSchedulerService(&webServer, &dummyFS, EMSESP::esp8266React.getSecurityManager());
WebCustomEntityService EMSESP::webCustomEntityService = WebCustomEntityService(&webServer, &dummyFS, EMSESP::esp8266React.getSecurityManager());
+WebModulesService EMSESP::webModulesService = WebModulesService(&webServer, &dummyFS, EMSESP::esp8266React.getSecurityManager());
#else
ESP8266React EMSESP::esp8266React(&webServer, &LittleFS);
WebSettingsService EMSESP::webSettingsService = WebSettingsService(&webServer, &LittleFS, EMSESP::esp8266React.getSecurityManager());
WebCustomizationService EMSESP::webCustomizationService = WebCustomizationService(&webServer, &LittleFS, EMSESP::esp8266React.getSecurityManager());
WebSchedulerService EMSESP::webSchedulerService = WebSchedulerService(&webServer, &LittleFS, EMSESP::esp8266React.getSecurityManager());
WebCustomEntityService EMSESP::webCustomEntityService = WebCustomEntityService(&webServer, &LittleFS, EMSESP::esp8266React.getSecurityManager());
+WebModulesService EMSESP::webModulesService = WebModulesService(&webServer, &LittleFS, EMSESP::esp8266React.getSecurityManager());
#endif
WebActivityService EMSESP::webActivityService = WebActivityService(&webServer, EMSESP::esp8266React.getSecurityManager());
@@ -75,7 +77,6 @@ TemperatureSensor EMSESP::temperaturesensor_; // Temperature sensors
AnalogSensor EMSESP::analogsensor_; // Analog sensors
Shower EMSESP::shower_; // Shower logic
Preferences EMSESP::nvs_; // NV Storage
-ModuleLibrary EMSESP::module_; // Module Library
// static/common variables
uint16_t EMSESP::watch_id_ = WATCH_ID_NONE; // for when log is TRACE. 0 means no trace set
@@ -1635,6 +1636,8 @@ void EMSESP::start() {
analogsensor_.start(); // Analog external sensors
webLogService.start(); // apply settings to weblog service
+ webModulesService.begin(); // setup the external library modules
+
// Load our library of known devices into stack mem. Names are stored in Flash memory
device_library_ = {
#include "device_library.h"
@@ -1646,8 +1649,6 @@ void EMSESP::start() {
#endif
webServer.begin(); // start the web server
-
- module_.start(this); // setup the external library modules
}
// main loop calling all services
@@ -1666,7 +1667,7 @@ void EMSESP::loop() {
publish_all_loop(); // with HA messages in parts to avoid flooding the mqtt queue
mqtt_.loop(); // sends out anything in the MQTT queue
webSchedulerService.loop(); // handle any scheduled jobs
- module_.loop(); // loop the external library modules
+ webModulesService.loop(); // loop through the external library modules
// force a query on the EMS devices to fetch latest data at a set interval (1 min)
scheduled_fetch_values();
diff --git a/src/emsesp.h b/src/emsesp.h
index 71a1c87e6..bfc83c816 100644
--- a/src/emsesp.h
+++ b/src/emsesp.h
@@ -49,6 +49,7 @@
#include "web/WebAPIService.h"
#include "web/WebLogService.h"
#include "web/WebCustomEntityService.h"
+#include "web/WebModulesService.h"
#include "emsdevicevalue.h"
#include "emsdevice.h"
@@ -222,7 +223,6 @@ class EMSESP {
static RxService rxservice_;
static TxService txservice_;
static Preferences nvs_;
- static ModuleLibrary module_;
// web controllers
static ESP8266React esp8266React;
@@ -235,6 +235,7 @@ class EMSESP {
static WebCustomizationService webCustomizationService;
static WebSchedulerService webSchedulerService;
static WebCustomEntityService webCustomEntityService;
+ static WebModulesService webModulesService;
private:
static std::string device_tostring(const uint8_t device_id);
@@ -280,7 +281,6 @@ class EMSESP {
#endif
protected:
- // EMSESP();
static uuid::log::Logger logger_;
};
diff --git a/src/web/WebModulesService.cpp b/src/web/WebModulesService.cpp
new file mode 100644
index 000000000..b9cc9f350
--- /dev/null
+++ b/src/web/WebModulesService.cpp
@@ -0,0 +1,82 @@
+/*
+ * EMS-ESP - https://github.com/emsesp/EMS-ESP
+ * Copyright 2020-2024 Paul Derbyshire
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "emsesp.h"
+#include "WebModulesService.h"
+
+namespace emsesp {
+
+ModuleLibrary moduleLibrary; // Module Library
+EMSESP * emsesp_; // forward declaration
+
+WebModulesService::WebModulesService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
+ : _httpEndpoint(WebModules::read, WebModules::update, this, server, EMSESP_MODULES_SERVICE_PATH, securityManager, AuthenticationPredicates::IS_AUTHENTICATED)
+ , _fsPersistence(WebModules::read, WebModules::update, this, fs, EMSESP_MODULES_FILE) {
+}
+
+// load the settings when the service starts
+void WebModulesService::begin() {
+ EMSESP::logger().info("Starting Modules service");
+ moduleLibrary.start(emsesp_);
+
+ _fsPersistence.readFromFS(); // read the file from FS
+}
+
+void WebModulesService::loop() {
+ moduleLibrary.loop(); // loop the external library modules
+}
+
+// this creates the modules file, saving it to the FS
+// and also calls when the Modules web page is refreshed
+void WebModules::read(WebModules & webModules, JsonObject root) {
+ // EMSESP::logger().debug("module read called"); // TODO remove
+
+ JsonDocument doc_modules;
+ JsonObject root_modules = doc_modules.to();
+ moduleLibrary.list(root_modules); // get list the external library modules, put in root json object
+
+ JsonArray modules = root["modules"].to();
+ uint8_t counter = 0;
+ for (const JsonObject module : root_modules["modules"].as()) {
+ JsonObject mi = modules.add();
+ mi["id"] = counter++; // id is only used to render the table and must be unique
+ mi["name"] = module["name"].as();
+ mi["author"] = module["author"].as();
+ mi["version"] = module["version"].as();
+ mi["status"] = module["status"].as();
+ mi["enabled"] = module["enabled"].as();
+ }
+}
+
+// read any Module settings from the FS settings
+// and then apply the enable/disable
+// it's also called on a save
+StateUpdateResult WebModules::update(JsonObject root, WebModules & webModules) {
+ // EMSESP::logger().debug("module update called"); // TODO remove
+
+ if (root["modules"].is()) {
+ for (const JsonObject module : root["modules"].as()) {
+ // set enabled/disabled
+ moduleLibrary.enable(module["name"], module["enabled"].as());
+ }
+ }
+
+ return StateUpdateResult::CHANGED;
+}
+
+} // namespace emsesp
diff --git a/src/web/WebModulesService.h b/src/web/WebModulesService.h
new file mode 100644
index 000000000..715424a42
--- /dev/null
+++ b/src/web/WebModulesService.h
@@ -0,0 +1,50 @@
+/*
+ * EMS-ESP - https://github.com/emsesp/EMS-ESP
+ * Copyright 2020-2024 Paul Derbyshire
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef WebModulesService_h
+#define WebModulesService_h
+
+#define EMSESP_MODULES_FILE "/config/emsespModules.json"
+#define EMSESP_MODULES_SERVICE_PATH "/rest/modules" // GET and POST
+
+namespace emsesp {
+
+class WebModules {
+ public:
+ static void read(WebModules & webModules, JsonObject root);
+ static StateUpdateResult update(JsonObject root, WebModules & webModules);
+};
+
+class WebModulesService : public StatefulService {
+ public:
+ WebModulesService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager);
+
+ void begin();
+ void loop();
+
+#ifndef EMSESP_STANDALONE
+ private:
+#endif
+
+ HttpEndpoint _httpEndpoint;
+ FSPersistence _fsPersistence;
+};
+
+} // namespace emsesp
+
+#endif
\ No newline at end of file