Nostr data source implementation
This commit is contained in:
parent
8e71f29d10
commit
87b22e5851
13 changed files with 236 additions and 51 deletions
2
data
2
data
|
@ -1 +1 @@
|
|||
Subproject commit 2363d98965bb1fdbfdf5d130b41732f5b864e2d0
|
||||
Subproject commit ee4d6d88c76fa279e643faabf4216c88145e0b2c
|
|
@ -41,6 +41,7 @@ lib_deps =
|
|||
https://github.com/dsbaars/universal_pin
|
||||
https://github.com/dsbaars/GxEPD2#universal_pin
|
||||
https://github.com/tzapu/WiFiManager.git#v2.0.17
|
||||
rblb/Nostrduino@^1.2.5
|
||||
|
||||
[env:lolin_s3_mini]
|
||||
extends = btclock_base
|
||||
|
|
|
@ -172,7 +172,25 @@ void onWebsocketBlockMessage(esp_websocket_event_data_t *event_data)
|
|||
return;
|
||||
}
|
||||
|
||||
currentBlockHeight = block["height"].as<uint>();
|
||||
processNewBlock(block["height"].as<uint>());
|
||||
}
|
||||
else if (doc.containsKey("mempool-blocks"))
|
||||
{
|
||||
JsonArray blockInfo = doc["mempool-blocks"].as<JsonArray>();
|
||||
|
||||
uint medianFee = (uint)round(blockInfo[0]["medianFee"].as<double>());
|
||||
|
||||
processNewBlockFee(medianFee);
|
||||
}
|
||||
|
||||
doc.clear();
|
||||
}
|
||||
|
||||
void processNewBlock(uint newBlockHeight) {
|
||||
if (newBlockHeight < currentBlockHeight)
|
||||
return;
|
||||
|
||||
currentBlockHeight = newBlockHeight;
|
||||
|
||||
// Serial.printf("New block found: %d\r\n", block["height"].as<uint>());
|
||||
preferences.putUInt("blockHeight", currentBlockHeight);
|
||||
|
@ -210,20 +228,15 @@ void onWebsocketBlockMessage(esp_websocket_event_data_t *event_data)
|
|||
}
|
||||
}
|
||||
}
|
||||
else if (doc.containsKey("mempool-blocks"))
|
||||
{
|
||||
JsonArray blockInfo = doc["mempool-blocks"].as<JsonArray>();
|
||||
|
||||
uint medianFee = (uint)round(blockInfo[0]["medianFee"].as<double>());
|
||||
|
||||
if (blockMedianFee == medianFee)
|
||||
void processNewBlockFee(uint newBlockFee) {
|
||||
if (blockMedianFee == newBlockFee)
|
||||
{
|
||||
doc.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// Serial.printf("New median fee: %d\r\n", medianFee);
|
||||
blockMedianFee = medianFee;
|
||||
blockMedianFee = newBlockFee;
|
||||
|
||||
if (workQueue != nullptr)
|
||||
{
|
||||
|
@ -232,9 +245,6 @@ void onWebsocketBlockMessage(esp_websocket_event_data_t *event_data)
|
|||
}
|
||||
}
|
||||
|
||||
doc.clear();
|
||||
}
|
||||
|
||||
uint getBlockHeight() { return currentBlockHeight; }
|
||||
|
||||
void setBlockHeight(uint newBlockHeight)
|
||||
|
|
|
@ -31,6 +31,9 @@ bool isBlockNotifyConnected();
|
|||
void stopBlockNotify();
|
||||
void restartBlockNotify();
|
||||
|
||||
void processNewBlock(uint newBlockHeight);
|
||||
void processNewBlockFee(uint newBlockFee);
|
||||
|
||||
bool getBlockNotifyInit();
|
||||
uint getLastBlockUpdate();
|
||||
int getBlockFetch();
|
||||
|
|
|
@ -48,7 +48,9 @@ void setup()
|
|||
{
|
||||
delay(1000);
|
||||
}
|
||||
} else if (mcp1.digitalRead(1) == LOW) {
|
||||
}
|
||||
else if (mcp1.digitalRead(1) == LOW)
|
||||
{
|
||||
preferences.clear();
|
||||
queueLedEffect(LED_EFFECT_WIFI_ERASE_SETTINGS);
|
||||
ESP.restart();
|
||||
|
@ -66,8 +68,16 @@ void setup()
|
|||
setupTasks();
|
||||
setupTimers();
|
||||
|
||||
if (preferences.getBool("useNostr", DEFAULT_USE_NOSTR))
|
||||
{
|
||||
setupNostrNotify();
|
||||
setupNostrTask();
|
||||
}
|
||||
else
|
||||
{
|
||||
xTaskCreate(setupWebsocketClients, "setupWebsocketClients", 8192, NULL,
|
||||
tskIDLE_PRIORITY, NULL);
|
||||
}
|
||||
|
||||
setupButtonTask();
|
||||
setupOTA();
|
||||
|
@ -404,7 +414,8 @@ void setupHardware()
|
|||
Wire.beginTransmission(0x5C);
|
||||
byte error = Wire.endTransmission();
|
||||
|
||||
if (error == 0) {
|
||||
if (error == 0)
|
||||
{
|
||||
Serial.println(F("Found BH1750"));
|
||||
hasLuxSensor = true;
|
||||
bh1750.begin(BH1750::CONTINUOUS_LOW_RES_MODE, 0x5C);
|
||||
|
@ -742,14 +753,15 @@ void setupFrontlight()
|
|||
{
|
||||
preferences.putBool("flFlashOnUpd", DEFAULT_FL_FLASH_ON_UPDATE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
float getLightLevel() {
|
||||
float getLightLevel()
|
||||
{
|
||||
return bh1750.readLightLevel();
|
||||
}
|
||||
|
||||
bool hasLightLevel() {
|
||||
bool hasLightLevel()
|
||||
{
|
||||
return hasLuxSensor;
|
||||
}
|
||||
#endif
|
||||
|
@ -784,5 +796,3 @@ String getFsRev()
|
|||
fsHash.close();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
#include "lib/improv.hpp"
|
||||
#include "lib/led_handler.hpp"
|
||||
#include "lib/ota.hpp"
|
||||
#include "lib/nostr_notify.hpp"
|
||||
|
||||
#include "lib/price_notify.hpp"
|
||||
#include "lib/screen_handler.hpp"
|
||||
#include "lib/shared.hpp"
|
||||
|
|
|
@ -22,6 +22,10 @@
|
|||
#define DEFAULT_HOSTNAME_PREFIX "btclock"
|
||||
#define DEFAULT_MEMPOOL_INSTANCE "mempool.space"
|
||||
|
||||
#define DEFAULT_USE_NOSTR false
|
||||
#define DEFAULT_NOSTR_NPUB "642317135fd4c4205323b9dea8af3270657e62d51dc31a657c0ec8aab31c6288"
|
||||
#define DEFAULT_NOSTR_RELAY "wss://nostr.dbtc.link"
|
||||
|
||||
#define DEFAULT_SECONDS_BETWEEN_PRICE_UPDATE 30
|
||||
#define DEFAULT_MINUTES_FULL_REFRESH 60
|
||||
|
||||
|
|
123
src/lib/nostr_notify.cpp
Normal file
123
src/lib/nostr_notify.cpp
Normal file
|
@ -0,0 +1,123 @@
|
|||
#include "nostr_notify.hpp"
|
||||
|
||||
std::vector<nostr::NostrPool *> pools;
|
||||
nostr::Transport *transport;
|
||||
TaskHandle_t nostrTaskHandle = NULL;
|
||||
|
||||
void setupNostrNotify()
|
||||
{
|
||||
nostr::esp32::ESP32Platform::initNostr(false);
|
||||
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
transport = nostr::esp32::ESP32Platform::getTransport();
|
||||
nostr::NostrPool *pool = new nostr::NostrPool(transport);
|
||||
String relay = preferences.getString("nostrRelay");
|
||||
String pubKey = preferences.getString("nostrPubKey");
|
||||
pools.push_back(pool);
|
||||
// Lets subscribe to the relay
|
||||
String subId = pool->subscribeMany(
|
||||
{relay},
|
||||
{{// we set the filters here (see
|
||||
// https://github.com/nostr-protocol/nips/blob/master/01.md#from-client-to-relay-sending-events-and-creating-subscriptions)
|
||||
{"kinds", {"1"}},
|
||||
{"authors", {pubKey}}}},
|
||||
[&](const String &subId, nostr::SignedNostrEvent *event)
|
||||
{
|
||||
// Received events callback, we can access the event content with
|
||||
// event->getContent() Here you should handle the event, for this
|
||||
// test we will just serialize it and print to console
|
||||
JsonDocument doc;
|
||||
JsonArray arr = doc["data"].to<JsonArray>();
|
||||
event->toSendableEvent(arr);
|
||||
// Access the second element which is the object
|
||||
JsonObject obj = arr[1].as<JsonObject>();
|
||||
|
||||
// Access the "tags" array
|
||||
JsonArray tags = obj["tags"].as<JsonArray>();
|
||||
|
||||
// Flag to check if the tag was found
|
||||
bool tagFound = false;
|
||||
uint medianFee = 0;
|
||||
String typeValue;
|
||||
|
||||
// Iterate over the tags array
|
||||
for (JsonArray tag : tags)
|
||||
{
|
||||
// Check if the tag is an array with two elements
|
||||
if (tag.size() == 2)
|
||||
{
|
||||
const char *key = tag[0];
|
||||
const char *value = tag[1];
|
||||
|
||||
// Check if the key is "type" and the value is "priceUsd"
|
||||
if (strcmp(key, "type") == 0 && (strcmp(value, "priceUsd") == 0 || strcmp(value, "blockHeight") == 0))
|
||||
{
|
||||
typeValue = value;
|
||||
tagFound = true;
|
||||
}
|
||||
else if (strcmp(key, "medianFee") == 0)
|
||||
{
|
||||
medianFee = tag[1].as<uint>();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (tagFound)
|
||||
{
|
||||
if (typeValue.equals("priceUsd"))
|
||||
{
|
||||
processNewPrice(obj["content"].as<uint>());
|
||||
}
|
||||
else if (typeValue.equals("blockHeight"))
|
||||
{
|
||||
processNewBlock(obj["content"].as<uint>());
|
||||
}
|
||||
|
||||
if (medianFee != 0)
|
||||
{
|
||||
processNewBlockFee(medianFee);
|
||||
}
|
||||
}
|
||||
},
|
||||
[&](const String &subId, const String &reason)
|
||||
{
|
||||
// This is the callback that will be called when the subscription is
|
||||
// closed
|
||||
Serial.println("Subscription closed: " + reason);
|
||||
},
|
||||
[&](const String &subId)
|
||||
{
|
||||
// This is the callback that will be called when the subscription is
|
||||
// EOSE
|
||||
Serial.println("Subscription EOSE: " + subId);
|
||||
});
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
Serial.println("Error: " + String(e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
void nostrTask(void *pvParameters)
|
||||
{
|
||||
int blockFetch = getBlockFetch();
|
||||
processNewBlock(blockFetch);
|
||||
|
||||
while (1)
|
||||
{
|
||||
for (nostr::NostrPool *pool : pools)
|
||||
{
|
||||
// Run internal loop: refresh relays, complete pending connections, send
|
||||
// pending messages
|
||||
pool->loop();
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
}
|
||||
}
|
||||
|
||||
void setupNostrTask()
|
||||
{
|
||||
xTaskCreate(nostrTask, "nostrTask", 16384, NULL, 10, &nostrTaskHandle);
|
||||
}
|
17
src/lib/nostr_notify.hpp
Normal file
17
src/lib/nostr_notify.hpp
Normal file
|
@ -0,0 +1,17 @@
|
|||
#pragma once
|
||||
|
||||
#include "shared.hpp"
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <esp32/ESP32Platform.h>
|
||||
#include <NostrEvent.h>
|
||||
#include <NostrPool.h>
|
||||
#include <Transport.h>
|
||||
#include <Utils.h>
|
||||
#include "price_notify.hpp"
|
||||
#include "block_notify.hpp"
|
||||
|
||||
void setupNostrNotify();
|
||||
|
||||
void nostrTask(void *pvParameters);
|
||||
void setupNostrTask();
|
|
@ -97,6 +97,13 @@ void onWebsocketPriceMessage(esp_websocket_event_data_t *event_data)
|
|||
if (doc.containsKey("bitcoin"))
|
||||
{
|
||||
if (currentPrice != doc["bitcoin"].as<long>())
|
||||
{
|
||||
processNewPrice(doc["bitcoin"].as<long>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void processNewPrice(uint newPrice)
|
||||
{
|
||||
uint minSecPriceUpd = preferences.getUInt(
|
||||
"minSecPriceUpd", DEFAULT_SECONDS_BETWEEN_PRICE_UPDATE);
|
||||
|
@ -106,8 +113,12 @@ void onWebsocketPriceMessage(esp_websocket_event_data_t *event_data)
|
|||
(currentTime - lastPriceUpdate) > minSecPriceUpd)
|
||||
{
|
||||
// const unsigned long oldPrice = currentPrice;
|
||||
currentPrice = doc["bitcoin"].as<uint>();
|
||||
currentPrice = newPrice;
|
||||
if (lastPriceUpdate == 0 ||
|
||||
(currentTime - lastPriceUpdate) > 120)
|
||||
{
|
||||
preferences.putUInt("lastPrice", currentPrice);
|
||||
}
|
||||
lastPriceUpdate = currentTime;
|
||||
// if (abs((int)(oldPrice-currentPrice)) > round(0.0015*oldPrice)) {
|
||||
if (workQueue != nullptr && (getCurrentScreen() == SCREEN_BTC_TICKER ||
|
||||
|
@ -120,8 +131,6 @@ void onWebsocketPriceMessage(esp_websocket_event_data_t *event_data)
|
|||
//}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint getLastPriceUpdate()
|
||||
{
|
||||
|
|
|
@ -17,6 +17,8 @@ void onWebsocketPriceMessage(esp_websocket_event_data_t *event_data);
|
|||
uint getPrice();
|
||||
void setPrice(uint newPrice);
|
||||
|
||||
void processNewPrice(uint newPrice);
|
||||
|
||||
bool isPriceNotifyConnected();
|
||||
void stopPriceNotify();
|
||||
void restartPriceNotify();
|
||||
|
|
|
@ -442,7 +442,7 @@ void onApiSettingsPatch(AsyncWebServerRequest *request, JsonVariant &json)
|
|||
settings["timePerScreen"].as<uint>() * 60);
|
||||
}
|
||||
|
||||
String strSettings[] = {"hostnamePrefix", "mempoolInstance"};
|
||||
String strSettings[] = {"hostnamePrefix", "mempoolInstance", "nostrPubKey", "nostrRelay"};
|
||||
|
||||
for (String setting : strSettings)
|
||||
{
|
||||
|
@ -477,7 +477,7 @@ void onApiSettingsPatch(AsyncWebServerRequest *request, JsonVariant &json)
|
|||
String boolSettings[] = {"fetchEurPrice", "ledTestOnPower", "ledFlashOnUpd",
|
||||
"mdnsEnabled", "otaEnabled", "stealFocus",
|
||||
"mcapBigChar", "useSatsSymbol", "useBlkCountdown",
|
||||
"suffixPrice", "disableLeds", "ownDataSource", "flAlwaysOn", "flFlashOnUpd", "mempoolSecure"};
|
||||
"suffixPrice", "disableLeds", "ownDataSource", "flAlwaysOn", "flFlashOnUpd", "mempoolSecure", "useNostr"};
|
||||
|
||||
for (String setting : boolSettings)
|
||||
{
|
||||
|
@ -577,6 +577,7 @@ void onApiSettingsGet(AsyncWebServerRequest *request)
|
|||
root["mempoolInstance"] =
|
||||
preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE);
|
||||
root["mempoolSecure"] = preferences.getBool("mempoolSecure", DEFAULT_MEMPOOL_SECURE);
|
||||
root["useNostr"] = preferences.getBool("useNostr", DEFAULT_USE_NOSTR);
|
||||
root["ledTestOnPower"] = preferences.getBool("ledTestOnPower", DEFAULT_LED_TEST_ON_POWER);
|
||||
root["ledFlashOnUpd"] = preferences.getBool("ledFlashOnUpd", DEFAULT_LED_FLASH_ON_UPD);
|
||||
root["ledBrightness"] = preferences.getUInt("ledBrightness", DEFAULT_LED_BRIGHTNESS);
|
||||
|
@ -596,6 +597,9 @@ void onApiSettingsGet(AsyncWebServerRequest *request)
|
|||
root["txPower"] = WiFi.getTxPower();
|
||||
root["ownDataSource"] = preferences.getBool("ownDataSource", DEFAULT_OWN_DATA_SOURCE);
|
||||
|
||||
root["nostrPubKey"] = preferences.getString("nostrPubKey", DEFAULT_NOSTR_NPUB);
|
||||
root["nostrRelay"] = preferences.getString("nostrRelay", DEFAULT_NOSTR_RELAY);
|
||||
|
||||
#ifdef HAS_FRONTLIGHT
|
||||
root["hasFrontlight"] = true;
|
||||
root["flMaxBrightness"] = preferences.getUInt("flMaxBrightness", DEFAULT_FL_MAX_BRIGHTNESS);
|
||||
|
|
Loading…
Reference in a new issue