2021-03-25 20:00:08 +01:00
|
|
|
#include "wled.h"
|
|
|
|
|
|
|
|
/*
|
|
|
|
* WebSockets server for bidirectional communication
|
|
|
|
*/
|
|
|
|
#ifdef WLED_ENABLE_WEBSOCKETS
|
|
|
|
|
|
|
|
uint16_t wsLiveClientId = 0;
|
|
|
|
unsigned long wsLastLiveTime = 0;
|
|
|
|
//uint8_t* wsFrameBuffer = nullptr;
|
|
|
|
|
|
|
|
#define WS_LIVE_INTERVAL 40
|
|
|
|
|
|
|
|
void wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len)
|
|
|
|
{
|
|
|
|
if(type == WS_EVT_CONNECT){
|
|
|
|
//client connected
|
|
|
|
sendDataWs(client);
|
2021-07-25 22:44:26 +02:00
|
|
|
DEBUG_PRINTLN(F("WS client connected."));
|
2021-03-25 20:00:08 +01:00
|
|
|
} else if(type == WS_EVT_DISCONNECT){
|
|
|
|
//client disconnected
|
|
|
|
if (client->id() == wsLiveClientId) wsLiveClientId = 0;
|
2021-07-25 22:44:26 +02:00
|
|
|
DEBUG_PRINTLN(F("WS client disconnected."));
|
2021-03-25 20:00:08 +01:00
|
|
|
} else if(type == WS_EVT_DATA){
|
|
|
|
//data packet
|
|
|
|
AwsFrameInfo * info = (AwsFrameInfo*)arg;
|
|
|
|
if(info->final && info->index == 0 && info->len == len){
|
2021-07-08 02:01:17 +02:00
|
|
|
//the whole message is in a single frame and we got all of its data (max. 1450byte)
|
2021-03-25 20:00:08 +01:00
|
|
|
if(info->opcode == WS_TEXT)
|
|
|
|
{
|
2021-08-17 12:47:01 +02:00
|
|
|
if (len > 0 && len < 10 && data[0] == 'p') {
|
|
|
|
//application layer ping/pong heartbeat.
|
|
|
|
//client-side socket layer ping packets are unresponded (investigate)
|
|
|
|
client->text(F("pong"));
|
|
|
|
return;
|
|
|
|
}
|
2021-03-25 20:00:08 +01:00
|
|
|
bool verboseResponse = false;
|
|
|
|
{ //scope JsonDocument so it releases its buffer
|
2021-11-14 16:56:34 +01:00
|
|
|
DEBUG_PRINTLN(F("WS JSON receive buffer requested."));
|
|
|
|
#ifdef WLED_USE_DYNAMIC_JSON
|
2021-11-07 11:58:16 +01:00
|
|
|
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
|
2021-11-14 16:56:34 +01:00
|
|
|
#else
|
|
|
|
if (!requestJSONBufferLock(15)) return;
|
|
|
|
#endif
|
2021-03-25 20:00:08 +01:00
|
|
|
|
2021-11-03 14:52:22 +01:00
|
|
|
DeserializationError error = deserializeJson(doc, data, len);
|
|
|
|
JsonObject root = doc.as<JsonObject>();
|
|
|
|
if (error || root.isNull()) {
|
2021-11-12 23:33:10 +01:00
|
|
|
releaseJSONBufferLock();
|
2021-11-03 14:52:22 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
/*
|
2021-07-07 16:55:22 +02:00
|
|
|
#ifdef WLED_DEBUG
|
2021-07-25 22:44:26 +02:00
|
|
|
DEBUG_PRINT(F("Incoming WS: "));
|
2021-07-07 16:55:22 +02:00
|
|
|
serializeJson(root,Serial);
|
|
|
|
DEBUG_PRINTLN();
|
|
|
|
#endif
|
2021-11-03 14:52:22 +01:00
|
|
|
*/
|
2021-07-11 02:38:31 +02:00
|
|
|
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"))
|
2021-03-25 20:00:08 +01:00
|
|
|
{
|
|
|
|
wsLiveClientId = root["lv"] ? client->id() : 0;
|
2021-07-08 02:01:17 +02:00
|
|
|
} else {
|
2021-11-12 23:33:10 +01:00
|
|
|
//fileDoc = &doc; // used for applying presets (presets.cpp)
|
2021-07-08 02:01:17 +02:00
|
|
|
verboseResponse = deserializeState(root);
|
2021-11-12 23:33:10 +01:00
|
|
|
//fileDoc = nullptr;
|
2021-07-11 02:38:31 +02:00
|
|
|
if (!interfaceUpdateCallMode) {
|
|
|
|
//special case, only on playlist load, avoid sending twice in rapid succession
|
|
|
|
if (millis() - lastInterfaceUpdate > 1700) verboseResponse = false;
|
|
|
|
}
|
2021-03-25 20:00:08 +01:00
|
|
|
}
|
2021-11-12 23:33:10 +01:00
|
|
|
releaseJSONBufferLock();
|
2021-03-25 20:00:08 +01:00
|
|
|
}
|
2021-07-02 00:24:14 +02:00
|
|
|
//update if it takes longer than 300ms until next "broadcast"
|
|
|
|
if (verboseResponse && (millis() - lastInterfaceUpdate < 1700 || !interfaceUpdateCallMode)) sendDataWs(client);
|
2021-03-25 20:00:08 +01:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//message is comprised of multiple frames or the frame is split into multiple packets
|
|
|
|
//if(info->index == 0){
|
|
|
|
//if (!wsFrameBuffer && len < 4096) wsFrameBuffer = new uint8_t[4096];
|
|
|
|
//}
|
|
|
|
|
|
|
|
//if (wsFrameBuffer && len < 4096 && info->index + info->)
|
|
|
|
//{
|
|
|
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
if((info->index + len) == info->len){
|
|
|
|
if(info->final){
|
|
|
|
if(info->message_opcode == WS_TEXT) {
|
|
|
|
client->text(F("{\"error\":9}")); //we do not handle split packets right now
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-07-25 22:44:26 +02:00
|
|
|
DEBUG_PRINTLN(F("WS multipart message."));
|
2021-03-25 20:00:08 +01:00
|
|
|
}
|
|
|
|
} else if(type == WS_EVT_ERROR){
|
|
|
|
//error was received from the other end
|
2021-07-25 22:44:26 +02:00
|
|
|
DEBUG_PRINTLN(F("WS error."));
|
2021-03-25 20:00:08 +01:00
|
|
|
|
|
|
|
} else if(type == WS_EVT_PONG){
|
|
|
|
//pong message was received (in response to a ping request maybe)
|
2021-07-25 22:44:26 +02:00
|
|
|
DEBUG_PRINTLN(F("WS pong."));
|
2021-03-25 20:00:08 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void sendDataWs(AsyncWebSocketClient * client)
|
|
|
|
{
|
|
|
|
if (!ws.count()) return;
|
|
|
|
AsyncWebSocketMessageBuffer * buffer;
|
|
|
|
|
|
|
|
{ //scope JsonDocument so it releases its buffer
|
2021-11-14 16:56:34 +01:00
|
|
|
DEBUG_PRINTLN(F("WS JSON send buffer requested."));
|
|
|
|
#ifdef WLED_USE_DYNAMIC_JSON
|
2021-11-07 11:58:16 +01:00
|
|
|
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
|
2021-11-14 16:56:34 +01:00
|
|
|
#else
|
|
|
|
if (!requestJSONBufferLock(16)) return;
|
|
|
|
#endif
|
2021-11-07 11:58:16 +01:00
|
|
|
|
2021-03-25 20:00:08 +01:00
|
|
|
JsonObject state = doc.createNestedObject("state");
|
|
|
|
serializeState(state);
|
|
|
|
JsonObject info = doc.createNestedObject("info");
|
|
|
|
serializeInfo(info);
|
2021-11-21 14:14:39 +01:00
|
|
|
DEBUG_PRINTF("JSON buffer size: %u for WS request.\n", doc.memoryUsage());
|
2021-03-25 20:00:08 +01:00
|
|
|
size_t len = measureJson(doc);
|
|
|
|
buffer = ws.makeBuffer(len);
|
2021-11-03 14:52:22 +01:00
|
|
|
if (!buffer) {
|
2021-11-12 23:33:10 +01:00
|
|
|
releaseJSONBufferLock();
|
2021-11-03 14:52:22 +01:00
|
|
|
return; //out of memory
|
|
|
|
}
|
2021-07-25 22:44:26 +02:00
|
|
|
/*
|
|
|
|
#ifdef WLED_DEBUG
|
|
|
|
DEBUG_PRINT(F("Outgoing WS: "));
|
|
|
|
serializeJson(doc,Serial);
|
|
|
|
DEBUG_PRINTLN();
|
|
|
|
#endif
|
|
|
|
*/
|
2021-03-25 20:00:08 +01:00
|
|
|
serializeJson(doc, (char *)buffer->get(), len +1);
|
2021-11-12 23:33:10 +01:00
|
|
|
releaseJSONBufferLock();
|
2021-03-25 20:00:08 +01:00
|
|
|
}
|
2021-07-25 22:44:26 +02:00
|
|
|
DEBUG_PRINT(F("Sending WS data "));
|
2021-03-25 20:00:08 +01:00
|
|
|
if (client) {
|
|
|
|
client->text(buffer);
|
2021-07-25 22:44:26 +02:00
|
|
|
DEBUG_PRINTLN(F("to a single client."));
|
2021-03-25 20:00:08 +01:00
|
|
|
} else {
|
|
|
|
ws.textAll(buffer);
|
2021-07-25 22:44:26 +02:00
|
|
|
DEBUG_PRINTLN(F("to multiple clients."));
|
2021-03-25 20:00:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void handleWs()
|
|
|
|
{
|
|
|
|
if (millis() - wsLastLiveTime > WS_LIVE_INTERVAL)
|
|
|
|
{
|
|
|
|
ws.cleanupClients();
|
|
|
|
bool success = true;
|
|
|
|
if (wsLiveClientId)
|
|
|
|
success = serveLiveLeds(nullptr, wsLiveClientId);
|
|
|
|
wsLastLiveTime = millis();
|
|
|
|
if (!success) wsLastLiveTime -= 20; //try again in 20ms if failed due to non-empty WS queue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#else
|
|
|
|
void handleWs() {}
|
|
|
|
void sendDataWs(AsyncWebSocketClient * client) {}
|
2020-06-26 17:28:35 +02:00
|
|
|
#endif
|