btclock_v3/src/lib/block_notify.cpp

272 lines
7.5 KiB
C++
Raw Normal View History

2023-11-07 01:11:12 +01:00
#include "block_notify.hpp"
2023-11-07 21:26:15 +01:00
// Initialize static members
WebSocketsClient BlockNotify::webSocket;
uint32_t BlockNotify::currentBlockHeight = 878000;
uint16_t BlockNotify::blockMedianFee = 1;
bool BlockNotify::notifyInit = false;
unsigned long int BlockNotify::lastBlockUpdate = 0;
TaskHandle_t BlockNotify::taskHandle = nullptr;
2023-11-07 21:26:15 +01:00
void BlockNotify::taskNotify(void* pvParameters)
{
for (;;)
2024-03-30 11:40:58 +01:00
{
webSocket.loop();
vTaskDelay(10 / portTICK_PERIOD_MS);
2023-11-30 22:38:01 +01:00
}
}
2023-11-07 21:26:15 +01:00
void BlockNotify::setupTask()
2024-03-30 11:40:58 +01:00
{
xTaskCreate(taskNotify, "blockNotify", (6 * 1024), NULL, tskIDLE_PRIORITY,
&taskHandle);
}
2023-11-07 01:11:12 +01:00
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));
JsonDocument doc;
doc["action"] = "want";
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;
}
}
2023-11-30 22:38:01 +01:00
}
2023-11-08 12:18:59 +01:00
void BlockNotify::setup()
2024-03-30 11:40:58 +01:00
{
IPAddress result;
int dnsErr = -1;
String mempoolInstance = preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE);
2023-11-07 01:11:12 +01:00
while (dnsErr != 1 && !strchr(mempoolInstance.c_str(), ':'))
{
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));
}
}
// Get current block height through regular API
int blockFetch = fetchLatestBlock();
2023-11-07 01:11:12 +01:00
if (blockFetch > currentBlockHeight)
currentBlockHeight = blockFetch;
2023-11-07 01:11:12 +01:00
if (currentBlockHeight != -1)
{
lastBlockUpdate = esp_timer_get_time() / 1000000;
}
if (workQueue != nullptr)
{
WorkItem blockUpdate = {TASK_BLOCK_UPDATE, 0};
xQueueSend(workQueue, &blockUpdate, portMAX_DELAY);
}
const bool useSSL = preferences.getBool("mempoolSecure", DEFAULT_MEMPOOL_SECURE);
const int port = useSSL ? 443 : 80;
2024-07-11 22:08:42 +02:00
if (useSSL) {
webSocket.beginSSL(mempoolInstance.c_str(), port, "/api/v1/ws");
// webSocket.beginSSL("ws.btclock.dev", port, "/api/v1/ws");
2024-07-11 22:08:42 +02:00
} else {
webSocket.begin(mempoolInstance.c_str(), port, "/api/v1/ws");
}
webSocket.onEvent(onWebsocketEvent);
webSocket.setReconnectInterval(5000);
webSocket.enableHeartbeat(15000, 3000, 2);
2024-07-11 22:08:42 +02:00
setupTask();
2024-07-11 22:08:42 +02:00
}
void BlockNotify::processNewBlock(uint32_t newBlockHeight) {
2025-01-05 23:13:05 +01:00
if (newBlockHeight <= currentBlockHeight)
2025-01-05 22:11:53 +01:00
{
return;
}
2023-11-30 22:38:01 +01:00
2025-01-05 22:11:53 +01:00
currentBlockHeight = newBlockHeight;
2024-03-30 11:40:58 +01:00
lastBlockUpdate = esp_timer_get_time() / 1000000;
2023-11-30 22:38:01 +01:00
2024-03-30 11:40:58 +01:00
if (workQueue != nullptr)
{
2025-01-05 22:11:53 +01:00
WorkItem blockUpdate = {TASK_BLOCK_UPDATE, 0};
xQueueSend(workQueue, &blockUpdate, portMAX_DELAY);
}
2023-11-07 01:11:12 +01:00
2025-01-05 22:11:53 +01:00
if (ScreenHandler::getCurrentScreen() != SCREEN_BLOCK_HEIGHT &&
preferences.getBool("stealFocus", DEFAULT_STEAL_FOCUS))
{
2023-11-30 22:38:01 +01:00
uint64_t timerPeriod = 0;
2024-03-30 11:40:58 +01:00
if (isTimerActive())
{
2025-01-05 22:11:53 +01:00
timerPeriod = getTimerSeconds();
esp_timer_stop(screenRotateTimer);
2023-11-08 12:18:59 +01:00
}
ScreenHandler::setCurrentScreen(SCREEN_BLOCK_HEIGHT);
2024-03-30 11:40:58 +01:00
if (timerPeriod > 0)
{
2025-01-05 22:11:53 +01:00
esp_timer_start_periodic(screenRotateTimer,
2023-11-30 22:38:01 +01:00
timerPeriod * usPerSecond);
}
vTaskDelay(pdMS_TO_TICKS(315*NUM_SCREENS)); // Extra delay because of screen switching
2025-01-05 22:11:53 +01:00
}
2023-11-30 22:38:01 +01:00
2025-01-05 22:11:53 +01:00
if (preferences.getBool("ledFlashOnUpd", DEFAULT_LED_FLASH_ON_UPD))
{
2024-03-30 11:40:58 +01:00
vTaskDelay(pdMS_TO_TICKS(250)); // Wait until screens are updated
2025-01-05 21:19:28 +01:00
getLedHandler().queueEffect(LED_FLASH_BLOCK_NOTIFY);
}
2024-07-11 22:08:42 +02:00
}
void BlockNotify::processNewBlockFee(uint16_t newBlockFee) {
if (blockMedianFee == newBlockFee)
2024-03-30 11:40:58 +01:00
{
return;
2024-03-30 11:40:58 +01:00
}
2024-07-11 22:08:42 +02:00
blockMedianFee = newBlockFee;
2024-03-30 11:40:58 +01:00
if (workQueue != nullptr)
{
WorkItem blockUpdate = {TASK_FEE_UPDATE, 0};
xQueueSend(workQueue, &blockUpdate, portMAX_DELAY);
2024-03-30 11:40:58 +01:00
}
2023-11-07 01:11:12 +01:00
}
uint32_t BlockNotify::getBlockHeight() const {
return currentBlockHeight;
}
2023-11-08 12:18:59 +01:00
void BlockNotify::setBlockHeight(uint32_t newBlockHeight)
2024-03-30 11:40:58 +01:00
{
currentBlockHeight = newBlockHeight;
}
uint16_t BlockNotify::getBlockMedianFee() const {
return blockMedianFee;
}
void BlockNotify::setBlockMedianFee(uint16_t newBlockMedianFee)
2024-03-30 11:40:58 +01:00
{
blockMedianFee = newBlockMedianFee;
}
bool BlockNotify::isConnected() const
2024-03-30 11:40:58 +01:00
{
return webSocket.isConnected();
}
bool BlockNotify::isInitialized() const
2024-03-30 11:40:58 +01:00
{
return notifyInit;
2024-01-31 23:45:26 +01:00
}
void BlockNotify::stop()
2024-03-30 11:40:58 +01:00
{
webSocket.disconnect();
if (taskHandle != NULL) {
vTaskDelete(taskHandle);
taskHandle = NULL;
}
2024-03-30 11:40:58 +01:00
}
void BlockNotify::restart()
{
stop();
setup();
}
int BlockNotify::fetchLatestBlock() {
2024-12-20 23:02:54 +01:00
try {
String mempoolInstance = preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE);
const String protocol = preferences.getBool("mempoolSecure", DEFAULT_MEMPOOL_SECURE) ? "https" : "http";
String url = protocol + "://" + mempoolInstance + "/api/blocks/tip/height";
HTTPClient* http = HttpHelper::begin(url);
Serial.println("Fetching block height from " + url);
int httpCode = http->GET();
if (httpCode > 0 && httpCode == HTTP_CODE_OK) {
String blockHeightStr = http->getString();
HttpHelper::end(http);
return blockHeightStr.toInt();
}
HttpHelper::end(http);
Serial.println("HTTP code" + String(httpCode));
} catch (...) {
Serial.println(F("An exception occurred while trying to get the latest block"));
2024-06-28 17:36:46 +02:00
}
2024-12-20 23:02:54 +01:00
return 2203; // B-T-C
2024-03-30 11:40:58 +01:00
}
uint BlockNotify::getLastBlockUpdate() const
2024-03-30 11:40:58 +01:00
{
return lastBlockUpdate;
2024-03-30 11:40:58 +01:00
}
void BlockNotify::setLastBlockUpdate(uint lastUpdate)
2024-03-30 11:40:58 +01:00
{
lastBlockUpdate = lastUpdate;
2023-11-07 01:11:12 +01:00
}