From 93f716cbb0397eea4b59c8f90e86722ffbf9c75f Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Fri, 10 Jan 2025 11:39:43 +0000 Subject: [PATCH 1/5] Add name and enabled support to usermods - MM feature --- wled00/fcn_declare.h | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index cb21e8c2ed..2ec4ef6e81 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -380,6 +380,9 @@ const unsigned int um_data_size = sizeof(um_data_t); // 12 bytes class Usermod { protected: um_data_t *um_data; // um_data should be allocated using new in (derived) Usermod's setup() or constructor + bool enabled = false; + const char *_name; + public: Usermod() { um_data = nullptr; } virtual ~Usermod() { if (um_data) delete um_data; } @@ -393,8 +396,20 @@ class Usermod { virtual void addToJsonState(JsonObject& obj) {} // add JSON objects for WLED state virtual void addToJsonInfo(JsonObject& obj) {} // add JSON objects for UI Info page virtual void readFromJsonState(JsonObject& obj) {} // process JSON messages received from web server - virtual void addToConfig(JsonObject& obj) {} // add JSON entries that go to cfg.json - virtual bool readFromConfig(JsonObject& obj) { return true; } // Note as of 2021-06 readFromConfig() now needs to return a bool, see usermod_v2_example.h + virtual void addToConfig(JsonObject& obj) { + if(_name) { + // add JSON entries that go to cfg.json + JsonObject top = obj.createNestedObject(FPSTR(_name)); + top[FPSTR("enabled")] = enabled; + } + } + virtual bool readFromConfig(JsonObject& obj) { // Note as of 2021-06 readFromConfig() now needs to return a bool, see usermod_v2_example.h + if(!_name) { + return true; + } + JsonObject top = obj[FPSTR(_name)]; + return !top.isNull() && getJsonValue(top[FPSTR("enabled")], enabled); + } virtual void onMqttConnect(bool sessionPresent) {} // fired when MQTT connection is established (so usermod can subscribe) virtual bool onMqttMessage(char* topic, char* payload) { return false; } // fired upon MQTT message received (wled topic) virtual bool onEspNowMessage(uint8_t* sender, uint8_t* payload, uint8_t len) { return false; } // fired upon ESP-NOW message received From 3a6b438ff6db59dadddbad939e7932d25be85cb6 Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Sat, 11 Jan 2025 12:42:38 +0000 Subject: [PATCH 2/5] Move enabled flag from example to base UserMod class --- usermods/EXAMPLE_v2/usermod_v2_example.h | 11 ----------- wled00/fcn_declare.h | 3 +++ 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/usermods/EXAMPLE_v2/usermod_v2_example.h b/usermods/EXAMPLE_v2/usermod_v2_example.h index df05f3e3dc..b56696e92a 100644 --- a/usermods/EXAMPLE_v2/usermod_v2_example.h +++ b/usermods/EXAMPLE_v2/usermod_v2_example.h @@ -26,7 +26,6 @@ class MyExampleUsermod : public Usermod { private: // Private class members. You can declare variables and functions only accessible to your usermod here - bool enabled = false; bool initDone = false; unsigned long lastTime = 0; @@ -54,16 +53,6 @@ class MyExampleUsermod : public Usermod { // non WLED related methods, may be used for data exchange between usermods (non-inline methods should be defined out of class) - /** - * Enable/Disable the usermod - */ - inline void enable(bool enable) { enabled = enable; } - - /** - * Get usermod enabled/disabled state - */ - inline bool isEnabled() { return enabled; } - // in such case add the following to another usermod: // in private vars: // #ifdef USERMOD_EXAMPLE diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index 2ec4ef6e81..e8c4e2804c 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -416,6 +416,9 @@ class Usermod { virtual void onUpdateBegin(bool) {} // fired prior to and after unsuccessful firmware update virtual void onStateChange(uint8_t mode) {} // fired upon WLED state change virtual uint16_t getId() {return USERMOD_ID_UNSPECIFIED;} + virtual void enable(bool enable) { enabled = enable; } + inline bool isEnabled() { return enabled; } + // API shims private: From 165a8c11f93252e1a6c1547dcdfbb499fa3fdbbb Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Sat, 11 Jan 2025 12:45:19 +0000 Subject: [PATCH 3/5] Only delegate from UserModManager to UserMod of that Mod is enabled at boot time --- usermods/EXAMPLE_v2/usermod_v2_example.h | 7 ++--- wled00/fcn_declare.h | 4 +-- wled00/um_manager.cpp | 37 +++++++++++++----------- 3 files changed, 25 insertions(+), 23 deletions(-) diff --git a/usermods/EXAMPLE_v2/usermod_v2_example.h b/usermods/EXAMPLE_v2/usermod_v2_example.h index b56696e92a..058643f317 100644 --- a/usermods/EXAMPLE_v2/usermod_v2_example.h +++ b/usermods/EXAMPLE_v2/usermod_v2_example.h @@ -103,9 +103,9 @@ class MyExampleUsermod : public Usermod { * Instead, use a timer check as shown here. */ void loop() override { - // if usermod is disabled or called during strip updating just exit + // if called during strip updating just exit // NOTE: on very long strips strip.isUpdating() may always return true so update accordingly - if (!enabled || strip.isUpdating()) return; + if (strip.isUpdating()) return; // do your magic here if (millis() - lastTime > 1000) { @@ -303,8 +303,7 @@ class MyExampleUsermod : public Usermod { bool handleButton(uint8_t b) override { yield(); // ignore certain button types as they may have other consequences - if (!enabled - || buttonType[b] == BTN_TYPE_NONE + if (buttonType[b] == BTN_TYPE_NONE || buttonType[b] == BTN_TYPE_RESERVED || buttonType[b] == BTN_TYPE_PIR_SENSOR || buttonType[b] == BTN_TYPE_ANALOG diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index e8c4e2804c..dc590dba00 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -380,7 +380,7 @@ const unsigned int um_data_size = sizeof(um_data_t); // 12 bytes class Usermod { protected: um_data_t *um_data; // um_data should be allocated using new in (derived) Usermod's setup() or constructor - bool enabled = false; + bool enabled = true; // maintain compatibility for usermods that don't use this const char *_name; public: @@ -437,7 +437,7 @@ class Usermod { namespace UsermodManager { extern byte numMods; - + extern byte numModsEnabled; void loop(); void handleOverlayDraw(); bool handleButton(uint8_t b); diff --git a/wled00/um_manager.cpp b/wled00/um_manager.cpp index 1fdb6d688b..68bbbf4781 100644 --- a/wled00/um_manager.cpp +++ b/wled00/um_manager.cpp @@ -5,13 +5,15 @@ static Usermod* ums[WLED_MAX_USERMODS] = {nullptr}; byte UsermodManager::numMods = 0; +static Usermod* umsEnabled[WLED_MAX_USERMODS] = {nullptr}; +byte UsermodManager::numModsEnabled = 0; //Usermod Manager internals -void UsermodManager::setup() { for (unsigned i = 0; i < numMods; i++) ums[i]->setup(); } -void UsermodManager::connected() { for (unsigned i = 0; i < numMods; i++) ums[i]->connected(); } -void UsermodManager::loop() { for (unsigned i = 0; i < numMods; i++) ums[i]->loop(); } -void UsermodManager::handleOverlayDraw() { for (unsigned i = 0; i < numMods; i++) ums[i]->handleOverlayDraw(); } -void UsermodManager::appendConfigData(Print& dest) { for (unsigned i = 0; i < numMods; i++) ums[i]->appendConfigData(dest); } +void UsermodManager::setup() { for (Usermod* um : umsEnabled) if(um) if(um) { um->setup(); } } +void UsermodManager::connected() { for (Usermod* um : umsEnabled) if(um) { um->connected(); } } +void UsermodManager::loop() { for (Usermod* um : umsEnabled) if(um) { um->loop(); } } +void UsermodManager::handleOverlayDraw() { for (Usermod* um : umsEnabled) if(um) { um->handleOverlayDraw(); } } +void UsermodManager::appendConfigData(Print& dest) { for (Usermod* um : ums) if(um) { um->appendConfigData(dest); } } bool UsermodManager::handleButton(uint8_t b) { bool overrideIO = false; for (unsigned i = 0; i < numMods; i++) { @@ -20,16 +22,16 @@ bool UsermodManager::handleButton(uint8_t b) { return overrideIO; } bool UsermodManager::getUMData(um_data_t **data, uint8_t mod_id) { - for (unsigned i = 0; i < numMods; i++) { - if (mod_id > 0 && ums[i]->getId() != mod_id) continue; // only get data form requested usermod if provided - if (ums[i]->getUMData(data)) return true; // if usermod does provide data return immediately (only one usermod can provide data at one time) + for (Usermod* um : umsEnabled) { + if (mod_id > 0 && um->getId() != mod_id) continue; // only get data form requested usermod if provided + if (um->getUMData(data)) return true; // if usermod does provide data return immediately (only one usermod can provide data at one time) } return false; } -void UsermodManager::addToJsonState(JsonObject& obj) { for (unsigned i = 0; i < numMods; i++) ums[i]->addToJsonState(obj); } -void UsermodManager::addToJsonInfo(JsonObject& obj) { for (unsigned i = 0; i < numMods; i++) ums[i]->addToJsonInfo(obj); } -void UsermodManager::readFromJsonState(JsonObject& obj) { for (unsigned i = 0; i < numMods; i++) ums[i]->readFromJsonState(obj); } -void UsermodManager::addToConfig(JsonObject& obj) { for (unsigned i = 0; i < numMods; i++) ums[i]->addToConfig(obj); } +void UsermodManager::addToJsonState(JsonObject& obj) { for (Usermod* um : umsEnabled) if(um) { um->addToJsonState(obj); } } +void UsermodManager::addToJsonInfo(JsonObject& obj) { for (Usermod* um : ums) if(um) { um->addToJsonInfo(obj); } } +void UsermodManager::readFromJsonState(JsonObject& obj) { for (Usermod* um : umsEnabled) if(um) { um->readFromJsonState(obj); } } +void UsermodManager::addToConfig(JsonObject& obj) { for (Usermod* um : ums) if(um) { um->addToConfig(obj); } } bool UsermodManager::readFromConfig(JsonObject& obj) { bool allComplete = true; for (unsigned i = 0; i < numMods; i++) { @@ -38,20 +40,20 @@ bool UsermodManager::readFromConfig(JsonObject& obj) { return allComplete; } #ifndef WLED_DISABLE_MQTT -void UsermodManager::onMqttConnect(bool sessionPresent) { for (unsigned i = 0; i < numMods; i++) ums[i]->onMqttConnect(sessionPresent); } +void UsermodManager::onMqttConnect(bool sessionPresent) { for (Usermod* um : umsEnabled) if(um) { um->onMqttConnect(sessionPresent); } } bool UsermodManager::onMqttMessage(char* topic, char* payload) { - for (unsigned i = 0; i < numMods; i++) if (ums[i]->onMqttMessage(topic, payload)) return true; + for (Usermod* um : umsEnabled) if (um && um->onMqttMessage(topic, payload)) return true; return false; } #endif #ifndef WLED_DISABLE_ESPNOW bool UsermodManager::onEspNowMessage(uint8_t* sender, uint8_t* payload, uint8_t len) { - for (unsigned i = 0; i < numMods; i++) if (ums[i]->onEspNowMessage(sender, payload, len)) return true; + for (Usermod* um : umsEnabled) if (um && um->onEspNowMessage(sender, payload, len)) return true; return false; } #endif -void UsermodManager::onUpdateBegin(bool init) { for (unsigned i = 0; i < numMods; i++) ums[i]->onUpdateBegin(init); } // notify usermods that update is to begin -void UsermodManager::onStateChange(uint8_t mode) { for (unsigned i = 0; i < numMods; i++) ums[i]->onStateChange(mode); } // notify usermods that WLED state changed +void UsermodManager::onUpdateBegin(bool init) { for (Usermod* um : umsEnabled) if(um) { um->onUpdateBegin(init); } } // notify usermods that update is to begin +void UsermodManager::onStateChange(uint8_t mode) { for (Usermod* um : umsEnabled) if(um) { um->onStateChange(mode); } } // notify usermods that WLED state changed /* * Enables usermods to lookup another Usermod. @@ -69,6 +71,7 @@ bool UsermodManager::add(Usermod* um) { if (numMods >= WLED_MAX_USERMODS || um == nullptr) return false; ums[numMods++] = um; + if(um->isEnabled()) umsEnabled[numModsEnabled++] = um; return true; } From 24ec249fa9382df45b2cd906a25559f36e036bdd Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Sat, 11 Jan 2025 13:20:20 +0000 Subject: [PATCH 4/5] read config before adding usermod to enabled usermod list --- wled00/fcn_declare.h | 1 - wled00/um_manager.cpp | 8 ++++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index dc590dba00..d6c2b1e277 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -437,7 +437,6 @@ class Usermod { namespace UsermodManager { extern byte numMods; - extern byte numModsEnabled; void loop(); void handleOverlayDraw(); bool handleButton(uint8_t b); diff --git a/wled00/um_manager.cpp b/wled00/um_manager.cpp index 68bbbf4781..c89c504aa8 100644 --- a/wled00/um_manager.cpp +++ b/wled00/um_manager.cpp @@ -6,7 +6,6 @@ static Usermod* ums[WLED_MAX_USERMODS] = {nullptr}; byte UsermodManager::numMods = 0; static Usermod* umsEnabled[WLED_MAX_USERMODS] = {nullptr}; -byte UsermodManager::numModsEnabled = 0; //Usermod Manager internals void UsermodManager::setup() { for (Usermod* um : umsEnabled) if(um) if(um) { um->setup(); } } @@ -36,6 +35,12 @@ bool UsermodManager::readFromConfig(JsonObject& obj) { bool allComplete = true; for (unsigned i = 0; i < numMods; i++) { if (!ums[i]->readFromConfig(obj)) allComplete = false; + if(!ums[i]->isEnabled()) { + umsEnabled[i] = ums[i]; + } + else { + umsEnabled[i] = nullptr; + } } return allComplete; } @@ -71,7 +76,6 @@ bool UsermodManager::add(Usermod* um) { if (numMods >= WLED_MAX_USERMODS || um == nullptr) return false; ums[numMods++] = um; - if(um->isEnabled()) umsEnabled[numModsEnabled++] = um; return true; } From dc2de23b1305d007ab3f141d0b100d45056325f1 Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Sat, 11 Jan 2025 14:24:31 +0000 Subject: [PATCH 5/5] UserModManager - use vectors to remove need for nullptr checks --- wled00/fcn_declare.h | 5 ++-- wled00/set.cpp | 1 + wled00/um_manager.cpp | 60 +++++++++++++++++++++---------------------- 3 files changed, 32 insertions(+), 34 deletions(-) diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index d6c2b1e277..bef8e67542 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -380,7 +380,7 @@ const unsigned int um_data_size = sizeof(um_data_t); // 12 bytes class Usermod { protected: um_data_t *um_data; // um_data should be allocated using new in (derived) Usermod's setup() or constructor - bool enabled = true; // maintain compatibility for usermods that don't use this + bool enabled = false; const char *_name; public: @@ -436,7 +436,6 @@ class Usermod { }; namespace UsermodManager { - extern byte numMods; void loop(); void handleOverlayDraw(); bool handleButton(uint8_t b); @@ -460,7 +459,7 @@ namespace UsermodManager { void onStateChange(uint8_t); bool add(Usermod* um); Usermod* lookup(uint16_t mod_id); - inline byte getModCount() {return numMods;}; + byte getModCount(); }; //usermods_list.cpp diff --git a/wled00/set.cpp b/wled00/set.cpp index 160eb48f07..dca1afd0d9 100644 --- a/wled00/set.cpp +++ b/wled00/set.cpp @@ -750,6 +750,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) DEBUG_PRINTF_P(PSTR(" = %s\n"), value.c_str()); } } + DEBUG_PRINTLN(F("Re-init UsermodManager::")); UsermodManager::readFromConfig(um); // force change of usermod parameters DEBUG_PRINTLN(F("Done re-init UsermodManager::")); releaseJSONBufferLock(); diff --git a/wled00/um_manager.cpp b/wled00/um_manager.cpp index c89c504aa8..042b9a137d 100644 --- a/wled00/um_manager.cpp +++ b/wled00/um_manager.cpp @@ -3,20 +3,19 @@ * Registration and management utility for v2 usermods */ -static Usermod* ums[WLED_MAX_USERMODS] = {nullptr}; -byte UsermodManager::numMods = 0; -static Usermod* umsEnabled[WLED_MAX_USERMODS] = {nullptr}; +static std::vector ums; +static std::vector umsEnabled; //Usermod Manager internals -void UsermodManager::setup() { for (Usermod* um : umsEnabled) if(um) if(um) { um->setup(); } } -void UsermodManager::connected() { for (Usermod* um : umsEnabled) if(um) { um->connected(); } } -void UsermodManager::loop() { for (Usermod* um : umsEnabled) if(um) { um->loop(); } } -void UsermodManager::handleOverlayDraw() { for (Usermod* um : umsEnabled) if(um) { um->handleOverlayDraw(); } } -void UsermodManager::appendConfigData(Print& dest) { for (Usermod* um : ums) if(um) { um->appendConfigData(dest); } } +void UsermodManager::setup() { for (Usermod* um : umsEnabled) um->setup(); } +void UsermodManager::connected() { for (Usermod* um : umsEnabled) um->connected(); } +void UsermodManager::loop() { for (Usermod* um : umsEnabled) um->loop(); } +void UsermodManager::handleOverlayDraw() { for (Usermod* um : umsEnabled) um->handleOverlayDraw(); } +void UsermodManager::appendConfigData(Print& dest) { for (Usermod* um : ums) um->appendConfigData(dest); } bool UsermodManager::handleButton(uint8_t b) { bool overrideIO = false; - for (unsigned i = 0; i < numMods; i++) { - if (ums[i]->handleButton(b)) overrideIO = true; + for (Usermod* um : umsEnabled) { + if (um->handleButton(b)) overrideIO = true; } return overrideIO; } @@ -27,46 +26,44 @@ bool UsermodManager::getUMData(um_data_t **data, uint8_t mod_id) { } return false; } -void UsermodManager::addToJsonState(JsonObject& obj) { for (Usermod* um : umsEnabled) if(um) { um->addToJsonState(obj); } } -void UsermodManager::addToJsonInfo(JsonObject& obj) { for (Usermod* um : ums) if(um) { um->addToJsonInfo(obj); } } -void UsermodManager::readFromJsonState(JsonObject& obj) { for (Usermod* um : umsEnabled) if(um) { um->readFromJsonState(obj); } } -void UsermodManager::addToConfig(JsonObject& obj) { for (Usermod* um : ums) if(um) { um->addToConfig(obj); } } +void UsermodManager::addToJsonState(JsonObject& obj) { for (Usermod* um : umsEnabled) um->addToJsonState(obj); } +void UsermodManager::addToJsonInfo(JsonObject& obj) { for (Usermod* um : ums) um->addToJsonInfo(obj); } +void UsermodManager::readFromJsonState(JsonObject& obj) { for (Usermod* um : umsEnabled) um->readFromJsonState(obj); } +void UsermodManager::addToConfig(JsonObject& obj) { for (Usermod* um : ums) um->addToConfig(obj); } bool UsermodManager::readFromConfig(JsonObject& obj) { bool allComplete = true; - for (unsigned i = 0; i < numMods; i++) { - if (!ums[i]->readFromConfig(obj)) allComplete = false; - if(!ums[i]->isEnabled()) { - umsEnabled[i] = ums[i]; - } - else { - umsEnabled[i] = nullptr; + umsEnabled.clear(); + for (Usermod* um : ums) { + if (!um->readFromConfig(obj)) allComplete = false; + if(!um->isEnabled()) { + umsEnabled.push_back(um); } } return allComplete; } #ifndef WLED_DISABLE_MQTT -void UsermodManager::onMqttConnect(bool sessionPresent) { for (Usermod* um : umsEnabled) if(um) { um->onMqttConnect(sessionPresent); } } +void UsermodManager::onMqttConnect(bool sessionPresent) { for (Usermod* um : umsEnabled) um->onMqttConnect(sessionPresent); } bool UsermodManager::onMqttMessage(char* topic, char* payload) { - for (Usermod* um : umsEnabled) if (um && um->onMqttMessage(topic, payload)) return true; + for (Usermod* um : umsEnabled) if (um->onMqttMessage(topic, payload)) return true; return false; } #endif #ifndef WLED_DISABLE_ESPNOW bool UsermodManager::onEspNowMessage(uint8_t* sender, uint8_t* payload, uint8_t len) { - for (Usermod* um : umsEnabled) if (um && um->onEspNowMessage(sender, payload, len)) return true; + for (Usermod* um : umsEnabled) if (um->onEspNowMessage(sender, payload, len)) return true; return false; } #endif -void UsermodManager::onUpdateBegin(bool init) { for (Usermod* um : umsEnabled) if(um) { um->onUpdateBegin(init); } } // notify usermods that update is to begin -void UsermodManager::onStateChange(uint8_t mode) { for (Usermod* um : umsEnabled) if(um) { um->onStateChange(mode); } } // notify usermods that WLED state changed +void UsermodManager::onUpdateBegin(bool init) { for (Usermod* um : umsEnabled) um->onUpdateBegin(init); } // notify usermods that update is to begin +void UsermodManager::onStateChange(uint8_t mode) { for (Usermod* um : umsEnabled) um->onStateChange(mode); } // notify usermods that WLED state changed /* * Enables usermods to lookup another Usermod. */ Usermod* UsermodManager::lookup(uint16_t mod_id) { - for (unsigned i = 0; i < numMods; i++) { - if (ums[i]->getId() == mod_id) { - return ums[i]; + for (Usermod* um : ums) { + if (um->getId() == mod_id) { + return um; } } return nullptr; @@ -74,11 +71,12 @@ Usermod* UsermodManager::lookup(uint16_t mod_id) { bool UsermodManager::add(Usermod* um) { - if (numMods >= WLED_MAX_USERMODS || um == nullptr) return false; - ums[numMods++] = um; + if (ums.size() >= WLED_MAX_USERMODS || um == nullptr) return false; + ums.push_back(um); return true; } +byte UsermodManager::getModCount() { return ums.size(); } /* Usermod v2 interface shim for oappend */ Print* Usermod::oappend_shim = nullptr;