Refactor BlockNotify to a class, use websocketsClient
This commit is contained in:
parent
ebbec75e6b
commit
e330984ba2
9 changed files with 239 additions and 257 deletions
|
@ -1,186 +1,146 @@
|
||||||
#include "block_notify.hpp"
|
#include "block_notify.hpp"
|
||||||
#include "led_handler.hpp"
|
|
||||||
|
|
||||||
char *wsServer;
|
// Initialize static members
|
||||||
esp_websocket_client_handle_t blockNotifyClient = NULL;
|
WebSocketsClient BlockNotify::webSocket;
|
||||||
uint32_t currentBlockHeight = 873400;
|
uint32_t BlockNotify::currentBlockHeight = 878000;
|
||||||
uint16_t blockMedianFee = 1;
|
uint16_t BlockNotify::blockMedianFee = 1;
|
||||||
bool blockNotifyInit = false;
|
bool BlockNotify::notifyInit = false;
|
||||||
unsigned long int lastBlockUpdate;
|
unsigned long int BlockNotify::lastBlockUpdate = 0;
|
||||||
|
TaskHandle_t BlockNotify::taskHandle = nullptr;
|
||||||
|
|
||||||
const char *mempoolWsCert = R"EOF(
|
void BlockNotify::taskNotify(void* pvParameters)
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB
|
|
||||||
iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl
|
|
||||||
cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV
|
|
||||||
BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw
|
|
||||||
MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV
|
|
||||||
BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU
|
|
||||||
aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy
|
|
||||||
dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
|
|
||||||
AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B
|
|
||||||
3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY
|
|
||||||
tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/
|
|
||||||
Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2
|
|
||||||
VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT
|
|
||||||
79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6
|
|
||||||
c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT
|
|
||||||
Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l
|
|
||||||
c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee
|
|
||||||
UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE
|
|
||||||
Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd
|
|
||||||
BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G
|
|
||||||
A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF
|
|
||||||
Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO
|
|
||||||
VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3
|
|
||||||
ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs
|
|
||||||
8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR
|
|
||||||
iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze
|
|
||||||
Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ
|
|
||||||
XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/
|
|
||||||
qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB
|
|
||||||
VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB
|
|
||||||
L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG
|
|
||||||
jjxDah2nGN59PRbxYvnKkKj9
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
)EOF";
|
|
||||||
|
|
||||||
void setupBlockNotify()
|
|
||||||
{
|
{
|
||||||
IPAddress result;
|
for (;;)
|
||||||
|
|
||||||
int dnsErr = -1;
|
|
||||||
String mempoolInstance =
|
|
||||||
preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE);
|
|
||||||
|
|
||||||
while (dnsErr != 1 && !strchr(mempoolInstance.c_str(), ':'))
|
|
||||||
{
|
|
||||||
dnsErr = WiFi.hostByName(mempoolInstance.c_str(), result);
|
|
||||||
|
|
||||||
if (dnsErr != 1)
|
|
||||||
{
|
{
|
||||||
Serial.print(mempoolInstance);
|
webSocket.loop();
|
||||||
Serial.println(F("mempool DNS could not be resolved"));
|
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||||
WiFi.reconnect();
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Get current block height through regular API
|
|
||||||
int blockFetch = getBlockFetch();
|
|
||||||
|
|
||||||
if (blockFetch > currentBlockHeight)
|
|
||||||
currentBlockHeight = blockFetch;
|
|
||||||
|
|
||||||
if (currentBlockHeight != -1)
|
|
||||||
{
|
|
||||||
lastBlockUpdate = esp_timer_get_time() / 1000000;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (workQueue != nullptr)
|
|
||||||
{
|
|
||||||
WorkItem blockUpdate = {TASK_BLOCK_UPDATE, 0};
|
|
||||||
xQueueSend(workQueue, &blockUpdate, portMAX_DELAY);
|
|
||||||
}
|
|
||||||
|
|
||||||
// std::strcpy(wsServer, String("wss://" + mempoolInstance +
|
|
||||||
// "/api/v1/ws").c_str());
|
|
||||||
|
|
||||||
const String protocol = preferences.getBool("mempoolSecure", DEFAULT_MEMPOOL_SECURE) ? "wss" : "ws";
|
|
||||||
|
|
||||||
String mempoolUri = protocol + "://" + preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE) + "/api/v1/ws";
|
|
||||||
|
|
||||||
esp_websocket_client_config_t config = {
|
|
||||||
// .uri = "wss://mempool.space/api/v1/ws",
|
|
||||||
.task_stack = (6*1024),
|
|
||||||
.user_agent = USER_AGENT
|
|
||||||
};
|
|
||||||
|
|
||||||
if (preferences.getBool("mempoolSecure", DEFAULT_MEMPOOL_SECURE)) {
|
|
||||||
config.cert_pem = mempoolWsCert;
|
|
||||||
}
|
|
||||||
|
|
||||||
config.uri = mempoolUri.c_str();
|
|
||||||
|
|
||||||
Serial.printf("Connecting to %s\r\n", preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE));
|
|
||||||
|
|
||||||
blockNotifyClient = esp_websocket_client_init(&config);
|
|
||||||
esp_websocket_register_events(blockNotifyClient, WEBSOCKET_EVENT_ANY,
|
|
||||||
onWebsocketBlockEvent, blockNotifyClient);
|
|
||||||
esp_websocket_client_start(blockNotifyClient);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void onWebsocketBlockEvent(void *handler_args, esp_event_base_t base,
|
void BlockNotify::setupTask()
|
||||||
int32_t event_id, void *event_data)
|
|
||||||
{
|
{
|
||||||
esp_websocket_event_data_t *data = (esp_websocket_event_data_t *)event_data;
|
xTaskCreate(taskNotify, "blockNotify", (6 * 1024), NULL, tskIDLE_PRIORITY,
|
||||||
const String sub = "{\"action\": \"want\", \"data\":[\"blocks\", \"mempool-blocks\"]}";
|
&taskHandle);
|
||||||
switch (event_id)
|
}
|
||||||
{
|
|
||||||
case WEBSOCKET_EVENT_CONNECTED:
|
|
||||||
blockNotifyInit = true;
|
|
||||||
|
|
||||||
Serial.println(F("Connected to Mempool.space WebSocket"));
|
void BlockNotify::onWebsocketEvent(WStype_t type, uint8_t* payload, size_t length) {
|
||||||
|
switch(type) {
|
||||||
|
case WStype_DISCONNECTED: {
|
||||||
|
Serial.println(F("Mempool.space WS Connection Closed"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WStype_CONNECTED: {
|
||||||
|
notifyInit = true;
|
||||||
|
Serial.print(F("Connected to "));
|
||||||
|
Serial.println(preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE));
|
||||||
|
|
||||||
Serial.println(sub);
|
JsonDocument doc;
|
||||||
if (esp_websocket_client_send_text(blockNotifyClient, sub.c_str(),
|
doc["action"] = "want";
|
||||||
sub.length(), portMAX_DELAY) == -1)
|
JsonArray data = doc.createNestedArray("data");
|
||||||
|
data.add("blocks");
|
||||||
|
data.add("mempool-blocks");
|
||||||
|
|
||||||
|
String sub;
|
||||||
|
serializeJson(doc, sub);
|
||||||
|
Serial.println(sub);
|
||||||
|
webSocket.sendTXT(sub.c_str());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WStype_TEXT: {
|
||||||
|
JsonDocument doc;
|
||||||
|
JsonDocument filter;
|
||||||
|
filter["block"]["height"] = true;
|
||||||
|
filter["mempool-blocks"][0]["medianFee"] = true;
|
||||||
|
|
||||||
|
deserializeJson(doc, (char*)payload, DeserializationOption::Filter(filter));
|
||||||
|
|
||||||
|
if (debugLogEnabled()) {
|
||||||
|
Serial.println(doc.as<String>());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (doc["block"].is<JsonObject>())
|
||||||
|
{
|
||||||
|
JsonObject block = doc["block"];
|
||||||
|
if (block["height"].as<uint>() != currentBlockHeight) {
|
||||||
|
BlockNotify::getInstance().processNewBlock(block["height"].as<uint>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (doc["mempool-blocks"].is<JsonArray>())
|
||||||
|
{
|
||||||
|
JsonArray blockInfo = doc["mempool-blocks"].as<JsonArray>();
|
||||||
|
uint medianFee = (uint)round(blockInfo[0]["medianFee"].as<double>());
|
||||||
|
BlockNotify::getInstance().processNewBlockFee(medianFee);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WStype_BIN:
|
||||||
|
case WStype_ERROR:
|
||||||
|
case WStype_FRAGMENT_TEXT_START:
|
||||||
|
case WStype_FRAGMENT_BIN_START:
|
||||||
|
case WStype_FRAGMENT:
|
||||||
|
case WStype_PING:
|
||||||
|
case WStype_PONG:
|
||||||
|
case WStype_FRAGMENT_FIN: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlockNotify::setup()
|
||||||
|
{
|
||||||
|
IPAddress result;
|
||||||
|
int dnsErr = -1;
|
||||||
|
String mempoolInstance = preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE);
|
||||||
|
|
||||||
|
while (dnsErr != 1 && !strchr(mempoolInstance.c_str(), ':'))
|
||||||
{
|
{
|
||||||
Serial.println(F("Mempool.space WS Block Subscribe Error"));
|
dnsErr = WiFi.hostByName(mempoolInstance.c_str(), result);
|
||||||
|
|
||||||
|
if (dnsErr != 1)
|
||||||
|
{
|
||||||
|
Serial.print(mempoolInstance);
|
||||||
|
Serial.println(F("mempool DNS could not be resolved"));
|
||||||
|
WiFi.reconnect();
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
// Get current block height through regular API
|
||||||
case WEBSOCKET_EVENT_DATA:
|
int blockFetch = fetchLatestBlock();
|
||||||
onWebsocketBlockMessage(data);
|
|
||||||
break;
|
|
||||||
case WEBSOCKET_EVENT_ERROR:
|
|
||||||
Serial.println(F("Mempool.space WS Connnection error"));
|
|
||||||
break;
|
|
||||||
case WEBSOCKET_EVENT_DISCONNECTED:
|
|
||||||
Serial.println(F("Mempool.space WS Connnection Closed"));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void onWebsocketBlockMessage(esp_websocket_event_data_t *event_data)
|
if (blockFetch > currentBlockHeight)
|
||||||
{
|
currentBlockHeight = blockFetch;
|
||||||
JsonDocument doc;
|
|
||||||
|
|
||||||
JsonDocument filter;
|
if (currentBlockHeight != -1)
|
||||||
filter["block"]["height"] = true;
|
{
|
||||||
filter["mempool-blocks"][0]["medianFee"] = true;
|
lastBlockUpdate = esp_timer_get_time() / 1000000;
|
||||||
|
|
||||||
deserializeJson(doc, (char *)event_data->data_ptr, DeserializationOption::Filter(filter));
|
|
||||||
|
|
||||||
// if (error) {
|
|
||||||
// Serial.print("deserializeJson() failed: ");
|
|
||||||
// Serial.println(error.c_str());
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
if (doc["block"].is<JsonObject>())
|
|
||||||
{
|
|
||||||
JsonObject block = doc["block"];
|
|
||||||
|
|
||||||
if (block["height"].as<uint>() == currentBlockHeight) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
processNewBlock(block["height"].as<uint>());
|
if (workQueue != nullptr)
|
||||||
}
|
{
|
||||||
else if (doc["mempool-blocks"].is<JsonArray>())
|
WorkItem blockUpdate = {TASK_BLOCK_UPDATE, 0};
|
||||||
{
|
xQueueSend(workQueue, &blockUpdate, portMAX_DELAY);
|
||||||
JsonArray blockInfo = doc["mempool-blocks"].as<JsonArray>();
|
}
|
||||||
|
|
||||||
uint medianFee = (uint)round(blockInfo[0]["medianFee"].as<double>());
|
const bool useSSL = preferences.getBool("mempoolSecure", DEFAULT_MEMPOOL_SECURE);
|
||||||
|
const int port = useSSL ? 443 : 80;
|
||||||
|
|
||||||
processNewBlockFee(medianFee);
|
if (useSSL) {
|
||||||
}
|
webSocket.beginSSL(mempoolInstance.c_str(), port, "/api/v1/ws");
|
||||||
|
// webSocket.beginSSL("ws.btclock.dev", port, "/api/v1/ws");
|
||||||
|
|
||||||
doc.clear();
|
} else {
|
||||||
|
webSocket.begin(mempoolInstance.c_str(), port, "/api/v1/ws");
|
||||||
|
}
|
||||||
|
|
||||||
|
webSocket.onEvent(onWebsocketEvent);
|
||||||
|
webSocket.setReconnectInterval(5000);
|
||||||
|
webSocket.enableHeartbeat(15000, 3000, 2);
|
||||||
|
|
||||||
|
setupTask();
|
||||||
}
|
}
|
||||||
|
|
||||||
void processNewBlock(uint32_t newBlockHeight) {
|
void BlockNotify::processNewBlock(uint32_t newBlockHeight) {
|
||||||
if (newBlockHeight <= currentBlockHeight)
|
if (newBlockHeight <= currentBlockHeight)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
@ -220,76 +180,65 @@ void processNewBlock(uint32_t newBlockHeight) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void processNewBlockFee(uint16_t newBlockFee) {
|
void BlockNotify::processNewBlockFee(uint16_t newBlockFee) {
|
||||||
if (blockMedianFee == newBlockFee)
|
if (blockMedianFee == newBlockFee)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serial.printf("New median fee: %d\r\n", medianFee);
|
|
||||||
blockMedianFee = newBlockFee;
|
blockMedianFee = newBlockFee;
|
||||||
|
|
||||||
if (workQueue != nullptr)
|
if (workQueue != nullptr)
|
||||||
{
|
{
|
||||||
WorkItem blockUpdate = {TASK_FEE_UPDATE, 0};
|
WorkItem blockUpdate = {TASK_FEE_UPDATE, 0};
|
||||||
xQueueSend(workQueue, &blockUpdate, portMAX_DELAY);
|
xQueueSend(workQueue, &blockUpdate, portMAX_DELAY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t getBlockHeight() { return currentBlockHeight; }
|
uint32_t BlockNotify::getBlockHeight() const {
|
||||||
|
return currentBlockHeight;
|
||||||
void setBlockHeight(uint32_t newBlockHeight)
|
|
||||||
{
|
|
||||||
currentBlockHeight = newBlockHeight;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t getBlockMedianFee() { return blockMedianFee; }
|
void BlockNotify::setBlockHeight(uint32_t newBlockHeight)
|
||||||
|
|
||||||
void setBlockMedianFee(uint16_t newBlockMedianFee)
|
|
||||||
{
|
{
|
||||||
blockMedianFee = newBlockMedianFee;
|
currentBlockHeight = newBlockHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isBlockNotifyConnected()
|
uint16_t BlockNotify::getBlockMedianFee() const {
|
||||||
{
|
return blockMedianFee;
|
||||||
if (blockNotifyClient == NULL)
|
|
||||||
return false;
|
|
||||||
return esp_websocket_client_is_connected(blockNotifyClient);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool getBlockNotifyInit()
|
void BlockNotify::setBlockMedianFee(uint16_t newBlockMedianFee)
|
||||||
{
|
{
|
||||||
return blockNotifyInit;
|
blockMedianFee = newBlockMedianFee;
|
||||||
}
|
}
|
||||||
|
|
||||||
void stopBlockNotify()
|
bool BlockNotify::isConnected() const
|
||||||
{
|
{
|
||||||
if (blockNotifyClient == NULL)
|
return webSocket.isConnected();
|
||||||
return;
|
|
||||||
|
|
||||||
esp_websocket_client_close(blockNotifyClient, pdMS_TO_TICKS(5000));
|
|
||||||
esp_websocket_client_stop(blockNotifyClient);
|
|
||||||
esp_websocket_client_destroy(blockNotifyClient);
|
|
||||||
|
|
||||||
blockNotifyClient = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void restartBlockNotify()
|
bool BlockNotify::isInitialized() const
|
||||||
{
|
{
|
||||||
stopBlockNotify();
|
return notifyInit;
|
||||||
|
|
||||||
if (blockNotifyClient == NULL) {
|
|
||||||
setupBlockNotify();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// esp_websocket_client_close(blockNotifyClient, pdMS_TO_TICKS(5000));
|
|
||||||
// esp_websocket_client_stop(blockNotifyClient);
|
|
||||||
// esp_websocket_client_start(blockNotifyClient);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BlockNotify::stop()
|
||||||
|
{
|
||||||
|
webSocket.disconnect();
|
||||||
|
if (taskHandle != NULL) {
|
||||||
|
vTaskDelete(taskHandle);
|
||||||
|
taskHandle = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int getBlockFetch() {
|
void BlockNotify::restart()
|
||||||
|
{
|
||||||
|
stop();
|
||||||
|
setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
int BlockNotify::fetchLatestBlock() {
|
||||||
try {
|
try {
|
||||||
String mempoolInstance = preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE);
|
String mempoolInstance = preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE);
|
||||||
const String protocol = preferences.getBool("mempoolSecure", DEFAULT_MEMPOOL_SECURE) ? "https" : "http";
|
const String protocol = preferences.getBool("mempoolSecure", DEFAULT_MEMPOOL_SECURE) ? "https" : "http";
|
||||||
|
@ -312,12 +261,12 @@ int getBlockFetch() {
|
||||||
return 2203; // B-T-C
|
return 2203; // B-T-C
|
||||||
}
|
}
|
||||||
|
|
||||||
uint getLastBlockUpdate()
|
uint BlockNotify::getLastBlockUpdate() const
|
||||||
{
|
{
|
||||||
return lastBlockUpdate;
|
return lastBlockUpdate;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setLastBlockUpdate(uint lastUpdate)
|
void BlockNotify::setLastBlockUpdate(uint lastUpdate)
|
||||||
{
|
{
|
||||||
lastBlockUpdate = lastUpdate;
|
lastBlockUpdate = lastUpdate;
|
||||||
}
|
}
|
|
@ -4,8 +4,7 @@
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include <HTTPClient.h>
|
#include <HTTPClient.h>
|
||||||
#include <esp_timer.h>
|
#include <esp_timer.h>
|
||||||
#include <esp_websocket_client.h>
|
#include <WebSocketsClient.h>
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
@ -14,28 +13,54 @@
|
||||||
#include "lib/timers.hpp"
|
#include "lib/timers.hpp"
|
||||||
#include "lib/shared.hpp"
|
#include "lib/shared.hpp"
|
||||||
|
|
||||||
// using namespace websockets;
|
class BlockNotify {
|
||||||
|
public:
|
||||||
|
static BlockNotify& getInstance() {
|
||||||
|
static BlockNotify instance;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
void setupBlockNotify();
|
// Delete copy constructor and assignment operator
|
||||||
|
BlockNotify(const BlockNotify&) = delete;
|
||||||
|
void operator=(const BlockNotify&) = delete;
|
||||||
|
|
||||||
void onWebsocketBlockEvent(void *handler_args, esp_event_base_t base,
|
// Block notification setup and control
|
||||||
int32_t event_id, void *event_data);
|
void setup();
|
||||||
void onWebsocketBlockMessage(esp_websocket_event_data_t *event_data);
|
void stop();
|
||||||
|
void restart();
|
||||||
|
bool isConnected() const;
|
||||||
|
bool isInitialized() const;
|
||||||
|
|
||||||
void setBlockHeight(uint32_t newBlockHeight);
|
// Block height management
|
||||||
uint32_t getBlockHeight();
|
void setBlockHeight(uint32_t newBlockHeight);
|
||||||
|
uint32_t getBlockHeight() const;
|
||||||
|
|
||||||
void setBlockMedianFee(uint16_t blockMedianFee);
|
// Block fee management
|
||||||
uint16_t getBlockMedianFee();
|
void setBlockMedianFee(uint16_t blockMedianFee);
|
||||||
|
uint16_t getBlockMedianFee() const;
|
||||||
|
|
||||||
bool isBlockNotifyConnected();
|
// Block processing
|
||||||
void stopBlockNotify();
|
void processNewBlock(uint32_t newBlockHeight);
|
||||||
void restartBlockNotify();
|
void processNewBlockFee(uint16_t newBlockFee);
|
||||||
|
|
||||||
void processNewBlock(uint32_t newBlockHeight);
|
// Block fetch and update tracking
|
||||||
void processNewBlockFee(uint16_t newBlockFee);
|
int fetchLatestBlock();
|
||||||
|
uint getLastBlockUpdate() const;
|
||||||
|
void setLastBlockUpdate(uint lastUpdate);
|
||||||
|
|
||||||
bool getBlockNotifyInit();
|
// Task handling
|
||||||
uint32_t getLastBlockUpdate();
|
static void taskNotify(void* pvParameters);
|
||||||
int getBlockFetch();
|
|
||||||
void setLastBlockUpdate(uint32_t lastUpdate);
|
private:
|
||||||
|
BlockNotify() = default; // Private constructor for singleton
|
||||||
|
|
||||||
|
void setupTask();
|
||||||
|
static void onWebsocketEvent(WStype_t type, uint8_t* payload, size_t length);
|
||||||
|
|
||||||
|
static WebSocketsClient webSocket;
|
||||||
|
static uint32_t currentBlockHeight;
|
||||||
|
static uint16_t blockMedianFee;
|
||||||
|
static bool notifyInit;
|
||||||
|
static unsigned long int lastBlockUpdate;
|
||||||
|
static TaskHandle_t taskHandle;
|
||||||
|
};
|
|
@ -265,7 +265,7 @@ void setupPreferences()
|
||||||
|
|
||||||
EPDManager::getInstance().setForegroundColor(preferences.getUInt("fgColor", DEFAULT_FG_COLOR));
|
EPDManager::getInstance().setForegroundColor(preferences.getUInt("fgColor", DEFAULT_FG_COLOR));
|
||||||
EPDManager::getInstance().setBackgroundColor(preferences.getUInt("bgColor", DEFAULT_BG_COLOR));
|
EPDManager::getInstance().setBackgroundColor(preferences.getUInt("bgColor", DEFAULT_BG_COLOR));
|
||||||
setBlockHeight(preferences.getUInt("blockHeight", INITIAL_BLOCK_HEIGHT));
|
BlockNotify::getInstance().setBlockHeight(preferences.getUInt("blockHeight", INITIAL_BLOCK_HEIGHT));
|
||||||
setPrice(preferences.getUInt("lastPrice", INITIAL_LAST_PRICE), CURRENCY_USD);
|
setPrice(preferences.getUInt("lastPrice", INITIAL_LAST_PRICE), CURRENCY_USD);
|
||||||
|
|
||||||
if (!preferences.isKey("enableDebugLog")) {
|
if (!preferences.isKey("enableDebugLog")) {
|
||||||
|
@ -373,7 +373,7 @@ void setupWebsocketClients(void *pvParameters)
|
||||||
}
|
}
|
||||||
else if (dataSource == THIRD_PARTY_SOURCE)
|
else if (dataSource == THIRD_PARTY_SOURCE)
|
||||||
{
|
{
|
||||||
setupBlockNotify();
|
BlockNotify::getInstance().setup();
|
||||||
setupPriceNotify();
|
setupPriceNotify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -79,8 +79,9 @@ void nostrTask(void *pvParameters)
|
||||||
{
|
{
|
||||||
DataSourceType dataSource = getDataSource();
|
DataSourceType dataSource = getDataSource();
|
||||||
if(dataSource == NOSTR_SOURCE) {
|
if(dataSource == NOSTR_SOURCE) {
|
||||||
int blockFetch = getBlockFetch();
|
auto& blockNotify = BlockNotify::getInstance();
|
||||||
processNewBlock(blockFetch);
|
int blockFetch = blockNotify.fetchLatestBlock();
|
||||||
|
blockNotify.processNewBlock(blockFetch);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
|
@ -174,11 +175,13 @@ void handleNostrEventCallback(const String &subId, nostr::SignedNostrEvent *even
|
||||||
processNewPrice(obj["content"].as<uint>(), CURRENCY_USD);
|
processNewPrice(obj["content"].as<uint>(), CURRENCY_USD);
|
||||||
}
|
}
|
||||||
else if (typeValue == "blockHeight") {
|
else if (typeValue == "blockHeight") {
|
||||||
processNewBlock(obj["content"].as<uint>());
|
auto& blockNotify = BlockNotify::getInstance();
|
||||||
|
blockNotify.processNewBlock(obj["content"].as<uint>());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (medianFee != 0) {
|
if (medianFee != 0) {
|
||||||
processNewBlockFee(medianFee);
|
auto& blockNotify = BlockNotify::getInstance();
|
||||||
|
blockNotify.processNewBlockFee(medianFee);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,8 +74,8 @@ void onOTAStart()
|
||||||
ButtonHandler::suspendTask();
|
ButtonHandler::suspendTask();
|
||||||
|
|
||||||
// stopWebServer();
|
// stopWebServer();
|
||||||
stopBlockNotify();
|
auto& blockNotify = BlockNotify::getInstance();
|
||||||
stopPriceNotify();
|
blockNotify.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleOTATask(void *parameter)
|
void handleOTATask(void *parameter)
|
||||||
|
|
|
@ -251,9 +251,8 @@ void workerTask(void *pvParameters) {
|
||||||
} else if (currentScreenValue == SCREEN_SATS_PER_CURRENCY) {
|
} else if (currentScreenValue == SCREEN_SATS_PER_CURRENCY) {
|
||||||
taskEpdContent = parseSatsPerCurrency(price, currency, preferences.getBool("useSatsSymbol", DEFAULT_USE_SATS_SYMBOL));
|
taskEpdContent = parseSatsPerCurrency(price, currency, preferences.getBool("useSatsSymbol", DEFAULT_USE_SATS_SYMBOL));
|
||||||
} else {
|
} else {
|
||||||
taskEpdContent =
|
auto& blockNotify = BlockNotify::getInstance();
|
||||||
parseMarketCap(getBlockHeight(), price, currency,
|
taskEpdContent = parseMarketCap(blockNotify.getBlockHeight(), price, currency, preferences.getBool("mcapBigChar", DEFAULT_MCAP_BIG_CHAR));
|
||||||
preferences.getBool("mcapBigChar", DEFAULT_MCAP_BIG_CHAR));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EPDManager::getInstance().setContent(taskEpdContent);
|
EPDManager::getInstance().setContent(taskEpdContent);
|
||||||
|
@ -261,16 +260,19 @@ void workerTask(void *pvParameters) {
|
||||||
}
|
}
|
||||||
case TASK_FEE_UPDATE: {
|
case TASK_FEE_UPDATE: {
|
||||||
if (currentScreenValue == SCREEN_BLOCK_FEE_RATE) {
|
if (currentScreenValue == SCREEN_BLOCK_FEE_RATE) {
|
||||||
taskEpdContent = parseBlockFees(static_cast<std::uint16_t>(getBlockMedianFee()));
|
auto& blockNotify = BlockNotify::getInstance();
|
||||||
|
taskEpdContent = parseBlockFees(static_cast<std::uint16_t>(blockNotify.getBlockMedianFee()));
|
||||||
EPDManager::getInstance().setContent(taskEpdContent);
|
EPDManager::getInstance().setContent(taskEpdContent);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TASK_BLOCK_UPDATE: {
|
case TASK_BLOCK_UPDATE: {
|
||||||
if (currentScreenValue != SCREEN_HALVING_COUNTDOWN) {
|
if (currentScreenValue != SCREEN_HALVING_COUNTDOWN) {
|
||||||
taskEpdContent = parseBlockHeight(getBlockHeight());
|
auto& blockNotify = BlockNotify::getInstance();
|
||||||
|
taskEpdContent = parseBlockHeight(blockNotify.getBlockHeight());
|
||||||
} else {
|
} else {
|
||||||
taskEpdContent = parseHalvingCountdown(getBlockHeight(), preferences.getBool("useBlkCountdown", DEFAULT_USE_BLOCK_COUNTDOWN));
|
auto& blockNotify = BlockNotify::getInstance();
|
||||||
|
taskEpdContent = parseHalvingCountdown(blockNotify.getBlockHeight(), preferences.getBool("useBlkCountdown", DEFAULT_USE_BLOCK_COUNTDOWN));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentScreenValue == SCREEN_HALVING_COUNTDOWN ||
|
if (currentScreenValue == SCREEN_HALVING_COUNTDOWN ||
|
||||||
|
|
|
@ -131,7 +131,7 @@ namespace V2Notify
|
||||||
{
|
{
|
||||||
uint newBlockHeight = doc["blockheight"].as<uint>();
|
uint newBlockHeight = doc["blockheight"].as<uint>();
|
||||||
|
|
||||||
if (newBlockHeight == getBlockHeight())
|
if (newBlockHeight == BlockNotify::getInstance().getBlockHeight())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -140,7 +140,7 @@ namespace V2Notify
|
||||||
Serial.print(F("processNewBlock "));
|
Serial.print(F("processNewBlock "));
|
||||||
Serial.println(newBlockHeight);
|
Serial.println(newBlockHeight);
|
||||||
}
|
}
|
||||||
processNewBlock(newBlockHeight);
|
BlockNotify::getInstance().processNewBlock(newBlockHeight);
|
||||||
}
|
}
|
||||||
else if (doc["blockfee"].is<uint>())
|
else if (doc["blockfee"].is<uint>())
|
||||||
{
|
{
|
||||||
|
@ -151,7 +151,7 @@ namespace V2Notify
|
||||||
Serial.println(medianFee);
|
Serial.println(medianFee);
|
||||||
}
|
}
|
||||||
|
|
||||||
processNewBlockFee(medianFee);
|
BlockNotify::getInstance().processNewBlockFee(medianFee);
|
||||||
}
|
}
|
||||||
else if (doc["price"].is<JsonObject>())
|
else if (doc["price"].is<JsonObject>())
|
||||||
{
|
{
|
||||||
|
|
|
@ -247,7 +247,8 @@ JsonDocument getStatusObject()
|
||||||
JsonObject conStatus = root["connectionStatus"].to<JsonObject>();
|
JsonObject conStatus = root["connectionStatus"].to<JsonObject>();
|
||||||
|
|
||||||
conStatus["price"] = isPriceNotifyConnected();
|
conStatus["price"] = isPriceNotifyConnected();
|
||||||
conStatus["blocks"] = isBlockNotifyConnected();
|
auto& blockNotify = BlockNotify::getInstance();
|
||||||
|
conStatus["blocks"] = blockNotify.isConnected();
|
||||||
conStatus["V2"] = V2Notify::isV2NotifyConnected();
|
conStatus["V2"] = V2Notify::isV2NotifyConnected();
|
||||||
conStatus["nostr"] = nostrConnected();
|
conStatus["nostr"] = nostrConnected();
|
||||||
|
|
||||||
|
@ -906,7 +907,7 @@ void onApiStopDataSources(AsyncWebServerRequest *request)
|
||||||
request->beginResponseStream(JSON_CONTENT);
|
request->beginResponseStream(JSON_CONTENT);
|
||||||
|
|
||||||
stopPriceNotify();
|
stopPriceNotify();
|
||||||
stopBlockNotify();
|
BlockNotify::getInstance().stop();
|
||||||
|
|
||||||
request->send(response);
|
request->send(response);
|
||||||
}
|
}
|
||||||
|
@ -917,9 +918,7 @@ void onApiRestartDataSources(AsyncWebServerRequest *request)
|
||||||
request->beginResponseStream(JSON_CONTENT);
|
request->beginResponseStream(JSON_CONTENT);
|
||||||
|
|
||||||
restartPriceNotify();
|
restartPriceNotify();
|
||||||
restartBlockNotify();
|
BlockNotify::getInstance().restart();
|
||||||
// setupPriceNotify();
|
|
||||||
// setupBlockNotify();
|
|
||||||
|
|
||||||
request->send(response);
|
request->send(response);
|
||||||
}
|
}
|
||||||
|
|
20
src/main.cpp
20
src/main.cpp
|
@ -19,6 +19,7 @@
|
||||||
#include "ESPAsyncWebServer.h"
|
#include "ESPAsyncWebServer.h"
|
||||||
#include "lib/config.hpp"
|
#include "lib/config.hpp"
|
||||||
#include "lib/led_handler.hpp"
|
#include "lib/led_handler.hpp"
|
||||||
|
#include "lib/block_notify.hpp"
|
||||||
|
|
||||||
uint wifiLostConnection;
|
uint wifiLostConnection;
|
||||||
uint priceNotifyLostConnection = 0;
|
uint priceNotifyLostConnection = 0;
|
||||||
|
@ -49,7 +50,8 @@ void handleBlockNotifyDisconnection() {
|
||||||
|
|
||||||
if ((getUptime() - blockNotifyLostConnection) > 300) { // 5 minutes timeout
|
if ((getUptime() - blockNotifyLostConnection) > 300) { // 5 minutes timeout
|
||||||
Serial.println(F("Block notification connection lost for 5 minutes, restarting handler..."));
|
Serial.println(F("Block notification connection lost for 5 minutes, restarting handler..."));
|
||||||
restartBlockNotify();
|
auto& blockNotify = BlockNotify::getInstance();
|
||||||
|
blockNotify.restart();
|
||||||
blockNotifyLostConnection = 0;
|
blockNotifyLostConnection = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,13 +94,14 @@ void checkWiFiConnection() {
|
||||||
|
|
||||||
void checkMissedBlocks() {
|
void checkMissedBlocks() {
|
||||||
Serial.println(F("Long time (45 min) since last block, checking if I missed anything..."));
|
Serial.println(F("Long time (45 min) since last block, checking if I missed anything..."));
|
||||||
int currentBlock = getBlockFetch();
|
auto& blockNotify = BlockNotify::getInstance();
|
||||||
|
int currentBlock = blockNotify.fetchLatestBlock();
|
||||||
if (currentBlock != -1) {
|
if (currentBlock != -1) {
|
||||||
if (currentBlock != getBlockHeight()) {
|
if (currentBlock != blockNotify.getBlockHeight()) {
|
||||||
Serial.println(F("Detected stuck block height... restarting block handler."));
|
Serial.println(F("Detected stuck block height... restarting block handler."));
|
||||||
restartBlockNotify();
|
blockNotify.restart();
|
||||||
}
|
}
|
||||||
setLastBlockUpdate(getUptime());
|
blockNotify.setLastBlockUpdate(getUptime());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,9 +114,10 @@ void monitorDataConnections() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Block notification monitoring
|
// Block notification monitoring
|
||||||
if (getBlockNotifyInit() && !isBlockNotifyConnected()) {
|
auto& blockNotify = BlockNotify::getInstance();
|
||||||
|
if (blockNotify.isInitialized() && !blockNotify.isConnected()) {
|
||||||
handleBlockNotifyDisconnection();
|
handleBlockNotifyDisconnection();
|
||||||
} else if (blockNotifyLostConnection > 0 && isBlockNotifyConnected()) {
|
} else if (blockNotifyLostConnection > 0 && blockNotify.isConnected()) {
|
||||||
blockNotifyLostConnection = 0;
|
blockNotifyLostConnection = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,7 +129,7 @@ void monitorDataConnections() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for missed blocks
|
// Check for missed blocks
|
||||||
if ((getLastBlockUpdate() - getUptime()) > 45 * 60) {
|
if ((blockNotify.getLastBlockUpdate() - getUptime()) > 45 * 60) {
|
||||||
checkMissedBlocks();
|
checkMissedBlocks();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue