Skip to content

Commit

Permalink
Initial PoC for Issue PlummersSoftwareLLC#356, a websocket server for…
Browse files Browse the repository at this point in the history
… two way API communication
  • Loading branch information
KeiranHines committed Nov 26, 2023
1 parent 7ba9f57 commit a28c228
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 15 deletions.
7 changes: 6 additions & 1 deletion include/webserver.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ class CWebServer
static const std::map<String, ValueValidator> settingValidators;

AsyncWebServer _server;
AsyncWebSocket _socket;
StaticStatistics _staticStats;

// Helper functions/templates
Expand Down Expand Up @@ -199,14 +200,18 @@ class CWebServer
});
}

static void onSocketEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg,
uint8_t *data, size_t len);

public:

CWebServer()
: _server(80), _staticStats()
: _server(80), _socket("/ws"), _staticStats()
{}

// begin - register page load handlers and start serving pages
void begin();
void EffectsUpdate();
};

// Set value in lambda using a forwarding function. Always returns true
Expand Down
2 changes: 1 addition & 1 deletion platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
; mesmerizer HUB75 info panel with audio effects, weather, info, etc,

[platformio]
default_envs =
default_envs = mesmerizer
build_cache_dir = .pio/build_cache

; ==================
Expand Down
23 changes: 23 additions & 0 deletions site/src/context/effectsContext.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import PropTypes from 'prop-types';

const EffectsContext = createContext(undefined);
const effectsEndpoint = `${httpPrefix !== undefined ? httpPrefix : ""}/effects`;
const gateway = `ws://${httpPrefix !== undefined ? httpPrefix.replace('http://', '') : window.location.hostname}/ws`;
const refreshInterval = 30000; //30 Seconds

const EffectsProvider = ({ children }) => {
Expand All @@ -14,6 +15,28 @@ const EffectsProvider = ({ children }) => {
const [activeEffect, setActiveEffect] = useState(0);
const [effectTrigger, setEffectTrigger] = useState(false);
const [currentEffect, setCurrentEffect] = useState(Number(0));

useEffect(() => {
const ws = new WebSocket(gateway);
ws.onopen = () => {
console.log('connected to ws');
};
ws.onclose = () => {
console.log('connection to ws closed');
}
ws.onmessage = (event) => {
try {
console.log('Websocket message');
const json = JSON.parse(event.data);
console.log('ws data: ', json);
} catch(err) {
console.log('error proccessing ws message', err);
console.log('data:', event.data)
}
}
return () => ws.close();
}, [])

useEffect(() => {
const getDataFromDevice = async (params) => {
try {
Expand Down
2 changes: 1 addition & 1 deletion site/src/espaddr.jsx
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
const httpPrefix = process.env.NODE_ENV === "development" ? "http://255.255.255.0" : undefined;
const httpPrefix = process.env.NODE_ENV === "development" ? "http://10.1.5.1" : undefined;
export default httpPrefix;
2 changes: 2 additions & 0 deletions src/remotecontrol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,12 +92,14 @@ void RemoteControl::handle()
{
effectManager.ClearRemoteColor();
effectManager.NextEffect();
g_ptrSystem->WebServer().EffectsUpdate();
return;
}
else if (IR_BMINUS == result)
{
effectManager.ClearRemoteColor();
effectManager.PreviousEffect();
g_ptrSystem->WebServer().EffectsUpdate();
return;
}
else if (IR_SMOOTH == result)
Expand Down
64 changes: 52 additions & 12 deletions src/webserver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,8 @@ void CWebServer::begin()
});

_server.begin();

_socket.onEvent(onSocketEvent);
_server.addHandler(&_socket);
debugI("HTTP server started");
}

Expand All @@ -212,31 +213,29 @@ long CWebServer::GetEffectIndexFromParam(AsyncWebServerRequest * pRequest, bool
return strtol(pRequest->getParam("effectIndex", post, false)->value().c_str(), NULL, 10);
}

void CWebServer::GetEffectListText(AsyncWebServerRequest * pRequest)
std::unique_ptr<AsyncJsonResponse> buildEffectListJson()
{
static size_t jsonBufferSize = JSON_BUFFER_BASE_SIZE;
bool bufferOverflow;
debugV("GetEffectListText");

do
{
bufferOverflow = false;
auto response = std::make_unique<AsyncJsonResponse>(false, jsonBufferSize);
auto& j = response->getRoot();
auto& effectManager = g_ptrSystem->EffectManager();
auto &j = response->getRoot();
auto &effectManager = g_ptrSystem->EffectManager();

j["currentEffect"] = effectManager.GetCurrentEffectIndex();
j["currentEffect"] = effectManager.GetCurrentEffectIndex();
j["millisecondsRemaining"] = effectManager.GetTimeRemainingForCurrentEffect();
j["eternalInterval"] = effectManager.IsIntervalEternal();
j["effectInterval"] = effectManager.GetInterval();
j["eternalInterval"] = effectManager.IsIntervalEternal();
j["effectInterval"] = effectManager.GetInterval();

for (auto effect : effectManager.EffectsList())
{
StaticJsonDocument<256> effectDoc;

effectDoc["name"] = effect->FriendlyName();
effectDoc["name"] = effect->FriendlyName();
effectDoc["enabled"] = effect->IsEnabled();
effectDoc["core"] = effect->IsCoreEffect();
effectDoc["core"] = effect->IsCoreEffect();

if (!j["Effects"].add(effectDoc))
{
Expand All @@ -248,9 +247,19 @@ void CWebServer::GetEffectListText(AsyncWebServerRequest * pRequest)
}

if (!bufferOverflow)
AddCORSHeaderAndSendResponse(pRequest, response.release());
return response;

} while (bufferOverflow);
return nullptr;
}

void CWebServer::GetEffectListText(AsyncWebServerRequest * pRequest)
{
debugV("GetEffectListText");
std::unique_ptr response = buildEffectListJson();
if(response) {
AddCORSHeaderAndSendResponse(pRequest, response.release());
}
}

void CWebServer::GetStatistics(AsyncWebServerRequest * pRequest)
Expand Down Expand Up @@ -727,4 +736,35 @@ void CWebServer::Reset(AsyncWebServerRequest * pRequest)
debugW("Resetting device at API request!");
throw new std::runtime_error("Resetting device at API request");
}
}

void CWebServer::EffectsUpdate()
{
JsonVariant message = buildEffectListJson().release()->getRoot();
size_t len = measureJson(message);
char buffer[len];
if(buffer) {
serializeJson(message, buffer, len);
_socket.textAll(buffer, len);
}
}

void CWebServer::onSocketEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg,
uint8_t *data, size_t len)
{
switch (type)
{
case WS_EVT_CONNECT:
Serial.printf("WebSocket client #%u connected from %s\n", client->id(), client->remoteIP().toString().c_str());
break;
case WS_EVT_DISCONNECT:
Serial.printf("WebSocket client #%u disconnected\n", client->id());
break;
case WS_EVT_DATA:
// TODO Add any updates that need to fire when the web socket sends a message here.
break;
case WS_EVT_PONG:
case WS_EVT_ERROR:
break;
}
}

0 comments on commit a28c228

Please sign in to comment.