|
1 | 1 | # Modules |
2 | 2 |
|
| 3 | +<img width="829" alt="image" src="https://github.com/user-attachments/assets/3384f3ba-b5e6-4993-9a8b-80c25878e176" /> |
| 4 | + |
3 | 5 | ## Functional |
4 | 6 |
|
5 | | -With Moonbase Modules it is possible to create a new module entirely from one c++ file |
| 7 | +With Moonbase Modules it is possible to create new module entirely from one c++ class by only defining a json document describing the data structure and a function catching all the changes in the data. Http endpont and websockets are created automatically. There is no need to create any UI code, it is entirely driven by the json document. |
| 8 | + |
| 9 | +This is inspired by WLED usermods, further developed in StarBase and now MoonBase (using the ESP32-Sveltekit infrastructure) |
6 | 10 |
|
7 | 11 | See [Demo](https://moonmodules.org/MoonLight/custom/module/demo/) and [Animations](https://moonmodules.org/MoonLight/custom/module/animations/) as examples |
8 | 12 |
|
9 | 13 | ## Technical |
10 | 14 |
|
11 | | -* Create a class which inherits from Module e.g. class ModuleDemo : public Module |
| 15 | +* Create a class which inherits from Module |
12 | 16 | * Call the Module constructor with the name of the module. |
13 | | - * This name will be used to set up http endpoints and webserver sockets |
| 17 | + * This name will be used to set up http rest api and webserver sockets |
| 18 | + * See [ModuleDemo.h](https://github.com/ewowi/MoonBase/blob/main/src/custom/ModuleDemo.h) |
| 19 | + |
| 20 | +``` |
| 21 | +class ModuleDemo : public Module |
| 22 | +{ |
| 23 | +public: |
| 24 | +
|
| 25 | +ModuleDemo(PsychicHttpServer *server |
| 26 | + , ESP32SvelteKit *sveltekit |
| 27 | + , FilesService *filesService |
| 28 | + ) : Module("demo", server, sveltekit, filesService) { |
| 29 | + ESP_LOGD("", "ModuleDemo::constructor"); |
| 30 | + } |
| 31 | +} |
| 32 | +``` |
| 33 | + |
14 | 34 | * Implement function setupDefinition to create a json document with the datastructure |
15 | | - * Stored on the file system |
16 | | - * Used to generate the UI |
17 | | - * Used to initialy create the module data |
| 35 | + * Store data on the file system |
| 36 | + * Generate the UI |
| 37 | + * Initialy create the module data |
| 38 | + |
| 39 | +``` |
| 40 | +void setupDefinition(JsonArray root) override{ |
| 41 | + JsonObject property; |
| 42 | + JsonArray details; |
| 43 | + JsonArray values; |
| 44 | +
|
| 45 | + property = root.add<JsonObject>(); property["name"] = "hostName"; property["type"] = "text"; property["default"] = "MoonLight"; |
| 46 | + property = root.add<JsonObject>(); property["name"] = "connectionMode"; property["type"] = "select"; property["default"] = "Signal Strength"; values = property["values"].to<JsonArray>(); |
| 47 | + values.add("Offline"); |
| 48 | + values.add("Signal Strength"); |
| 49 | + values.add("Priority"); |
| 50 | +
|
| 51 | + property = root.add<JsonObject>(); property["name"] = "savedNetworks"; property["type"] = "array"; details = property["n"].to<JsonArray>(); |
| 52 | + { |
| 53 | + property = details.add<JsonObject>(); property["name"] = "SSID"; property["type"] = "text"; property["default"] = "ewtr"; property["min"] = 3; property["max"] = 32; |
| 54 | + property = details.add<JsonObject>(); property["name"] = "Password"; property["type"] = "password"; property["default"] = ""; |
| 55 | + } |
| 56 | +
|
| 57 | +} |
| 58 | +
|
| 59 | +``` |
| 60 | + |
18 | 61 | * Implement function onUpdate to define what happens if data changes |
19 | 62 | * struct UpdatedItem defines the update (parent property, name of property, value and index (in case of multiple records) |
20 | | - * This is run in the httpd / webserver task. To run it in the main (application task use runInLoopTask - see ModuleAnimations) |
21 | | -* Add the module in main.cpp |
22 | | -* Add the module in menu.svelte (this will be automated in the future) |
| 63 | + * This is run in the httpd / webserver task. To run it in the main (application task use runInLoopTask - see [ModuleAnimations](https://github.com/ewowi/MoonBase/blob/main/src/custom/ModuleAnimations.h)) |
| 64 | + |
| 65 | +``` |
| 66 | +void onUpdate(UpdatedItem updatedItem) override |
| 67 | +{ |
| 68 | + if (updatedItem.name == "lightsOn" || updatedItem.name == "brightness") { |
| 69 | + ESP_LOGD("", "handle %s.%s[%d] %s", updatedItem.parent.c_str(), updatedItem.name.c_str(), updatedItem.index, updatedItem.value.as<String>()); |
| 70 | + FastLED.setBrightness(_state.data["lightsOn"]?_state.data["brightness"]:0); |
| 71 | + } else if (updatedItem.parent == "nodes" && updatedItem.name == "animation") { |
| 72 | + ESP_LOGD("", "handle %s.%s[%d] %s", updatedItem.parent.c_str(), updatedItem.name.c_str(), updatedItem.index, _state.data["nodes"][updatedItem.index]["animation"].as<String>().c_str()); |
| 73 | + animation = _state.data["nodes"][updatedItem.index]["animation"].as<String>(); |
| 74 | + compileAndRun(animation); |
| 75 | + } else |
| 76 | + ESP_LOGD("", "no handle for %s.%s[%d] %s (%d %s)", updatedItem.parent.c_str(), updatedItem.name.c_str(), updatedItem.index, updatedItem.value.as<String>().c_str(), _state.data["driverOn"].as<bool>(), _state.data["nodes"][0]["animation"].as<String>()); |
| 77 | +} |
| 78 | +``` |
| 79 | + |
| 80 | +* Add the module in [main.cpp](https://github.com/ewowi/MoonBase/blob/main/src/main.cpp) |
23 | 81 |
|
24 | | -<img width="400" alt="image" src="https://github.com/user-attachments/assets/fcec054b-a918-4e31-bca4-71b75aa6673c" /> |
| 82 | +``` |
| 83 | +ModuleDemo moduleDemo = ModuleDemo(&server, &esp32sveltekit, &filesService); |
| 84 | +... |
| 85 | +moduleDemo.begin(); |
| 86 | +... |
| 87 | +moduleDemo.loop(); |
| 88 | +``` |
25 | 89 |
|
26 | | -Done by the Modules implementation |
27 | | -* Module.h will generate all the required server code |
28 | | -* Module.svelte will deal with the UI |
29 | | -* MultiInput.svelte is used by Module.svelte to display the right UI widget based on what is defined in the definition json |
| 90 | +* Add the module in [menu.svelte](https://github.com/ewowi/MoonBase/blob/main/interface/src/routes/menu.svelte) (this will be automated in the future) |
30 | 91 |
|
31 | | -### Server |
| 92 | +``` |
| 93 | +submenu: [ |
| 94 | + { |
| 95 | + title: 'Module Demo', |
| 96 | + icon: BulbIcon, |
| 97 | + href: '/custom/module?module=demo', |
| 98 | + feature: page.data.features.liveanimation, |
| 99 | + }, |
| 100 | +] |
| 101 | +``` |
32 | 102 |
|
33 | | -[Instances.h](https://github.com/MoonModules/MoonLight/blob/main/lib/framework/Instances.h) and [Instances.cpp](https://github.com/MoonModules/MoonLight/blob/main/lib/framework/Instances.cpp) |
| 103 | +* This is all to create a fully functioning new module |
34 | 104 |
|
35 | | -### UI |
| 105 | +Moonbase-Modules is implemented in: |
36 | 106 |
|
37 | | -[Instances.svelte](https://github.com/MoonModules/MoonLight/blob/main/interface/src/routes/system/status/Instances.svelte) |
| 107 | +* [Module.h](https://github.com/ewowi/MoonBase/blob/main/src/custom/Module.h) and [Module.cpp](https://github.com/ewowi/MoonBase/blob/main/src/custom/Module.cpp) will generate all the required server code |
| 108 | +* [Module.svelte](https://github.com/ewowi/MoonBase/blob/main/interface/src/routes/custom/module/Module.svelte) will deal with the UI |
| 109 | +* [MultiInput.svelte](https://github.com/ewowi/MoonBase/blob/main/interface/src/lib/components/custom/MultiInput.svelte) is used by Module.svelte to display the right UI widget based on what is defined in the definition json |
| 110 | +* Modifications done in [menu.svelte](https://github.com/ewowi/MoonBase/blob/main/interface/src/routes/menu.svelte) do identify a module by href and not by title alone |
0 commit comments