refactor(neopattern): unify control handling via event-based single path

Build control payload once from request params and apply locally through ctx.fire("api/neopattern"). If broadcast=true, send the same payload as a CLUSTER_EVENT over UDP.\n\nChanges:\n- Remove duplicated per-parameter setters in REST handler\n- Always fire local event to update state consistently\n- Broadcast reuses same payload; logs retained\n\nImpact:\n- Less duplication, clearer flow, identical behavior for local/remote updates, and simpler maintenance.
This commit is contained in:
2025-09-28 13:27:02 +02:00
parent cabf857bbd
commit 8da9f77441

View File

@@ -126,87 +126,43 @@ void NeoPatternService::handleControlRequest(AsyncWebServerRequest* request) {
bool updated = false;
bool broadcast = false;
if (request->hasParam("pattern", true)) {
String name = request->getParam("pattern", true)->value();
if (isValidPattern(name)) {
setPatternByName(name);
updated = true;
} else {
// Invalid pattern name - could add error handling here
LOG_WARN("NeoPattern", "Invalid pattern name: " + name);
}
}
if (request->hasParam("color", true)) {
String colorStr = request->getParam("color", true)->value();
uint32_t color = parseColor(colorStr);
setColor(color);
updated = true;
}
if (request->hasParam("color2", true)) {
String colorStr = request->getParam("color2", true)->value();
uint32_t color = parseColor(colorStr);
setColor2(color);
updated = true;
}
if (request->hasParam("brightness", true)) {
int b = request->getParam("brightness", true)->value().toInt();
if (b < 0) b = 0;
if (b > 255) b = 255;
setBrightness(static_cast<uint8_t>(b));
updated = true;
}
if (request->hasParam("total_steps", true)) {
int steps = request->getParam("total_steps", true)->value().toInt();
if (steps > 0) {
setTotalSteps(static_cast<uint16_t>(steps));
updated = true;
}
}
if (request->hasParam("direction", true)) {
String dirStr = request->getParam("direction", true)->value();
NeoDirection dir = (dirStr.equalsIgnoreCase("reverse")) ? NeoDirection::REVERSE : NeoDirection::FORWARD;
setDirection(dir);
updated = true;
}
if (request->hasParam("interval", true)) {
unsigned long interval = request->getParam("interval", true)->value().toInt();
if (interval > 0) {
setUpdateInterval(interval);
updated = true;
}
}
if (request->hasParam("broadcast", true)) {
String b = request->getParam("broadcast", true)->value();
broadcast = b.equalsIgnoreCase("true") || b == "1";
}
if (broadcast) {
// Build JSON payload from provided params
JsonDocument payload;
if (request->hasParam("pattern", true)) payload["pattern"] = request->getParam("pattern", true)->value();
if (request->hasParam("color", true)) payload["color"] = request->getParam("color", true)->value();
if (request->hasParam("color2", true)) payload["color2"] = request->getParam("color2", true)->value();
if (request->hasParam("brightness", true)) payload["brightness"] = request->getParam("brightness", true)->value();
if (request->hasParam("total_steps", true)) payload["total_steps"] = request->getParam("total_steps", true)->value();
if (request->hasParam("direction", true)) payload["direction"] = request->getParam("direction", true)->value();
if (request->hasParam("interval", true)) payload["interval"] = request->getParam("interval", true)->value();
// Build JSON payload from provided params (single source of truth)
JsonDocument payload;
bool any = false;
if (request->hasParam("pattern", true)) { payload["pattern"] = request->getParam("pattern", true)->value(); any = true; }
if (request->hasParam("color", true)) { payload["color"] = request->getParam("color", true)->value(); any = true; }
if (request->hasParam("color2", true)) { payload["color2"] = request->getParam("color2", true)->value(); any = true; }
if (request->hasParam("brightness", true)) { payload["brightness"] = request->getParam("brightness", true)->value(); any = true; }
if (request->hasParam("total_steps", true)) { payload["total_steps"] = request->getParam("total_steps", true)->value(); any = true; }
if (request->hasParam("direction", true)) { payload["direction"] = request->getParam("direction", true)->value(); any = true; }
if (request->hasParam("interval", true)) { payload["interval"] = request->getParam("interval", true)->value(); any = true; }
String payloadStr;
serializeJson(payload, payloadStr);
String payloadStr;
serializeJson(payload, payloadStr);
// Always apply locally via event so we have a single codepath for updates
if (any) {
std::string ev = "api/neopattern";
String localData = payloadStr;
LOG_INFO("NeoPattern", String("Applying local api/neopattern via event payloadLen=") + String(payloadStr.length()));
ctx.fire(ev, &localData);
updated = true;
}
// Broadcast to peers if requested
if (broadcast && any) {
JsonDocument eventDoc;
eventDoc["event"] = "api/neopattern";
eventDoc["data"] = payloadStr; // data is arbitrary JSON string
eventDoc["data"] = payloadStr; // data is JSON string
String eventJson;
serializeJson(eventDoc, eventJson);
// Compute subnet-directed broadcast to improve delivery on some networks
IPAddress ip = WiFi.localIP();
IPAddress mask = WiFi.subnetMask();
@@ -216,12 +172,6 @@ void NeoPatternService::handleControlRequest(AsyncWebServerRequest* request) {
String msg = String(ClusterProtocol::CLUSTER_EVENT_MSG) + ":" + eventJson;
ctx.udp->write(msg.c_str());
ctx.udp->endPacket();
// Fire locally as well so the sender applies the change
std::string ev = "api/neopattern";
String localData = payloadStr;
LOG_INFO("NeoPattern", "Firing local api/neopattern event after broadcast");
ctx.fire(ev, &localData);
}
// Return current state