RGBMatrixDisplay/src/Node.h

297 lines
9.7 KiB
C++

#ifndef NODE_H
#define NODE_H
#include <patrix/display/DisplayMatrix.h>
#include <patrix/node/PatrixNode.h>
#include <mode.h>
#include <patrix/core/http.h>
DisplayMatrix<32, 8> display(13);
static const auto style = R"(
<style>
body {
font-family: sans-serif;
font-size: 8vw;
margin: 0;
}
button.player{
width: 33vmin;
height: 33vmin;
font-size: 9vw;
}
table{
border-collapse: collapse;
}
td{
text-align: center;
}
</style>
)";
static const auto script = R"(
<script>
function get(path){
var r = new XMLHttpRequest();
r.open("GET", path, true);
r.send();
}
function configDate(){
const year = document.getElementById('year').value;
const month = document.getElementById('month').value - 1;
const day = document.getElementById('day').value;
const hour = document.getElementById('hour').value;
const minute = document.getElementById('minute').value;
const second = document.getElementById('second').value;
const targetEpochSeconds = (new Date(year, month, day, hour, minute, second).getTime() / 1000).toFixed(0);
get('/config/date?targetEpochSeconds=' + targetEpochSeconds);
}
</script>
)";
inline void httpMode(AsyncWebServerRequest *request) {
if (!request->hasParam("mode")) {
request->send(400);
}
setMode(static_cast<ModeId>(request->getParam("mode")->value().toInt()));
request->send(200);
}
inline void httpIndex(AsyncWebServerRequest *request) {
auto *response = request->beginResponseStream("text/html");
response->print(style);
response->print(script);
response->print(R"(<p>)");
response->print(R"(<a href="/player?index=0">Player 0</a><br>)");
response->print(R"(<a href="/player?index=1">Player 1</a><br>)");
response->print(R"(</p>)");
response->print(R"(<p>)");
response->print(R"(<a onclick="get('/mode?mode=0');">NONE</a><br>)");
response->print(R"(<a onclick="get('/mode?mode=1');">BORDER</a><br>)");
response->print(R"(<a onclick="get('/mode?mode=2');">CLOCK</a><br>)");
response->print(R"(<a onclick="get('/mode?mode=3');">GAME_OF_LIFE_BLACK_WHITE</a><br>)");
response->print(R"(<a onclick="get('/mode?mode=4');">GAME_OF_LIFE_GRAYSCALE</a><br>)");
response->print(R"(<a onclick="get('/mode?mode=5');">GAME_OF_LIFE_COLOR_FADE</a><br>)");
response->print(R"(<a onclick="get('/mode?mode=6');">GAME_OF_LIFE_RANDOM_COLOR</a><br>)");
response->print(R"(<a onclick="get('/mode?mode=7');">PONG</a><br>)");
response->print(R"(<a onclick="get('/mode?mode=8');">SPACE_INVADERS</a><br>)");
response->print(R"(<a onclick="get('/mode?mode=9');">COUNT_DOWN</a><br>)");
response->print(R"(<a onclick="get('/mode?mode=10');">COUNT_DOWN_BARS</a><br>)");
response->print(R"(<a onclick="get('/mode?mode=11');">COUNT_DOWN_SLEEP</a><br>)");
response->print(R"(<a onclick="get('/mode?mode=12');">STARFIELD</a><br>)");
response->print(R"(<a onclick="get('/mode?mode=13');">MATRIX</a><br>)");
response->print(R"(<a onclick="get('/mode?mode=14');">POWER</a><br>)");
response->print(R"(<a onclick="get('/mode?mode=15');">ENERGY</a><br>)");
response->print(R"(<a onclick="get('/mode?mode=16');">TIMER</a><br>)");
response->print(R"(</p>)");
response->print(R"(<p>)");
response->print(R"(Helligkeit: <a onclick="get('/brighter');">+</a> / <a onclick="get('/darker');">-</a><br>)");
response->print(R"(Geschwindigkeit: <a onclick="get('/faster');">+</a> / <a onclick="get('/slower');">-</a><br>)");
response->print(R"(</p>)");
response->print(R"(<p>)");
response->printf(R"(<input type="number" min="2025" max="3000" step="1" id="year">)");
response->printf(R"(<input type="number" min="1" max="12" step="1" id="month">)");
response->printf(R"(<input type="number" min="1" max="31" step="1" id="day">)");
response->printf(R"(<input type="number" min="0" max="23" step="1" id="hour">)");
response->printf(R"(<input type="number" min="0" max="59" step="1" id="minute">)");
response->printf(R"(<input type="number" min="0" max="59" step="1" id="second">)");
response->print(R"(<button onclick="configDate();">Datum setzen</button>)");
response->print(R"(</p>)");
response->print(R"(<p>)");
response->print(R"(<button onclick="get('/config/save');">Speichern erzwingen</button>)");
response->print(R"(</p>)");
request->send(response);
}
inline void web_player(AsyncWebServerRequest *request) {
char buffer[128];
if (!request->hasParam("index")) {
request->send(400, "text/plain", "Missing 'index'");
return;
}
const auto value = request->getParam("index")->value().toDouble();
const auto index = static_cast<int>(value);
auto *response = request->beginResponseStream("text/html");
response->print(style);
response->print(script);
response->print(R"(<meta name="viewport" content= "width=device-width, user-scalable=no">)");
response->print(R"(<table>)");
response->print(R"(<tr>)");
response->print(R"(<td><a href='/'>&larr;</td>)");
response->print(R"(<td>)");
snprintf(buffer, sizeof buffer, R"(<button class="player" onclick="get('/player/move?index=%d&x=0&y=-1');">&uarr;</button><br>)", index);
response->print(buffer);
response->print(R"(</td>)");
response->print(R"(<td>&nbsp;</td>)");
response->print(R"(</tr>)");
response->print(R"(<tr>)");
response->print(R"(<td>)");
snprintf(buffer, sizeof buffer, R"(<button class="player" onclick="get('/player/move?index=%d&x=-1&y=0');">&larr;</button><br>)", index);
response->print(buffer);
response->print(R"(</td>)");
response->print(R"(<td>)");
snprintf(buffer, sizeof buffer, R"(<button class="player" onclick="get('/player/fire?index=%d');">X</button><br>)", index);
response->print(buffer);
response->print(R"(</td>)");
response->print(R"(<td>)");
snprintf(buffer, sizeof buffer, R"(<button class="player" onclick="get('/player/move?index=%d&x=+1&y=0');">&rarr;</button><br>)", index);
response->print(buffer);
response->print(R"(</td>)");
response->print(R"(</tr>)");
response->print(R"(<tr>)");
response->print(R"(<td>&nbsp;</td>)");
response->print(R"(<td>)");
snprintf(buffer, sizeof buffer, R"(<button class="player" onclick="get('/player/move?index=%d&x=0&y=+1');">&darr;</button><br>)", index);
response->print(buffer);
response->print(R"(</td>)");
response->print(R"(<td>&nbsp;</td>)");
response->print(R"(</tr>)");
response->print(R"(</table>)");
request->send(response);
}
inline void web_player_move(AsyncWebServerRequest *request) {
// ReSharper disable once CppJoinDeclarationAndAssignment
double value;
if (!request->hasParam("index")) {
request->send(400, "text/plain", "Missing 'index'");
return;
}
value = request->getParam("index")->value().toDouble();
const auto index = static_cast<int>(value);
if (!request->hasParam("x")) {
request->send(400, "text/plain", "Missing 'x'");
return;
}
value = request->getParam("x")->value().toDouble();
const auto x = static_cast<int>(value);
if (!request->hasParam("y")) {
request->send(400, "text/plain", "Missing 'y'");
return;
}
value = request->getParam("y")->value().toDouble();
const auto y = static_cast<int>(value);
modeMove(index, x, y);
request->send(200, "application/json", "true");
}
inline void web_player_fire(AsyncWebServerRequest *request) {
// ReSharper disable once CppJoinDeclarationAndAssignment
double value;
if (!request->hasParam("index")) {
request->send(400, "text/plain", "Missing 'index'");
return;
}
value = request->getParam("index")->value().toDouble();
const auto index = static_cast<int>(value);
modeFire(index);
request->send(200, "application/json", "true");
}
inline void web_setMode(AsyncWebServerRequest *request) {
if (!request->hasParam("mode")) {
request->send(400, "text/plain", "Missing 'mode'");
return;
}
auto value = request->getParam("mode")->value().toDouble();
if (isnan(value)) {
request->send(400, "text/plain", "'mode' not a number");
return;
}
setMode(static_cast<ModeId>(value));
request->send(200);
}
inline void web_brighter(AsyncWebServerRequest *request) {
const auto newBrightness = display.getBrightness() + 10;
display.setBrightness(newBrightness >= 255 ? 255 : newBrightness);
request->send(200);
}
inline void web_darker(AsyncWebServerRequest *request) {
const auto newBrightness = display.getBrightness() - 10;
display.setBrightness(newBrightness <= 0 ? 0 : newBrightness);
request->send(200);
}
inline void web_faster(AsyncWebServerRequest *request) {
setSpeed(getSpeed() * 1.1);
request->send(200);
}
inline void web_slower(AsyncWebServerRequest *request) {
setSpeed(getSpeed() / 1.1);
request->send(200);
}
inline void web_config_save(AsyncWebServerRequest *request) {
config.write();
request->send(200);
}
inline void web_config_date(AsyncWebServerRequest *request) {
const auto targetEpochSeconds = std::stoul(request->getParam("targetEpochSeconds")->value().c_str());
config.set("targetEpochSeconds", targetEpochSeconds);
modeLoadConfig();
request->send(200);
}
class Node final : public PatrixNode {
public:
explicit Node() : PatrixNode(true, true, true) {
//
}
void setup() override {
modeSetup();
server.on("/", httpIndex);
server.on("/player", web_player);
server.on("/player/move", web_player_move);
server.on("/player/fire", web_player_fire);
server.on("/mode", httpMode);
server.on("/brighter", web_brighter);
server.on("/darker", web_darker);
server.on("/faster", web_faster);
server.on("/slower", web_slower);
server.on("/config/date", web_config_date);
server.on("/config/save", web_config_save);
display.setup();
display.setBrightness(10);
display.clear();
}
void loop() override {
modeLoop(display);
display.loop();
}
void mqttMessage(char *topic, char *message) override {
modeMqttMessage(topic, message);
}
};
#endif