Synchronus applyPreset() from HTTP JSON API call.
Bugfix for HTTP API preset. WS multiple broadcast fix. Turning segment on/off will not reset currentPreset/cause stateChanged.
This commit is contained in:
parent
f915201a27
commit
0f6b1e4ae1
@ -105,7 +105,6 @@ void sendImprovInfoResponse();
|
||||
void sendImprovRPCResponse(uint8_t commandId);
|
||||
|
||||
//ir.cpp
|
||||
//bool decodeIRCustom(uint32_t code);
|
||||
void applyRepeatActions();
|
||||
byte relativeChange(byte property, int8_t amount, byte lowerBoundary = 0, byte higherBoundary = 0xFF);
|
||||
void decodeIR(uint32_t code);
|
||||
@ -190,7 +189,7 @@ int16_t loadPlaylist(JsonObject playlistObject, byte presetId = 0);
|
||||
void handlePlaylist();
|
||||
|
||||
//presets.cpp
|
||||
void handlePresets();
|
||||
void handlePresets(bool force = false);
|
||||
bool applyPreset(byte index, byte callMode = CALL_MODE_DIRECT_CHANGE, bool fromJson = false);
|
||||
inline bool applyTemporaryPreset() {return applyPreset(255);};
|
||||
void savePreset(byte index, const char* pname = nullptr, JsonObject saveobj = JsonObject());
|
||||
|
@ -220,13 +220,13 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
|
||||
// seg.setOption(SEG_OPTION_FREEZE, false);
|
||||
}
|
||||
// send UDP if not in preset and something changed that is not just selection
|
||||
//if (!presetId && (seg.differs(prev) & 0x7F)) stateChanged = true;
|
||||
// send UDP if something changed that is not just selection
|
||||
if (seg.differs(prev) & 0x7F) stateChanged = true;
|
||||
// send UDP if something changed that is not just selection or segment power/opacity
|
||||
if ((seg.differs(prev) & 0x7E) && seg.getOption(SEG_OPTION_ON)==prev.getOption(SEG_OPTION_ON)) stateChanged = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// deserializes WLED state (fileDoc points to doc object if called from web server)
|
||||
// presetId is non-0 if called from handlePreset()
|
||||
bool deserializeState(JsonObject root, byte callMode, byte presetId)
|
||||
{
|
||||
bool stateResponse = root[F("v")] | false;
|
||||
@ -357,7 +357,7 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
|
||||
ps = presetCycCurr;
|
||||
if (getVal(root["ps"], &ps, presetCycMin, presetCycMax)) { //load preset (clears state request!)
|
||||
if (ps >= presetCycMin && ps <= presetCycMax) presetCycCurr = ps;
|
||||
applyPreset(ps, callMode, true);
|
||||
applyPreset(ps, callMode, !presetId); // may clear root object and replace it by preset content if presetId==0
|
||||
return stateResponse;
|
||||
}
|
||||
|
||||
@ -375,8 +375,6 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
|
||||
//do not notify here, because the first playlist entry will do
|
||||
if (root["on"].isNull()) callMode = CALL_MODE_NO_NOTIFY;
|
||||
else callMode = CALL_MODE_DIRECT_CHANGE; // possible bugfix for playlist only containing HTTP API preset FX=~
|
||||
} else {
|
||||
interfaceUpdateCallMode = CALL_MODE_WS_SEND;
|
||||
}
|
||||
|
||||
stateUpdated(callMode);
|
||||
|
@ -171,17 +171,14 @@ void updateInterfaces(uint8_t callMode)
|
||||
callMode != CALL_MODE_NO_NOTIFY) updateBlynk();
|
||||
#endif
|
||||
doPublishMqtt = true;
|
||||
interfaceUpdateCallMode = 0; //disable
|
||||
}
|
||||
|
||||
|
||||
void handleTransitions()
|
||||
{
|
||||
//handle still pending interface update
|
||||
if (interfaceUpdateCallMode && millis() - lastInterfaceUpdate > INTERFACE_UPDATE_COOLDOWN)
|
||||
{
|
||||
updateInterfaces(interfaceUpdateCallMode);
|
||||
interfaceUpdateCallMode = 0; //disable
|
||||
}
|
||||
if (interfaceUpdateCallMode && millis() - lastInterfaceUpdate > INTERFACE_UPDATE_COOLDOWN) updateInterfaces(interfaceUpdateCallMode);
|
||||
if (doPublishMqtt) publishMqtt();
|
||||
|
||||
if (transitionActive && transitionDelayTemp > 0)
|
||||
|
@ -17,16 +17,41 @@ bool applyPreset(byte index, byte callMode, bool fromJson)
|
||||
presetToApply = index;
|
||||
callModeToApply = callMode;
|
||||
checkPlaylist = fromJson;
|
||||
// the following is needed in case of HTTP JSON API call to return correct state to the caller
|
||||
// fromJson is true in case when deserializeState() was called with presetId==0
|
||||
if (fromJson) handlePresets(true); // force immediate processing
|
||||
return true;
|
||||
}
|
||||
|
||||
void handlePresets()
|
||||
void handlePresets(bool force)
|
||||
{
|
||||
if (presetToApply == 0 || fileDoc) return; //JSON buffer allocated (apply preset in next cycle) or no preset waiting
|
||||
if (presetToApply == 0 || (fileDoc && !force)) return; // JSON buffer already allocated and not force apply or no preset waiting
|
||||
|
||||
JsonObject fdo;
|
||||
const char *filename = presetToApply < 255 ? "/presets.json" : "/tmp.json";
|
||||
|
||||
//crude way to determine if this was called by a network request
|
||||
uint8_t core = 1;
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
core = xPortGetCoreID();
|
||||
#endif
|
||||
//only allow use of fileDoc from the core responsible for network requests (AKA HTTP JSON API)
|
||||
//do not use active network request doc from preset called by main loop (playlist, schedule, ...)
|
||||
if (fileDoc && core && force && presetToApply < 255) {
|
||||
// this will overwrite doc with preset content but applyPreset() is the last in such case and content of doc is no longer needed
|
||||
errorFlag = readObjectFromFileUsingId(filename, presetToApply, fileDoc) ? ERR_NONE : ERR_FS_PLOAD;
|
||||
JsonObject fdo = fileDoc->as<JsonObject>();
|
||||
// if we applyPreset from JSON and preset contains "seg" we must unload playlist
|
||||
if (checkPlaylist && !fdo["seg"].isNull()) unloadPlaylist();
|
||||
fdo.remove("ps"); //remove load request for presets to prevent recursive crash
|
||||
deserializeState(fdo, callModeToApply, presetToApply);
|
||||
if (!errorFlag) currentPreset = presetToApply;
|
||||
presetToApply = 0; //clear request for preset
|
||||
callModeToApply = 0;
|
||||
checkPlaylist = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// allocate buffer
|
||||
DEBUG_PRINTLN(F("Apply preset JSON buffer requested."));
|
||||
if (!requestJSONBufferLock(9)) return; // will also assign fileDoc
|
||||
@ -46,8 +71,10 @@ void handlePresets()
|
||||
const char* httpwin = fdo["win"];
|
||||
if (httpwin) {
|
||||
String apireq = "win"; apireq += '&'; // reduce flash string usage
|
||||
apireq += F("IN&"); // interenal call
|
||||
apireq += httpwin;
|
||||
handleSet(nullptr, apireq, false);
|
||||
setValuesFromFirstSelectedSeg(); // fills legacy values
|
||||
} else {
|
||||
fdo.remove("ps"); //remove load request for presets to prevent recursive crash
|
||||
// if we applyPreset from JSON and preset contains "seg" we must unload playlist
|
||||
|
@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
// version code in format yymmddb (b = daily build)
|
||||
#define VERSION 2204112
|
||||
#define VERSION 2204161
|
||||
|
||||
//uncomment this if you have a "my_config.h" file you'd like to use
|
||||
//#define WLED_USE_MY_CONFIG
|
||||
|
@ -34,34 +34,32 @@ void wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventTyp
|
||||
client->text(F("pong"));
|
||||
return;
|
||||
}
|
||||
bool verboseResponse = false;
|
||||
{ //scope JsonDocument so it releases its buffer
|
||||
DEBUG_PRINTLN(F("WS JSON receive buffer requested."));
|
||||
if (!requestJSONBufferLock(11)) return;
|
||||
|
||||
DeserializationError error = deserializeJson(doc, data, len);
|
||||
JsonObject root = doc.as<JsonObject>();
|
||||
if (error || root.isNull()) {
|
||||
releaseJSONBufferLock();
|
||||
return;
|
||||
}
|
||||
if (root["v"] && root.size() == 1) {
|
||||
//if the received value is just "{"v":true}", send only to this client
|
||||
verboseResponse = true;
|
||||
} else if (root.containsKey("lv"))
|
||||
{
|
||||
wsLiveClientId = root["lv"] ? client->id() : 0;
|
||||
} else {
|
||||
verboseResponse = deserializeState(root);
|
||||
if (!interfaceUpdateCallMode) {
|
||||
//special case, only on playlist load, avoid sending twice in rapid succession
|
||||
if (millis() - lastInterfaceUpdate > (INTERFACE_UPDATE_COOLDOWN -300)) verboseResponse = false;
|
||||
}
|
||||
}
|
||||
releaseJSONBufferLock(); // will clean fileDoc
|
||||
bool verboseResponse = false;
|
||||
DEBUG_PRINTLN(F("WS JSON receive buffer requested."));
|
||||
if (!requestJSONBufferLock(11)) return;
|
||||
|
||||
DeserializationError error = deserializeJson(doc, data, len);
|
||||
JsonObject root = doc.as<JsonObject>();
|
||||
if (error || root.isNull()) {
|
||||
releaseJSONBufferLock();
|
||||
return;
|
||||
}
|
||||
if (root["v"] && root.size() == 1) {
|
||||
//if the received value is just "{"v":true}", send only to this client
|
||||
verboseResponse = true;
|
||||
} else if (root.containsKey("lv")) {
|
||||
wsLiveClientId = root["lv"] ? client->id() : 0;
|
||||
} else {
|
||||
verboseResponse = deserializeState(root);
|
||||
}
|
||||
releaseJSONBufferLock(); // will clean fileDoc
|
||||
|
||||
// force broadcast in 500ms after upadting client
|
||||
if (verboseResponse) {
|
||||
sendDataWs(client);
|
||||
lastInterfaceUpdate = millis() - (INTERFACE_UPDATE_COOLDOWN -500);
|
||||
}
|
||||
//update if it takes longer than 300ms until next "broadcast"
|
||||
if (verboseResponse && (millis() - lastInterfaceUpdate < (INTERFACE_UPDATE_COOLDOWN -300) || !interfaceUpdateCallMode)) sendDataWs(client);
|
||||
}
|
||||
} else {
|
||||
//message is comprised of multiple frames or the frame is split into multiple packets
|
||||
@ -99,28 +97,30 @@ void sendDataWs(AsyncWebSocketClient * client)
|
||||
if (!ws.count()) return;
|
||||
AsyncWebSocketMessageBuffer * buffer;
|
||||
|
||||
{ //scope JsonDocument so it releases its buffer
|
||||
DEBUG_PRINTLN(F("WS JSON send buffer requested."));
|
||||
if (!requestJSONBufferLock(12)) return;
|
||||
DEBUG_PRINTLN(F("WS JSON send buffer requested."));
|
||||
if (!requestJSONBufferLock(12)) return;
|
||||
|
||||
JsonObject state = doc.createNestedObject("state");
|
||||
serializeState(state);
|
||||
JsonObject info = doc.createNestedObject("info");
|
||||
serializeInfo(info);
|
||||
DEBUG_PRINTF("JSON buffer size: %u for WS request.\n", doc.memoryUsage());
|
||||
size_t len = measureJson(doc);
|
||||
size_t heap1 = ESP.getFreeHeap();
|
||||
buffer = ws.makeBuffer(len); // will not allocate correct memory sometimes
|
||||
size_t heap2 = ESP.getFreeHeap();
|
||||
if (!buffer || heap1-heap2<len) {
|
||||
releaseJSONBufferLock();
|
||||
ws.closeAll(1013); //code 1013 = temporary overload, try again later
|
||||
ws.cleanupClients(0); //disconnect all clients to release memory
|
||||
return; //out of memory
|
||||
}
|
||||
serializeJson(doc, (char *)buffer->get(), len +1);
|
||||
JsonObject state = doc.createNestedObject("state");
|
||||
serializeState(state);
|
||||
JsonObject info = doc.createNestedObject("info");
|
||||
serializeInfo(info);
|
||||
|
||||
DEBUG_PRINTF("JSON buffer size: %u for WS request.\n", doc.memoryUsage());
|
||||
size_t len = measureJson(doc);
|
||||
|
||||
size_t heap1 = ESP.getFreeHeap();
|
||||
buffer = ws.makeBuffer(len); // will not allocate correct memory sometimes
|
||||
size_t heap2 = ESP.getFreeHeap();
|
||||
if (!buffer || heap1-heap2<len) {
|
||||
releaseJSONBufferLock();
|
||||
}
|
||||
ws.closeAll(1013); //code 1013 = temporary overload, try again later
|
||||
ws.cleanupClients(0); //disconnect all clients to release memory
|
||||
return; //out of memory
|
||||
}
|
||||
|
||||
serializeJson(doc, (char *)buffer->get(), len +1);
|
||||
releaseJSONBufferLock();
|
||||
|
||||
DEBUG_PRINT(F("Sending WS data "));
|
||||
if (client) {
|
||||
client->text(buffer);
|
||||
|
Loading…
Reference in New Issue
Block a user