Added EventSource for webUI updates, completed more features
This commit is contained in:
parent
91fd921e2e
commit
280764a2fa
16 changed files with 241 additions and 78 deletions
|
@ -172,8 +172,8 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<label for="tzOffset" class="col-sm-6 col-form-label">Timezone offset</label>
|
<label for="tzOffset" class="col-sm-6 col-form-label">Timezone offset</label>
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6 mb-3">
|
||||||
<div class="input-group mb-3">
|
<div class="input-group">
|
||||||
<input type="number" name="tzOffset" id="tzOffset" class="form-control">
|
<input type="number" name="tzOffset" id="tzOffset" class="form-control">
|
||||||
<span class="input-group-text">min</span>
|
<span class="input-group-text">min</span>
|
||||||
<button class="btn btn-outline-secondary" type="button" id="getTzOffsetBtn">Auto</button>
|
<button class="btn btn-outline-secondary" type="button" id="getTzOffsetBtn">Auto</button>
|
||||||
|
@ -181,11 +181,21 @@
|
||||||
<div class="form-text">A restart is required to apply TZ offset.</div>
|
<div class="form-text">A restart is required to apply TZ offset.</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<label for="minSecPriceUpd" class="col-sm-6 col-form-label">Time between price updates</label>
|
||||||
|
<div class="col-sm-6 mb-3">
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="number" name="minSecPriceUpd" id="minSecPriceUpd" class="form-control">
|
||||||
|
<span class="input-group-text">sec</span>
|
||||||
|
</div>
|
||||||
|
<div class="form-text">Short amounts might shorten lifespan.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class=" col-sm-6">
|
<div class=" col-sm-6">
|
||||||
<div class="form-check form-switch">
|
<div class="form-check form-switch">
|
||||||
<input class="form-check-input" type="checkbox" id="ledFlashOnUpdate" name="ledFlashOnUpd" value="1">
|
<input class="form-check-input" type="checkbox" id="ledFlashOnUpdate" name="ledFlashOnUpd" value="1">
|
||||||
<label class="form-check-label" for="ledFlashOnUpdate">LED flash on update</label>
|
<label class="form-check-label" for="ledFlashOnUpdate">LED flash on new block</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -19,12 +19,7 @@ toTime = (secs) => {
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
getBcStatus = () => {
|
let processStatusData = (jsonData) => {
|
||||||
fetch('/api/status', {
|
|
||||||
method: 'get'
|
|
||||||
})
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(jsonData => {
|
|
||||||
var source = document.getElementById("entry-template").innerHTML;
|
var source = document.getElementById("entry-template").innerHTML;
|
||||||
var template = Handlebars.compile(source);
|
var template = Handlebars.compile(source);
|
||||||
|
|
||||||
|
@ -35,7 +30,7 @@ getBcStatus = () => {
|
||||||
memTotal: Math.round(jsonData.espHeapSize / 1024),
|
memTotal: Math.round(jsonData.espHeapSize / 1024),
|
||||||
uptime: toTime(jsonData.espUptime),
|
uptime: toTime(jsonData.espUptime),
|
||||||
currentScreen: jsonData.currentScreen,
|
currentScreen: jsonData.currentScreen,
|
||||||
rendered: jsonData.rendered,
|
rendered: jsonData.data,
|
||||||
data: jsonData.data,
|
data: jsonData.data,
|
||||||
screens: screens,
|
screens: screens,
|
||||||
ledStatus: jsonData.ledStatus ? jsonData.ledStatus.map((t) => (t).toString(16)) : [],
|
ledStatus: jsonData.ledStatus ? jsonData.ledStatus.map((t) => (t).toString(16)) : [],
|
||||||
|
@ -44,14 +39,45 @@ getBcStatus = () => {
|
||||||
|
|
||||||
|
|
||||||
document.getElementById('output').innerHTML = template(context);
|
document.getElementById('output').innerHTML = template(context);
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
//error block
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interval = setInterval(getBcStatus, 2500);
|
|
||||||
getBcStatus();
|
if (!!window.EventSource) {
|
||||||
|
var source = new EventSource('/events');
|
||||||
|
|
||||||
|
source.addEventListener('open', function (e) {
|
||||||
|
console.log("Status EventSource Connected");
|
||||||
|
if (e.data) {
|
||||||
|
processStatusData(JSON.parse(e.data));
|
||||||
|
}
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
source.addEventListener('error', function (e) {
|
||||||
|
if (e.target.readyState != EventSource.OPEN) {
|
||||||
|
console.log("Status EventSource Disconnected");
|
||||||
|
}
|
||||||
|
source.close();
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
source.addEventListener('status', function (e) {
|
||||||
|
processStatusData(JSON.parse(e.data));
|
||||||
|
}, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// getBcStatus = () => {
|
||||||
|
// fetch('/api/status', {
|
||||||
|
// method: 'get'
|
||||||
|
// })
|
||||||
|
// .then(response => response.json())
|
||||||
|
// .then()
|
||||||
|
// .catch(err => {
|
||||||
|
// //error block
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// interval = setInterval(getBcStatus, 2500);
|
||||||
|
// getBcStatus();
|
||||||
|
|
||||||
fetch('/api/settings', {
|
fetch('/api/settings', {
|
||||||
method: 'get'
|
method: 'get'
|
||||||
|
@ -77,17 +103,18 @@ fetch('/api/settings', {
|
||||||
if (jsonData.useBitcoinNode)
|
if (jsonData.useBitcoinNode)
|
||||||
document.getElementById('useBitcoinNode').checked = true;
|
document.getElementById('useBitcoinNode').checked = true;
|
||||||
|
|
||||||
let nodeFields = ["rpcHost", "rpcPort", "rpcUser", "tzOffset"];
|
// let nodeFields = ["rpcHost", "rpcPort", "rpcUser", "tzOffset"];
|
||||||
|
|
||||||
for (let n of nodeFields) {
|
// for (let n of nodeFields) {
|
||||||
document.getElementById(n).value = jsonData[n];
|
// document.getElementById(n).value = jsonData[n];
|
||||||
}
|
// }
|
||||||
|
|
||||||
document.getElementById('timePerScreen').value = jsonData.timerSeconds / 60;
|
document.getElementById('timePerScreen').value = jsonData.timerSeconds / 60;
|
||||||
document.getElementById('ledBrightness').value = jsonData.ledBrightness;
|
document.getElementById('ledBrightness').value = jsonData.ledBrightness;
|
||||||
document.getElementById('fullRefreshMin').value = jsonData.fullRefreshMin;
|
document.getElementById('fullRefreshMin').value = jsonData.fullRefreshMin;
|
||||||
document.getElementById('wpTimeout').value = jsonData.wpTimeout;
|
document.getElementById('tzOffset').value = jsonData.tzOffset;
|
||||||
document.getElementById('mempoolInstance').value = jsonData.mempoolInstance;
|
document.getElementById('mempoolInstance').value = jsonData.mempoolInstance;
|
||||||
|
document.getElementById('minSecPriceUpd').value = jsonData.minSecPriceUpd;
|
||||||
|
|
||||||
if (jsonData.gitRev)
|
if (jsonData.gitRev)
|
||||||
document.getElementById('gitRev').innerHTML = "Version: " + jsonData.gitRev;
|
document.getElementById('gitRev').innerHTML = "Version: " + jsonData.gitRev;
|
||||||
|
|
|
@ -24,12 +24,11 @@ board_build.partitions = partition.csv
|
||||||
build_flags =
|
build_flags =
|
||||||
!python scripts/git_rev.py
|
!python scripts/git_rev.py
|
||||||
-DLAST_BUILD_TIME=$UNIX_TIME
|
-DLAST_BUILD_TIME=$UNIX_TIME
|
||||||
-DASYNCWEBSERVER_REGEX
|
|
||||||
-DARDUINO_USB_CDC_ON_BOOT
|
-DARDUINO_USB_CDC_ON_BOOT
|
||||||
-fexceptions
|
-fexceptions
|
||||||
build_unflags =
|
build_unflags =
|
||||||
-fno-exceptions
|
|
||||||
-Werror=all
|
-Werror=all
|
||||||
|
-fno-exceptions
|
||||||
lib_deps =
|
lib_deps =
|
||||||
bblanchon/ArduinoJson@^6.21.3
|
bblanchon/ArduinoJson@^6.21.3
|
||||||
esphome/Improv@^1.2.3
|
esphome/Improv@^1.2.3
|
||||||
|
@ -49,3 +48,5 @@ build_flags =
|
||||||
-D NEOPIXEL_PIN=34
|
-D NEOPIXEL_PIN=34
|
||||||
-D NEOPIXEL_COUNT=4
|
-D NEOPIXEL_COUNT=4
|
||||||
-D NUM_SCREENS=7
|
-D NUM_SCREENS=7
|
||||||
|
build_unflags =
|
||||||
|
${btclock_base.build_unflags}
|
||||||
|
|
|
@ -32,7 +32,7 @@ void setupBlockNotify()
|
||||||
{
|
{
|
||||||
String blockHeightStr = http->getString();
|
String blockHeightStr = http->getString();
|
||||||
currentBlockHeight = blockHeightStr.toInt();
|
currentBlockHeight = blockHeightStr.toInt();
|
||||||
xTaskNotifyGive(blockUpdateTaskHandle);
|
// xTaskNotifyGive(blockUpdateTaskHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
// std::strcpy(wsServer, String("wss://" + mempoolInstance + "/api/v1/ws").c_str());
|
// std::strcpy(wsServer, String("wss://" + mempoolInstance + "/api/v1/ws").c_str());
|
||||||
|
@ -95,9 +95,12 @@ void onWebsocketMessage(esp_websocket_event_data_t *event_data)
|
||||||
|
|
||||||
if (blockUpdateTaskHandle != nullptr) {
|
if (blockUpdateTaskHandle != nullptr) {
|
||||||
xTaskNotifyGive(blockUpdateTaskHandle);
|
xTaskNotifyGive(blockUpdateTaskHandle);
|
||||||
|
if (preferences.getBool("ledFlashOnUpd", false)) {
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(250)); // Wait until screens are updated
|
||||||
queueLedEffect(LED_FLASH_BLOCK_NOTIFY);
|
queueLedEffect(LED_FLASH_BLOCK_NOTIFY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
doc.clear();
|
doc.clear();
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ std::map<int, std::string> screenNameMap;
|
||||||
|
|
||||||
void setup()
|
void setup()
|
||||||
{
|
{
|
||||||
|
setupPreferences();
|
||||||
setupHardware();
|
setupHardware();
|
||||||
if (mcp.digitalRead(3) == LOW)
|
if (mcp.digitalRead(3) == LOW)
|
||||||
{
|
{
|
||||||
|
@ -20,7 +21,6 @@ void setup()
|
||||||
setupDisplays();
|
setupDisplays();
|
||||||
tryImprovSetup();
|
tryImprovSetup();
|
||||||
|
|
||||||
setupPreferences();
|
|
||||||
setupWebserver();
|
setupWebserver();
|
||||||
|
|
||||||
// setupWifi();
|
// setupWifi();
|
||||||
|
@ -112,9 +112,15 @@ void setupTimers()
|
||||||
|
|
||||||
void finishSetup()
|
void finishSetup()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
if (preferences.getBool("ledStatus", false)) {
|
||||||
|
setLights(preferences.getUInt("ledColor", 0xFFCC00));
|
||||||
|
} else {
|
||||||
clearLeds();
|
clearLeds();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
std::map<int, std::string> getScreenNameMap() {
|
std::map<int, std::string> getScreenNameMap() {
|
||||||
return screenNameMap;
|
return screenNameMap;
|
||||||
}
|
}
|
||||||
|
@ -122,7 +128,7 @@ std::map<int, std::string> getScreenNameMap() {
|
||||||
void setupHardware()
|
void setupHardware()
|
||||||
{
|
{
|
||||||
setupLeds();
|
setupLeds();
|
||||||
|
WiFi.setHostname(getMyHostname().c_str());;
|
||||||
if (psramInit())
|
if (psramInit())
|
||||||
{
|
{
|
||||||
Serial.println(F("PSRAM is correctly initialized"));
|
Serial.println(F("PSRAM is correctly initialized"));
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <esp_sntp.h>
|
#include <esp_sntp.h>
|
||||||
#include "epd.hpp"
|
#include "epd.hpp"
|
||||||
#include "improv.hpp"
|
#include "improv.hpp"
|
||||||
|
#include <map>
|
||||||
|
|
||||||
#include "lib/screen_handler.hpp"
|
#include "lib/screen_handler.hpp"
|
||||||
#include "lib/webserver.hpp"
|
#include "lib/webserver.hpp"
|
||||||
|
@ -22,6 +23,7 @@
|
||||||
#define TIME_OFFSET_SECONDS 3600
|
#define TIME_OFFSET_SECONDS 3600
|
||||||
#define USER_AGENT "BTClock/2.0"
|
#define USER_AGENT "BTClock/2.0"
|
||||||
#define MCP_DEV_ADDR 0x20
|
#define MCP_DEV_ADDR 0x20
|
||||||
|
#define DEFAULT_SECONDS_BETWEEN_PRICE_UPDATE 30
|
||||||
|
|
||||||
#define DEFAULT_FG_COLOR GxEPD_WHITE
|
#define DEFAULT_FG_COLOR GxEPD_WHITE
|
||||||
#define DEFAULT_BG_COLOR GxEPD_BLACK
|
#define DEFAULT_BG_COLOR GxEPD_BLACK
|
||||||
|
|
|
@ -148,7 +148,16 @@ extern "C" void updateDisplay(void *pvParameters) noexcept
|
||||||
|
|
||||||
if (epdContent[epdIndex].compareTo(currentEpdContent[epdIndex]) != 0)
|
if (epdContent[epdIndex].compareTo(currentEpdContent[epdIndex]) != 0)
|
||||||
{
|
{
|
||||||
|
|
||||||
displays[epdIndex].init(0, false); // Little longer reset duration because of MCP
|
displays[epdIndex].init(0, false); // Little longer reset duration because of MCP
|
||||||
|
uint count = 0;
|
||||||
|
while (EPD_BUSY[epdIndex].digitalRead() == HIGH || count < 10) {
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(100));
|
||||||
|
if (count >= 9) {
|
||||||
|
displays[epdIndex].init(0, false);
|
||||||
|
}
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
bool updatePartial = true;
|
bool updatePartial = true;
|
||||||
|
|
||||||
|
|
|
@ -185,20 +185,30 @@ void blinkDelayTwoColor(int d, int times, uint32_t c1, uint32_t c2)
|
||||||
|
|
||||||
void clearLeds()
|
void clearLeds()
|
||||||
{
|
{
|
||||||
|
preferences.putBool("ledStatus", false);
|
||||||
pixels.clear();
|
pixels.clear();
|
||||||
pixels.show();
|
pixels.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setLights(int r, int g, int b)
|
void setLights(int r, int g, int b)
|
||||||
{
|
{
|
||||||
|
setLights(pixels.Color(r, g, b));
|
||||||
|
}
|
||||||
|
|
||||||
|
void setLights(uint32_t color)
|
||||||
|
{
|
||||||
|
preferences.putUInt("ledColor", color);
|
||||||
|
preferences.putBool("ledStatus", true);
|
||||||
|
|
||||||
for (int i = 0; i < NEOPIXEL_COUNT; i++)
|
for (int i = 0; i < NEOPIXEL_COUNT; i++)
|
||||||
{
|
{
|
||||||
pixels.setPixelColor(i, pixels.Color(r, g, b));
|
pixels.setPixelColor(i, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
pixels.show();
|
pixels.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
QueueHandle_t getLedTaskQueue()
|
QueueHandle_t getLedTaskQueue()
|
||||||
{
|
{
|
||||||
return ledTaskQueue;
|
return ledTaskQueue;
|
||||||
|
|
|
@ -36,3 +36,4 @@ void clearLeds();
|
||||||
QueueHandle_t getLedTaskQueue();
|
QueueHandle_t getLedTaskQueue();
|
||||||
bool queueLedEffect(uint effect);
|
bool queueLedEffect(uint effect);
|
||||||
void setLights(int r, int g, int b);
|
void setLights(int r, int g, int b);
|
||||||
|
void setLights(uint32_t color);
|
|
@ -4,6 +4,7 @@ const char *wsServerPrice = "wss://ws.coincap.io/prices?assets=bitcoin";
|
||||||
// WebsocketsClient client;
|
// WebsocketsClient client;
|
||||||
esp_websocket_client_handle_t clientPrice = NULL;
|
esp_websocket_client_handle_t clientPrice = NULL;
|
||||||
unsigned long int currentPrice;
|
unsigned long int currentPrice;
|
||||||
|
unsigned long int lastPriceUpdate = 0;
|
||||||
|
|
||||||
void setupPriceNotify()
|
void setupPriceNotify()
|
||||||
{
|
{
|
||||||
|
@ -47,9 +48,14 @@ void onWebsocketPriceMessage(esp_websocket_event_data_t* event_data)
|
||||||
if (doc.containsKey("bitcoin")) {
|
if (doc.containsKey("bitcoin")) {
|
||||||
if (currentPrice != doc["bitcoin"].as<long>()) {
|
if (currentPrice != doc["bitcoin"].as<long>()) {
|
||||||
|
|
||||||
const unsigned long oldPrice = currentPrice;
|
uint minSecPriceUpd = preferences.getUInt("minSecPriceUpd", DEFAULT_SECONDS_BETWEEN_PRICE_UPDATE);
|
||||||
|
uint currentTime = esp_timer_get_time() / 1000000;
|
||||||
|
|
||||||
|
if (lastPriceUpdate == 0 || (currentTime - lastPriceUpdate) > minSecPriceUpd) {
|
||||||
|
// const unsigned long oldPrice = currentPrice;
|
||||||
currentPrice = doc["bitcoin"].as<long>();
|
currentPrice = doc["bitcoin"].as<long>();
|
||||||
|
|
||||||
|
lastPriceUpdate = currentTime;
|
||||||
// if (abs((int)(oldPrice-currentPrice)) > round(0.0015*oldPrice)) {
|
// if (abs((int)(oldPrice-currentPrice)) > round(0.0015*oldPrice)) {
|
||||||
if (priceUpdateTaskHandle != nullptr && (getCurrentScreen() == SCREEN_BTC_TICKER || getCurrentScreen() == SCREEN_MSCW_TIME))
|
if (priceUpdateTaskHandle != nullptr && (getCurrentScreen() == SCREEN_BTC_TICKER || getCurrentScreen() == SCREEN_MSCW_TIME))
|
||||||
xTaskNotifyGive(priceUpdateTaskHandle);
|
xTaskNotifyGive(priceUpdateTaskHandle);
|
||||||
|
@ -57,6 +63,7 @@ void onWebsocketPriceMessage(esp_websocket_event_data_t* event_data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
unsigned long getPrice() {
|
unsigned long getPrice() {
|
||||||
return currentPrice;
|
return currentPrice;
|
||||||
|
|
|
@ -180,6 +180,8 @@ void setupTasks()
|
||||||
xTaskCreate(taskBlockUpdate, "updateBlock", 2048, NULL, tskIDLE_PRIORITY, &blockUpdateTaskHandle);
|
xTaskCreate(taskBlockUpdate, "updateBlock", 2048, NULL, tskIDLE_PRIORITY, &blockUpdateTaskHandle);
|
||||||
xTaskCreate(taskTimeUpdate, "updateTime", 4096, NULL, tskIDLE_PRIORITY, &timeUpdateTaskHandle);
|
xTaskCreate(taskTimeUpdate, "updateTime", 4096, NULL, tskIDLE_PRIORITY, &timeUpdateTaskHandle);
|
||||||
xTaskCreate(taskScreenRotate, "rotateScreen", 2048, NULL, tskIDLE_PRIORITY, &taskScreenRotateTaskHandle);
|
xTaskCreate(taskScreenRotate, "rotateScreen", 2048, NULL, tskIDLE_PRIORITY, &taskScreenRotateTaskHandle);
|
||||||
|
|
||||||
|
setCurrentScreen(preferences.getUInt("currentScreen", 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
void setupTimeUpdateTimer(void *pvParameters)
|
void setupTimeUpdateTimer(void *pvParameters)
|
||||||
|
@ -212,7 +214,10 @@ void setupScreenRotateTimer(void *pvParameters)
|
||||||
.name = "screen_rotate_timer"};
|
.name = "screen_rotate_timer"};
|
||||||
|
|
||||||
esp_timer_create(&screenRotateTimerConfig, &screenRotateTimer);
|
esp_timer_create(&screenRotateTimerConfig, &screenRotateTimer);
|
||||||
|
|
||||||
|
if (preferences.getBool("timerActive", true)) {
|
||||||
esp_timer_start_periodic(screenRotateTimer, getTimerSeconds() * usPerSecond);
|
esp_timer_start_periodic(screenRotateTimer, getTimerSeconds() * usPerSecond);
|
||||||
|
}
|
||||||
|
|
||||||
vTaskDelete(NULL);
|
vTaskDelete(NULL);
|
||||||
}
|
}
|
||||||
|
@ -233,11 +238,13 @@ void setTimerActive(bool status)
|
||||||
{
|
{
|
||||||
esp_timer_start_periodic(screenRotateTimer, getTimerSeconds() * usPerSecond);
|
esp_timer_start_periodic(screenRotateTimer, getTimerSeconds() * usPerSecond);
|
||||||
queueLedEffect(LED_EFFECT_START_TIMER);
|
queueLedEffect(LED_EFFECT_START_TIMER);
|
||||||
|
preferences.putBool("timerActive", true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
esp_timer_stop(screenRotateTimer);
|
esp_timer_stop(screenRotateTimer);
|
||||||
queueLedEffect(LED_EFFECT_PAUSE_TIMER);
|
queueLedEffect(LED_EFFECT_PAUSE_TIMER);
|
||||||
|
preferences.putBool("timerActive", false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,3 +4,9 @@ int modulo(int x, int N)
|
||||||
{
|
{
|
||||||
return (x % N + N) % N;
|
return (x % N + N) % N;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String getMyHostname() {
|
||||||
|
byte mac[6];
|
||||||
|
WiFi.macAddress(mac);
|
||||||
|
return "btclock" + String(mac[4], 16) = String(mac[5], 16);
|
||||||
|
}
|
|
@ -1 +1,7 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <WiFi.h>
|
||||||
|
#include "shared.hpp"
|
||||||
|
|
||||||
int modulo(int x,int N);
|
int modulo(int x,int N);
|
||||||
|
String getMyHostname();
|
|
@ -1,6 +1,7 @@
|
||||||
#include "webserver.hpp"
|
#include "webserver.hpp"
|
||||||
|
|
||||||
AsyncWebServer server(80);
|
AsyncWebServer server(80);
|
||||||
|
AsyncEventSource events("/events");
|
||||||
|
|
||||||
void setupWebserver()
|
void setupWebserver()
|
||||||
{
|
{
|
||||||
|
@ -10,6 +11,18 @@ void setupWebserver()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
events.onConnect([](AsyncEventSourceClient *client)
|
||||||
|
{
|
||||||
|
if (client->lastId())
|
||||||
|
{
|
||||||
|
Serial.printf("Client reconnected! Last message ID that it gat is: %u\n", client->lastId());
|
||||||
|
}
|
||||||
|
// send event with message "hello!", id current millis
|
||||||
|
// and set reconnect delay to 1 second
|
||||||
|
eventSourceLoop();
|
||||||
|
});
|
||||||
|
server.addHandler(&events);
|
||||||
|
|
||||||
server.serveStatic("/css", LittleFS, "/css/");
|
server.serveStatic("/css", LittleFS, "/css/");
|
||||||
server.serveStatic("/js", LittleFS, "/js/");
|
server.serveStatic("/js", LittleFS, "/js/");
|
||||||
server.serveStatic("/font", LittleFS, "/font/");
|
server.serveStatic("/font", LittleFS, "/font/");
|
||||||
|
@ -30,31 +43,38 @@ void setupWebserver()
|
||||||
|
|
||||||
server.on("/api/lights/off", HTTP_GET, onApiLightsOff);
|
server.on("/api/lights/off", HTTP_GET, onApiLightsOff);
|
||||||
server.on("/api/lights/color", HTTP_GET, onApiLightsSetColor);
|
server.on("/api/lights/color", HTTP_GET, onApiLightsSetColor);
|
||||||
server.on("^\\/api\\/lights\\/([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$", HTTP_GET, onApiLightsSetColor);
|
|
||||||
|
// server.on("^\\/api\\/lights\\/([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$", HTTP_GET, onApiLightsSetColor);
|
||||||
|
|
||||||
server.on("/api/restart", HTTP_GET, onApiRestart);
|
server.on("/api/restart", HTTP_GET, onApiRestart);
|
||||||
|
server.addRewrite(new OneParamRewrite("/api/lights/{color}", "/api/lights/color?c={color}"));
|
||||||
server.addRewrite(new OneParamRewrite("/api/show/screen/{s}", "/api/show/screen?s={s}"));
|
server.addRewrite(new OneParamRewrite("/api/show/screen/{s}", "/api/show/screen?s={s}"));
|
||||||
server.addRewrite(new OneParamRewrite("/api/show/text/{text}", "/api/show/text?t={text}"));
|
server.addRewrite(new OneParamRewrite("/api/show/text/{text}", "/api/show/text?t={text}"));
|
||||||
server.addRewrite(new OneParamRewrite("/api/show/number/{number}", "/api/show/text?t={text}"));
|
server.addRewrite(new OneParamRewrite("/api/show/number/{number}", "/api/show/text?t={text}"));
|
||||||
|
|
||||||
server.onNotFound(onNotFound);
|
server.onNotFound(onNotFound);
|
||||||
|
|
||||||
|
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*");
|
||||||
|
|
||||||
server.begin();
|
server.begin();
|
||||||
|
if (!MDNS.begin(getMyHostname()))
|
||||||
|
{
|
||||||
|
Serial.println(F("Error setting up MDNS responder!"));
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
delay(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MDNS.addService("http", "tcp", 80);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
StaticJsonDocument<768> getStatusObject()
|
||||||
* @Api
|
|
||||||
* @Path("/api/status")
|
|
||||||
*/
|
|
||||||
void onApiStatus(AsyncWebServerRequest *request)
|
|
||||||
{
|
{
|
||||||
AsyncResponseStream *response = request->beginResponseStream("application/json");
|
StaticJsonDocument<768> root;
|
||||||
StaticJsonDocument<512> root;
|
|
||||||
|
|
||||||
root["currentScreen"] = getCurrentScreen();
|
root["currentScreen"] = getCurrentScreen();
|
||||||
root["numScreens"] = NUM_SCREENS;
|
root["numScreens"] = NUM_SCREENS;
|
||||||
root["timerRunning"] = isTimerActive();;
|
root["timerRunning"] = isTimerActive();
|
||||||
root["espUptime"] = esp_timer_get_time() / 1000000;
|
root["espUptime"] = esp_timer_get_time() / 1000000;
|
||||||
root["currentPrice"] = getPrice();
|
root["currentPrice"] = getPrice();
|
||||||
root["currentBlockHeight"] = getBlockHeight();
|
root["currentBlockHeight"] = getBlockHeight();
|
||||||
|
@ -67,6 +87,37 @@ void onApiStatus(AsyncWebServerRequest *request)
|
||||||
conStatus["price"] = isPriceNotifyConnected();
|
conStatus["price"] = isPriceNotifyConnected();
|
||||||
conStatus["blocks"] = isBlockNotifyConnected();
|
conStatus["blocks"] = isBlockNotifyConnected();
|
||||||
|
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
void eventSourceLoop()
|
||||||
|
{
|
||||||
|
if (!events.count()) return;
|
||||||
|
StaticJsonDocument<768> root = getStatusObject();
|
||||||
|
JsonArray data = root.createNestedArray("data");
|
||||||
|
String epdContent[NUM_SCREENS];
|
||||||
|
std::array<String, NUM_SCREENS> retEpdContent = getCurrentEpdContent();
|
||||||
|
std::copy(std::begin(retEpdContent), std::end(retEpdContent), epdContent);
|
||||||
|
|
||||||
|
copyArray(epdContent, data);
|
||||||
|
|
||||||
|
size_t bufSize = measureJson(root);
|
||||||
|
char buffer[bufSize];
|
||||||
|
String bufString;
|
||||||
|
serializeJson(root, bufString);
|
||||||
|
|
||||||
|
events.send(bufString.c_str(), "status");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Api
|
||||||
|
* @Path("/api/status")
|
||||||
|
*/
|
||||||
|
void onApiStatus(AsyncWebServerRequest *request)
|
||||||
|
{
|
||||||
|
AsyncResponseStream *response = request->beginResponseStream("application/json");
|
||||||
|
|
||||||
|
StaticJsonDocument<768> root = getStatusObject();
|
||||||
JsonArray data = root.createNestedArray("data");
|
JsonArray data = root.createNestedArray("data");
|
||||||
JsonArray rendered = root.createNestedArray("rendered");
|
JsonArray rendered = root.createNestedArray("rendered");
|
||||||
String epdContent[NUM_SCREENS];
|
String epdContent[NUM_SCREENS];
|
||||||
|
@ -107,7 +158,6 @@ void onApiActionTimerRestart(AsyncWebServerRequest *request)
|
||||||
request->send(200);
|
request->send(200);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void onApiShowScreen(AsyncWebServerRequest *request)
|
void onApiShowScreen(AsyncWebServerRequest *request)
|
||||||
{
|
{
|
||||||
if (request->hasParam("s"))
|
if (request->hasParam("s"))
|
||||||
|
@ -128,7 +178,8 @@ void onApiShowText(AsyncWebServerRequest *request)
|
||||||
t.toUpperCase(); // This is needed as long as lowercase letters are glitchy
|
t.toUpperCase(); // This is needed as long as lowercase letters are glitchy
|
||||||
|
|
||||||
std::array<String, NUM_SCREENS> textEpdContent;
|
std::array<String, NUM_SCREENS> textEpdContent;
|
||||||
for (uint i = 0; i < NUM_SCREENS; i++) {
|
for (uint i = 0; i < NUM_SCREENS; i++)
|
||||||
|
{
|
||||||
textEpdContent[i] = t[i];
|
textEpdContent[i] = t[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,7 +207,8 @@ void onApiSettingsGet(AsyncWebServerRequest *request)
|
||||||
root["fgColor"] = getFgColor();
|
root["fgColor"] = getFgColor();
|
||||||
root["bgColor"] = getBgColor();
|
root["bgColor"] = getBgColor();
|
||||||
root["timerSeconds"] = getTimerSeconds();
|
root["timerSeconds"] = getTimerSeconds();
|
||||||
root["timerRunning"] = isTimerActive();;
|
root["timerRunning"] = isTimerActive();
|
||||||
|
root["minSecPriceUpd"] = preferences.getUInt("minSecPriceUpd", DEFAULT_SECONDS_BETWEEN_PRICE_UPDATE);
|
||||||
root["fullRefreshMin"] = preferences.getUInt("fullRefreshMin", 30);
|
root["fullRefreshMin"] = preferences.getUInt("fullRefreshMin", 30);
|
||||||
root["wpTimeout"] = preferences.getUInt("wpTimeout", 600);
|
root["wpTimeout"] = preferences.getUInt("wpTimeout", 600);
|
||||||
root["tzOffset"] = preferences.getInt("gmtOffset", TIME_OFFSET_SECONDS) / 60;
|
root["tzOffset"] = preferences.getInt("gmtOffset", TIME_OFFSET_SECONDS) / 60;
|
||||||
|
@ -166,7 +218,6 @@ void onApiSettingsGet(AsyncWebServerRequest *request)
|
||||||
root["rpcHost"] = preferences.getString("rpcHost", BITCOIND_HOST);
|
root["rpcHost"] = preferences.getString("rpcHost", BITCOIND_HOST);
|
||||||
root["mempoolInstance"] = preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE);
|
root["mempoolInstance"] = preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE);
|
||||||
|
|
||||||
root["epdColors"] = 2;
|
|
||||||
root["ledFlashOnUpdate"] = preferences.getBool("ledFlashOnUpd", false);
|
root["ledFlashOnUpdate"] = preferences.getBool("ledFlashOnUpd", false);
|
||||||
root["ledBrightness"] = preferences.getUInt("ledBrightness", 128);
|
root["ledBrightness"] = preferences.getUInt("ledBrightness", 128);
|
||||||
|
|
||||||
|
@ -312,6 +363,16 @@ void onApiSettingsPost(AsyncWebServerRequest *request)
|
||||||
settingsChanged = true;
|
settingsChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (request->hasParam("minSecPriceUpd", true))
|
||||||
|
{
|
||||||
|
AsyncWebParameter *p = request->getParam("minSecPriceUpd", true);
|
||||||
|
int minSecPriceUpd = p->value().toInt() * 60;
|
||||||
|
preferences.putInt("minSecPriceUpd", minSecPriceUpd);
|
||||||
|
Serial.print("Setting minSecPriceUpd ");
|
||||||
|
Serial.println(minSecPriceUpd);
|
||||||
|
settingsChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (request->hasParam("timePerScreen", true))
|
if (request->hasParam("timePerScreen", true))
|
||||||
{
|
{
|
||||||
AsyncWebParameter *p = request->getParam("timePerScreen", true);
|
AsyncWebParameter *p = request->getParam("timePerScreen", true);
|
||||||
|
@ -381,13 +442,15 @@ void onApiLightsOff(AsyncWebServerRequest *request)
|
||||||
|
|
||||||
void onApiLightsSetColor(AsyncWebServerRequest *request)
|
void onApiLightsSetColor(AsyncWebServerRequest *request)
|
||||||
{
|
{
|
||||||
String rgbColor = request->pathArg(0);
|
if (request->hasParam("c"))
|
||||||
|
{
|
||||||
|
String rgbColor = request->getParam("c")->value();
|
||||||
uint r, g, b;
|
uint r, g, b;
|
||||||
sscanf(rgbColor.c_str(), "%02x%02x%02x", &r, &g, &b);
|
sscanf(rgbColor.c_str(), "%02x%02x%02x", &r, &g, &b);
|
||||||
setLights(r, g, b);
|
setLights(r, g, b);
|
||||||
request->send(200, "text/plain", rgbColor);
|
request->send(200, "text/plain", rgbColor);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void onIndex(AsyncWebServerRequest *request) { request->send(LittleFS, "/index.html", String(), false); }
|
void onIndex(AsyncWebServerRequest *request) { request->send(LittleFS, "/index.html", String(), false); }
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "ESPAsyncWebServer.h"
|
#include "ESPAsyncWebServer.h"
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include <LittleFS.h>
|
#include <LittleFS.h>
|
||||||
|
#include <ESPmDNS.h>
|
||||||
|
|
||||||
#include "lib/block_notify.hpp"
|
#include "lib/block_notify.hpp"
|
||||||
#include "lib/price_notify.hpp"
|
#include "lib/price_notify.hpp"
|
||||||
|
@ -33,3 +34,6 @@ void onApiRestart(AsyncWebServerRequest *request);
|
||||||
|
|
||||||
void onIndex(AsyncWebServerRequest *request);
|
void onIndex(AsyncWebServerRequest *request);
|
||||||
void onNotFound(AsyncWebServerRequest *request);
|
void onNotFound(AsyncWebServerRequest *request);
|
||||||
|
|
||||||
|
StaticJsonDocument<768> getStatusObject();
|
||||||
|
void eventSourceLoop();
|
|
@ -10,6 +10,7 @@ extern "C" void app_main()
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
vTaskDelay(pdMS_TO_TICKS(5000));
|
eventSourceLoop();
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(2500));
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue