diff --git a/examples/base/main.cpp b/examples/base/main.cpp index 8ecb2cb..8e6e587 100644 --- a/examples/base/main.cpp +++ b/examples/base/main.cpp @@ -16,20 +16,27 @@ ClusterManager cluster(ctx, taskManager); ApiServer apiServer(ctx, taskManager, ctx.config.api_server_port); void setup() { - // Setup WiFi first - network.setupWiFi(); + // Setup WiFi first + network.setupWiFi(); - // Initialize and start all tasks - taskManager.initialize(); - - // Start the API server - apiServer.begin(); - - // Print initial task status - taskManager.printTaskStatus(); + // Add example labels for this node + auto it = ctx.memberList->find(ctx.hostname); + if (it != ctx.memberList->end()) { + it->second.labels["app"] = "base"; + it->second.labels["role"] = "demo"; + } + + // Initialize and start all tasks + taskManager.initialize(); + + // Start the API server + apiServer.begin(); + + // Print initial task status + taskManager.printTaskStatus(); } void loop() { - taskManager.execute(); - yield(); + taskManager.execute(); + yield(); } diff --git a/examples/neopixel/main.cpp b/examples/neopixel/main.cpp index ae5cd34..2762ff3 100644 --- a/examples/neopixel/main.cpp +++ b/examples/neopixel/main.cpp @@ -349,6 +349,15 @@ NeoPixelService neoService(ctx, taskManager, NEOPIXEL_COUNT, NEOPIXEL_PIN, NEOPI void setup() { network.setupWiFi(); + // Add example labels for this node + auto it = ctx.memberList->find(ctx.hostname); + if (it != ctx.memberList->end()) { + it->second.labels["app"] = "neopixel"; + it->second.labels["device"] = "light"; + it->second.labels["pixels"] = String(NEOPIXEL_COUNT); + it->second.labels["pin"] = String(NEOPIXEL_PIN); + } + taskManager.initialize(); apiServer.begin(); diff --git a/examples/relay/main.cpp b/examples/relay/main.cpp index 0f406a0..65c2053 100644 --- a/examples/relay/main.cpp +++ b/examples/relay/main.cpp @@ -107,6 +107,14 @@ void setup() { // Setup WiFi first network.setupWiFi(); + // Add example labels for this node + auto it = ctx.memberList->find(ctx.hostname); + if (it != ctx.memberList->end()) { + it->second.labels["app"] = "relay"; + it->second.labels["device"] = "actuator"; + it->second.labels["pin"] = String(RELAY_PIN); + } + // Initialize and start all tasks taskManager.initialize(); diff --git a/include/NodeInfo.h b/include/NodeInfo.h index bda152d..e667596 100644 --- a/include/NodeInfo.h +++ b/include/NodeInfo.h @@ -3,6 +3,7 @@ #include #include #include +#include struct NodeInfo { String hostname; @@ -18,6 +19,7 @@ struct NodeInfo { } resources; unsigned long latency = 0; // ms since lastSeen std::vector> apiEndpoints; // List of registered endpoints + std::map labels; // Arbitrary node labels (key -> value) }; const char* statusToStr(NodeInfo::Status status); diff --git a/src/ApiServer.cpp b/src/ApiServer.cpp index 535cb9e..c88a35d 100644 --- a/src/ApiServer.cpp +++ b/src/ApiServer.cpp @@ -95,6 +95,16 @@ void ApiServer::onSystemStatusRequest(AsyncWebServerRequest *request) { apiObj["uri"] = std::get<0>(entry); apiObj["method"] = std::get<1>(entry); } + // Include local node labels if present + if (ctx.memberList) { + auto it = ctx.memberList->find(ctx.hostname); + if (it != ctx.memberList->end()) { + JsonObject labelsObj = doc["labels"].to(); + for (const auto& kv : it->second.labels) { + labelsObj[kv.first.c_str()] = kv.second; + } + } + } String json; serializeJson(doc, json); request->send(200, "application/json", json); @@ -122,6 +132,13 @@ void ApiServer::onClusterMembersRequest(AsyncWebServerRequest *request) { apiObj["uri"] = std::get<0>(endpoint); methodToStr(endpoint, apiObj); } + // Add labels if present + if (!node.labels.empty()) { + JsonObject labelsObj = obj["labels"].to(); + for (const auto& kv : node.labels) { + labelsObj[kv.first.c_str()] = kv.second; + } + } } String json; serializeJson(doc, json); diff --git a/src/ClusterManager.cpp b/src/ClusterManager.cpp index 568afd2..3b9e47f 100644 --- a/src/ClusterManager.cpp +++ b/src/ClusterManager.cpp @@ -118,6 +118,16 @@ void ClusterManager::fetchNodeInfo(const IPAddress& ip) { node.apiEndpoints.push_back(std::make_tuple(uri, method)); } } + // Parse labels if present + node.labels.clear(); + if (doc["labels"].is()) { + JsonObject labelsObj = doc["labels"].as(); + for (JsonPair kvp : labelsObj) { + String k = String(kvp.key().c_str()); + String v = String(labelsObj[kvp.key()]); + node.labels[k] = v; + } + } Serial.printf("[Cluster] Fetched info for node: %s @ %s\n", node.hostname.c_str(), ip.toString().c_str()); break; } diff --git a/src/NetworkManager.cpp b/src/NetworkManager.cpp index be13f60..64d1a3a 100644 --- a/src/NetworkManager.cpp +++ b/src/NetworkManager.cpp @@ -54,5 +54,7 @@ void NetworkManager::setupWiFi() { } self.lastSeen = millis(); self.status = NodeInfo::ACTIVE; + // Initialize a default label for demonstration; users can modify at runtime + self.labels["hostname"] = ctx.hostname; ctx.fire("node_discovered", &self); }