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);
|
void sendImprovRPCResponse(uint8_t commandId);
|
||||||
|
|
||||||
//ir.cpp
|
//ir.cpp
|
||||||
//bool decodeIRCustom(uint32_t code);
|
|
||||||
void applyRepeatActions();
|
void applyRepeatActions();
|
||||||
byte relativeChange(byte property, int8_t amount, byte lowerBoundary = 0, byte higherBoundary = 0xFF);
|
byte relativeChange(byte property, int8_t amount, byte lowerBoundary = 0, byte higherBoundary = 0xFF);
|
||||||
void decodeIR(uint32_t code);
|
void decodeIR(uint32_t code);
|
||||||
@ -190,7 +189,7 @@ int16_t loadPlaylist(JsonObject playlistObject, byte presetId = 0);
|
|||||||
void handlePlaylist();
|
void handlePlaylist();
|
||||||
|
|
||||||
//presets.cpp
|
//presets.cpp
|
||||||
void handlePresets();
|
void handlePresets(bool force = false);
|
||||||
bool applyPreset(byte index, byte callMode = CALL_MODE_DIRECT_CHANGE, bool fromJson = false);
|
bool applyPreset(byte index, byte callMode = CALL_MODE_DIRECT_CHANGE, bool fromJson = false);
|
||||||
inline bool applyTemporaryPreset() {return applyPreset(255);};
|
inline bool applyTemporaryPreset() {return applyPreset(255);};
|
||||||
void savePreset(byte index, const char* pname = nullptr, JsonObject saveobj = JsonObject());
|
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);
|
// seg.setOption(SEG_OPTION_FREEZE, false);
|
||||||
}
|
}
|
||||||
// send UDP if not in preset and something changed that is not just selection
|
// 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 or segment power/opacity
|
||||||
// send UDP if something changed that is not just selection
|
if ((seg.differs(prev) & 0x7E) && seg.getOption(SEG_OPTION_ON)==prev.getOption(SEG_OPTION_ON)) stateChanged = true;
|
||||||
if (seg.differs(prev) & 0x7F) stateChanged = true;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// deserializes WLED state (fileDoc points to doc object if called from web server)
|
// 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 deserializeState(JsonObject root, byte callMode, byte presetId)
|
||||||
{
|
{
|
||||||
bool stateResponse = root[F("v")] | false;
|
bool stateResponse = root[F("v")] | false;
|
||||||
@ -357,7 +357,7 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
|
|||||||
ps = presetCycCurr;
|
ps = presetCycCurr;
|
||||||
if (getVal(root["ps"], &ps, presetCycMin, presetCycMax)) { //load preset (clears state request!)
|
if (getVal(root["ps"], &ps, presetCycMin, presetCycMax)) { //load preset (clears state request!)
|
||||||
if (ps >= presetCycMin && ps <= presetCycMax) presetCycCurr = ps;
|
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;
|
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
|
//do not notify here, because the first playlist entry will do
|
||||||
if (root["on"].isNull()) callMode = CALL_MODE_NO_NOTIFY;
|
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 callMode = CALL_MODE_DIRECT_CHANGE; // possible bugfix for playlist only containing HTTP API preset FX=~
|
||||||
} else {
|
|
||||||
interfaceUpdateCallMode = CALL_MODE_WS_SEND;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stateUpdated(callMode);
|
stateUpdated(callMode);
|
||||||
|
@ -171,17 +171,14 @@ void updateInterfaces(uint8_t callMode)
|
|||||||
callMode != CALL_MODE_NO_NOTIFY) updateBlynk();
|
callMode != CALL_MODE_NO_NOTIFY) updateBlynk();
|
||||||
#endif
|
#endif
|
||||||
doPublishMqtt = true;
|
doPublishMqtt = true;
|
||||||
|
interfaceUpdateCallMode = 0; //disable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void handleTransitions()
|
void handleTransitions()
|
||||||
{
|
{
|
||||||
//handle still pending interface update
|
//handle still pending interface update
|
||||||
if (interfaceUpdateCallMode && millis() - lastInterfaceUpdate > INTERFACE_UPDATE_COOLDOWN)
|
if (interfaceUpdateCallMode && millis() - lastInterfaceUpdate > INTERFACE_UPDATE_COOLDOWN) updateInterfaces(interfaceUpdateCallMode);
|
||||||
{
|
|
||||||
updateInterfaces(interfaceUpdateCallMode);
|
|
||||||
interfaceUpdateCallMode = 0; //disable
|
|
||||||
}
|
|
||||||
if (doPublishMqtt) publishMqtt();
|
if (doPublishMqtt) publishMqtt();
|
||||||
|
|
||||||
if (transitionActive && transitionDelayTemp > 0)
|
if (transitionActive && transitionDelayTemp > 0)
|
||||||
|
@ -17,16 +17,41 @@ bool applyPreset(byte index, byte callMode, bool fromJson)
|
|||||||
presetToApply = index;
|
presetToApply = index;
|
||||||
callModeToApply = callMode;
|
callModeToApply = callMode;
|
||||||
checkPlaylist = fromJson;
|
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;
|
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;
|
JsonObject fdo;
|
||||||
const char *filename = presetToApply < 255 ? "/presets.json" : "/tmp.json";
|
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
|
// allocate buffer
|
||||||
DEBUG_PRINTLN(F("Apply preset JSON buffer requested."));
|
DEBUG_PRINTLN(F("Apply preset JSON buffer requested."));
|
||||||
if (!requestJSONBufferLock(9)) return; // will also assign fileDoc
|
if (!requestJSONBufferLock(9)) return; // will also assign fileDoc
|
||||||
@ -46,8 +71,10 @@ void handlePresets()
|
|||||||
const char* httpwin = fdo["win"];
|
const char* httpwin = fdo["win"];
|
||||||
if (httpwin) {
|
if (httpwin) {
|
||||||
String apireq = "win"; apireq += '&'; // reduce flash string usage
|
String apireq = "win"; apireq += '&'; // reduce flash string usage
|
||||||
|
apireq += F("IN&"); // interenal call
|
||||||
apireq += httpwin;
|
apireq += httpwin;
|
||||||
handleSet(nullptr, apireq, false);
|
handleSet(nullptr, apireq, false);
|
||||||
|
setValuesFromFirstSelectedSeg(); // fills legacy values
|
||||||
} else {
|
} else {
|
||||||
fdo.remove("ps"); //remove load request for presets to prevent recursive crash
|
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
|
// 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)
|
// 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
|
//uncomment this if you have a "my_config.h" file you'd like to use
|
||||||
//#define WLED_USE_MY_CONFIG
|
//#define WLED_USE_MY_CONFIG
|
||||||
|
@ -34,34 +34,32 @@ void wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventTyp
|
|||||||
client->text(F("pong"));
|
client->text(F("pong"));
|
||||||
return;
|
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);
|
bool verboseResponse = false;
|
||||||
JsonObject root = doc.as<JsonObject>();
|
DEBUG_PRINTLN(F("WS JSON receive buffer requested."));
|
||||||
if (error || root.isNull()) {
|
if (!requestJSONBufferLock(11)) return;
|
||||||
releaseJSONBufferLock();
|
|
||||||
return;
|
DeserializationError error = deserializeJson(doc, data, len);
|
||||||
}
|
JsonObject root = doc.as<JsonObject>();
|
||||||
if (root["v"] && root.size() == 1) {
|
if (error || root.isNull()) {
|
||||||
//if the received value is just "{"v":true}", send only to this client
|
releaseJSONBufferLock();
|
||||||
verboseResponse = true;
|
return;
|
||||||
} else if (root.containsKey("lv"))
|
}
|
||||||
{
|
if (root["v"] && root.size() == 1) {
|
||||||
wsLiveClientId = root["lv"] ? client->id() : 0;
|
//if the received value is just "{"v":true}", send only to this client
|
||||||
} else {
|
verboseResponse = true;
|
||||||
verboseResponse = deserializeState(root);
|
} else if (root.containsKey("lv")) {
|
||||||
if (!interfaceUpdateCallMode) {
|
wsLiveClientId = root["lv"] ? client->id() : 0;
|
||||||
//special case, only on playlist load, avoid sending twice in rapid succession
|
} else {
|
||||||
if (millis() - lastInterfaceUpdate > (INTERFACE_UPDATE_COOLDOWN -300)) verboseResponse = false;
|
verboseResponse = deserializeState(root);
|
||||||
}
|
}
|
||||||
}
|
releaseJSONBufferLock(); // will clean fileDoc
|
||||||
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 {
|
} else {
|
||||||
//message is comprised of multiple frames or the frame is split into multiple packets
|
//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;
|
if (!ws.count()) return;
|
||||||
AsyncWebSocketMessageBuffer * buffer;
|
AsyncWebSocketMessageBuffer * buffer;
|
||||||
|
|
||||||
{ //scope JsonDocument so it releases its buffer
|
DEBUG_PRINTLN(F("WS JSON send buffer requested."));
|
||||||
DEBUG_PRINTLN(F("WS JSON send buffer requested."));
|
if (!requestJSONBufferLock(12)) return;
|
||||||
if (!requestJSONBufferLock(12)) return;
|
|
||||||
|
|
||||||
JsonObject state = doc.createNestedObject("state");
|
JsonObject state = doc.createNestedObject("state");
|
||||||
serializeState(state);
|
serializeState(state);
|
||||||
JsonObject info = doc.createNestedObject("info");
|
JsonObject info = doc.createNestedObject("info");
|
||||||
serializeInfo(info);
|
serializeInfo(info);
|
||||||
DEBUG_PRINTF("JSON buffer size: %u for WS request.\n", doc.memoryUsage());
|
|
||||||
size_t len = measureJson(doc);
|
DEBUG_PRINTF("JSON buffer size: %u for WS request.\n", doc.memoryUsage());
|
||||||
size_t heap1 = ESP.getFreeHeap();
|
size_t len = measureJson(doc);
|
||||||
buffer = ws.makeBuffer(len); // will not allocate correct memory sometimes
|
|
||||||
size_t heap2 = ESP.getFreeHeap();
|
size_t heap1 = ESP.getFreeHeap();
|
||||||
if (!buffer || heap1-heap2<len) {
|
buffer = ws.makeBuffer(len); // will not allocate correct memory sometimes
|
||||||
releaseJSONBufferLock();
|
size_t heap2 = ESP.getFreeHeap();
|
||||||
ws.closeAll(1013); //code 1013 = temporary overload, try again later
|
if (!buffer || heap1-heap2<len) {
|
||||||
ws.cleanupClients(0); //disconnect all clients to release memory
|
|
||||||
return; //out of memory
|
|
||||||
}
|
|
||||||
serializeJson(doc, (char *)buffer->get(), len +1);
|
|
||||||
releaseJSONBufferLock();
|
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 "));
|
DEBUG_PRINT(F("Sending WS data "));
|
||||||
if (client) {
|
if (client) {
|
||||||
client->text(buffer);
|
client->text(buffer);
|
||||||
|
Loading…
Reference in New Issue
Block a user