Implement multi-currency support with MsgPack
This commit is contained in:
parent
a4ff5a2f75
commit
c276d32807
18 changed files with 528 additions and 541 deletions
2
data
2
data
|
@ -1 +1 @@
|
|||
Subproject commit 34b09a2d1134d48d7733a3d11a9e6f3f15d080a9
|
||||
Subproject commit 2fffb3ef0284b4262ca97a81eb979259186604e5
|
|
@ -32,25 +32,41 @@ std::string getCurrencyCode(char input)
|
|||
switch (input)
|
||||
{
|
||||
case CURRENCY_EUR:
|
||||
return "EUR";
|
||||
return CURRENCY_CODE_EUR;
|
||||
break;
|
||||
case CURRENCY_GBP:
|
||||
return "GBP";
|
||||
return CURRENCY_CODE_GBP;
|
||||
break;
|
||||
case CURRENCY_JPY:
|
||||
return "YEN";
|
||||
return CURRENCY_CODE_JPY;
|
||||
break;
|
||||
case CURRENCY_AUD:
|
||||
return "AUD";
|
||||
return CURRENCY_CODE_AUD;
|
||||
break;
|
||||
case CURRENCY_CAD:
|
||||
return "CAD";
|
||||
return CURRENCY_CODE_CAD;
|
||||
break;
|
||||
default:
|
||||
return "USD";
|
||||
return CURRENCY_CODE_USD;
|
||||
}
|
||||
}
|
||||
|
||||
char getCurrencyChar(const std::string& input)
|
||||
{
|
||||
if (input == "EUR")
|
||||
return CURRENCY_EUR;
|
||||
else if (input == "GBP")
|
||||
return CURRENCY_GBP;
|
||||
else if (input == "JPY")
|
||||
return CURRENCY_JPY;
|
||||
else if (input == "AUD")
|
||||
return CURRENCY_AUD;
|
||||
else if (input == "CAD")
|
||||
return CURRENCY_CAD;
|
||||
else
|
||||
return CURRENCY_USD; // Assuming USD is the default for unknown inputs
|
||||
}
|
||||
|
||||
std::array<std::string, NUM_SCREENS> parsePriceData(std::uint32_t price, char currencySymbol, bool useSuffixFormat)
|
||||
{
|
||||
std::array<std::string, NUM_SCREENS> ret;
|
||||
|
@ -205,14 +221,8 @@ std::array<std::string, NUM_SCREENS> parseMarketCap(std::uint32_t blockHeight, s
|
|||
std::uint32_t firstIndex = 0;
|
||||
double supply = getSupplyAtBlock(blockHeight);
|
||||
int64_t marketCap = static_cast<std::int64_t>(supply * double(price));
|
||||
if (currencySymbol == '[')
|
||||
{
|
||||
ret[0] = "EUR/MCAP";
|
||||
}
|
||||
else
|
||||
{
|
||||
ret[0] = "USD/MCAP";
|
||||
}
|
||||
|
||||
ret[0] = getCurrencyCode(currencySymbol) + "/MCAP";
|
||||
|
||||
if (bigChars)
|
||||
{
|
||||
|
|
|
@ -12,6 +12,13 @@ const char CURRENCY_JPY = '^';
|
|||
const char CURRENCY_AUD = '_';
|
||||
const char CURRENCY_CAD = '`';
|
||||
|
||||
const std::string CURRENCY_CODE_USD = "USD";
|
||||
const std::string CURRENCY_CODE_EUR = "EUR";
|
||||
const std::string CURRENCY_CODE_GBP = "GBP";
|
||||
const std::string CURRENCY_CODE_JPY = "JPY";
|
||||
const std::string CURRENCY_CODE_AUD = "AUD";
|
||||
const std::string CURRENCY_CODE_CAD = "CAD";
|
||||
|
||||
std::array<std::string, NUM_SCREENS> parsePriceData(std::uint32_t price, char currency, bool useSuffixFormat = false);
|
||||
std::array<std::string, NUM_SCREENS> parseSatsPerCurrency(std::uint32_t price, char currencySymbol, bool withSatsSymbol);
|
||||
std::array<std::string, NUM_SCREENS> parseBlockHeight(std::uint32_t blockHeight);
|
||||
|
@ -21,3 +28,4 @@ std::array<std::string, NUM_SCREENS> parseBlockFees(std::uint16_t blockFees);
|
|||
|
||||
char getCurrencySymbol(char input);
|
||||
std::string getCurrencyCode(char input);
|
||||
char getCurrencyChar(const std::string& input);
|
|
@ -66,7 +66,6 @@ void setup()
|
|||
|
||||
setupWebserver();
|
||||
|
||||
// setupWifi();
|
||||
syncTime();
|
||||
finishSetup();
|
||||
|
||||
|
@ -268,24 +267,43 @@ void setupPreferences()
|
|||
setFgColor(preferences.getUInt("fgColor", DEFAULT_FG_COLOR));
|
||||
setBgColor(preferences.getUInt("bgColor", DEFAULT_BG_COLOR));
|
||||
setBlockHeight(preferences.getUInt("blockHeight", INITIAL_BLOCK_HEIGHT));
|
||||
setPrice(preferences.getUInt("lastPrice", INITIAL_LAST_PRICE));
|
||||
setPrice(preferences.getUInt("lastPrice", INITIAL_LAST_PRICE), CURRENCY_USD);
|
||||
|
||||
if (preferences.getBool("ownDataSource", DEFAULT_OWN_DATA_SOURCE))
|
||||
setCurrentCurrency(preferences.getUChar("lastCurrency", CURRENCY_USD));
|
||||
else
|
||||
setCurrentCurrency(CURRENCY_USD);
|
||||
|
||||
|
||||
addScreenMapping(SCREEN_BLOCK_HEIGHT, "Block Height");
|
||||
addScreenMapping(SCREEN_MSCW_TIME, "Sats per dollar");
|
||||
addScreenMapping(SCREEN_BTC_TICKER, "Ticker");
|
||||
|
||||
addScreenMapping(SCREEN_TIME, "Time");
|
||||
addScreenMapping(SCREEN_HALVING_COUNTDOWN, "Halving countdown");
|
||||
addScreenMapping(SCREEN_MARKET_CAP, "Market Cap");
|
||||
addScreenMapping(SCREEN_BLOCK_FEE_RATE, "Block Fee Rate");
|
||||
|
||||
addScreenMapping(SCREEN_SATS_PER_CURRENCY, "Sats per dollar");
|
||||
addScreenMapping(SCREEN_BTC_TICKER, "Ticker");
|
||||
addScreenMapping(SCREEN_MARKET_CAP, "Market Cap");
|
||||
|
||||
|
||||
// addScreenMapping(SCREEN_SATS_PER_CURRENCY_USD, "Sats per USD");
|
||||
// addScreenMapping(SCREEN_BTC_TICKER_USD, "Ticker USD");
|
||||
// addScreenMapping(SCREEN_MARKET_CAP_USD, "Market Cap USD");
|
||||
|
||||
// addScreenMapping(SCREEN_SATS_PER_CURRENCY_EUR, "Sats per EUR");
|
||||
// addScreenMapping(SCREEN_BTC_TICKER_EUR, "Ticker EUR");
|
||||
// addScreenMapping(SCREEN_MARKET_CAP_EUR, "Market Cap EUR");
|
||||
|
||||
// screenNameMap[SCREEN_BLOCK_HEIGHT] = "Block Height";
|
||||
// screenNameMap[SCREEN_BLOCK_FEE_RATE] = "Block Fee Rate";
|
||||
// screenNameMap[SCREEN_MSCW_TIME] = "Sats per dollar";
|
||||
// screenNameMap[SCREEN_SATS_PER_CURRENCY] = "Sats per dollar";
|
||||
// screenNameMap[SCREEN_BTC_TICKER] = "Ticker";
|
||||
// screenNameMap[SCREEN_TIME] = "Time";
|
||||
// screenNameMap[SCREEN_HALVING_COUNTDOWN] = "Halving countdown";
|
||||
// screenNameMap[SCREEN_MARKET_CAP] = "Market Cap";
|
||||
|
||||
//addCurrencyMappings(getActiveCurrencies());
|
||||
|
||||
if (preferences.getBool("bitaxeEnabled", DEFAULT_BITAXE_ENABLED))
|
||||
{
|
||||
addScreenMapping(SCREEN_BITAXE_HASHRATE, "BitAxe Hashrate");
|
||||
|
@ -293,16 +311,74 @@ void setupPreferences()
|
|||
}
|
||||
}
|
||||
|
||||
// void addCurrencyMappings(const std::vector<std::string>& currencies)
|
||||
// {
|
||||
// for (const auto& currency : currencies)
|
||||
// {
|
||||
// int satsPerCurrencyScreen;
|
||||
// int btcTickerScreen;
|
||||
// int marketCapScreen;
|
||||
|
||||
// // Determine the corresponding screen IDs based on the currency code
|
||||
// if (currency == "USD")
|
||||
// {
|
||||
// satsPerCurrencyScreen = SCREEN_SATS_PER_CURRENCY_USD;
|
||||
// btcTickerScreen = SCREEN_BTC_TICKER_USD;
|
||||
// marketCapScreen = SCREEN_MARKET_CAP_USD;
|
||||
// }
|
||||
// else if (currency == "EUR")
|
||||
// {
|
||||
// satsPerCurrencyScreen = SCREEN_SATS_PER_CURRENCY_EUR;
|
||||
// btcTickerScreen = SCREEN_BTC_TICKER_EUR;
|
||||
// marketCapScreen = SCREEN_MARKET_CAP_EUR;
|
||||
// }
|
||||
// else if (currency == "GBP")
|
||||
// {
|
||||
// satsPerCurrencyScreen = SCREEN_SATS_PER_CURRENCY_GBP;
|
||||
// btcTickerScreen = SCREEN_BTC_TICKER_GBP;
|
||||
// marketCapScreen = SCREEN_MARKET_CAP_GBP;
|
||||
// }
|
||||
// else if (currency == "JPY")
|
||||
// {
|
||||
// satsPerCurrencyScreen = SCREEN_SATS_PER_CURRENCY_JPY;
|
||||
// btcTickerScreen = SCREEN_BTC_TICKER_JPY;
|
||||
// marketCapScreen = SCREEN_MARKET_CAP_JPY;
|
||||
// }
|
||||
// else if (currency == "AUD")
|
||||
// {
|
||||
// satsPerCurrencyScreen = SCREEN_SATS_PER_CURRENCY_AUD;
|
||||
// btcTickerScreen = SCREEN_BTC_TICKER_AUD;
|
||||
// marketCapScreen = SCREEN_MARKET_CAP_AUD;
|
||||
// }
|
||||
// else if (currency == "CAD")
|
||||
// {
|
||||
// satsPerCurrencyScreen = SCREEN_SATS_PER_CURRENCY_CAD;
|
||||
// btcTickerScreen = SCREEN_BTC_TICKER_CAD;
|
||||
// marketCapScreen = SCREEN_MARKET_CAP_CAD;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// continue; // Unknown currency, skip it
|
||||
// }
|
||||
|
||||
// // Create the string locally to ensure it persists
|
||||
// std::string satsPerCurrencyString = "Sats per " + currency;
|
||||
// std::string btcTickerString = "Ticker " + currency;
|
||||
// std::string marketCapString = "Market Cap " + currency;
|
||||
|
||||
// // Pass the c_str() to the function
|
||||
// addScreenMapping(satsPerCurrencyScreen, satsPerCurrencyString.c_str());
|
||||
// addScreenMapping(btcTickerScreen, btcTickerString.c_str());
|
||||
// addScreenMapping(marketCapScreen, marketCapString.c_str());
|
||||
// }
|
||||
// }
|
||||
|
||||
void setupWebsocketClients(void *pvParameters)
|
||||
{
|
||||
setupBlockNotify();
|
||||
|
||||
if (preferences.getBool("fetchEurPrice", DEFAULT_FETCH_EUR_PRICE))
|
||||
{
|
||||
setupPriceFetchTask();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (preferences.getBool("ownDataSource", DEFAULT_OWN_DATA_SOURCE)) {
|
||||
setupV2Notify();
|
||||
} else {
|
||||
setupBlockNotify();
|
||||
setupPriceNotify();
|
||||
}
|
||||
|
||||
|
@ -453,218 +529,6 @@ void setupHardware()
|
|||
#endif
|
||||
}
|
||||
|
||||
|
||||
#ifdef IMPROV_ENABLED
|
||||
void improvGetAvailableWifiNetworks()
|
||||
{
|
||||
int networkNum = WiFi.scanNetworks();
|
||||
|
||||
for (int id = 0; id < networkNum; ++id)
|
||||
{
|
||||
std::vector<uint8_t> data = improv::build_rpc_response(
|
||||
improv::GET_WIFI_NETWORKS,
|
||||
{WiFi.SSID(id), String(WiFi.RSSI(id)),
|
||||
(WiFi.encryptionType(id) == WIFI_AUTH_OPEN ? "NO" : "YES")},
|
||||
false);
|
||||
improv_send_response(data);
|
||||
}
|
||||
// final response
|
||||
std::vector<uint8_t> data = improv::build_rpc_response(
|
||||
improv::GET_WIFI_NETWORKS, std::vector<std::string>{}, false);
|
||||
improv_send_response(data);
|
||||
}
|
||||
|
||||
bool improv_connectWifi(std::string ssid, std::string password)
|
||||
{
|
||||
uint8_t count = 0;
|
||||
|
||||
WiFi.begin(ssid.c_str(), password.c_str());
|
||||
|
||||
while (WiFi.status() != WL_CONNECTED)
|
||||
{
|
||||
blinkDelay(500, 2);
|
||||
|
||||
if (count > MAX_ATTEMPTS_WIFI_CONNECTION)
|
||||
{
|
||||
WiFi.disconnect();
|
||||
return false;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void onImprovErrorCallback(improv::Error err)
|
||||
{
|
||||
blinkDelayColor(100, 1, 255, 0, 0);
|
||||
// pixels.setPixelColor(0, pixels.Color(255, 0, 0));
|
||||
// pixels.setPixelColor(1, pixels.Color(255, 0, 0));
|
||||
// pixels.setPixelColor(2, pixels.Color(255, 0, 0));
|
||||
// pixels.setPixelColor(3, pixels.Color(255, 0, 0));
|
||||
// pixels.show();
|
||||
// vTaskDelay(pdMS_TO_TICKS(100));
|
||||
|
||||
// pixels.clear();
|
||||
// pixels.show();
|
||||
// vTaskDelay(pdMS_TO_TICKS(100));
|
||||
}
|
||||
|
||||
std::vector<std::string> getLocalUrl()
|
||||
{
|
||||
return {// URL where user can finish onboarding or use device
|
||||
// Recommended to use website hosted by device
|
||||
String("http://" + WiFi.localIP().toString()).c_str()};
|
||||
}
|
||||
|
||||
bool onImprovCommandCallback(improv::ImprovCommand cmd)
|
||||
{
|
||||
switch (cmd.command)
|
||||
{
|
||||
case improv::Command::GET_CURRENT_STATE:
|
||||
{
|
||||
if ((WiFi.status() == WL_CONNECTED))
|
||||
{
|
||||
improv_set_state(improv::State::STATE_PROVISIONED);
|
||||
std::vector<uint8_t> data = improv::build_rpc_response(
|
||||
improv::GET_CURRENT_STATE, getLocalUrl(), false);
|
||||
improv_send_response(data);
|
||||
}
|
||||
else
|
||||
{
|
||||
improv_set_state(improv::State::STATE_AUTHORIZED);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case improv::Command::WIFI_SETTINGS:
|
||||
{
|
||||
if (cmd.ssid.length() == 0)
|
||||
{
|
||||
improv_set_error(improv::Error::ERROR_INVALID_RPC);
|
||||
break;
|
||||
}
|
||||
|
||||
improv_set_state(improv::STATE_PROVISIONING);
|
||||
queueLedEffect(LED_EFFECT_WIFI_CONNECTING);
|
||||
|
||||
if (improv_connectWifi(cmd.ssid, cmd.password))
|
||||
{
|
||||
queueLedEffect(LED_EFFECT_WIFI_CONNECT_SUCCESS);
|
||||
|
||||
// std::array<String, NUM_SCREENS> epdContent = {"S", "U", "C", "C",
|
||||
// "E", "S", "S"}; setEpdContent(epdContent);
|
||||
|
||||
preferences.putBool("wifiConfigured", true);
|
||||
|
||||
improv_set_state(improv::STATE_PROVISIONED);
|
||||
std::vector<uint8_t> data = improv::build_rpc_response(
|
||||
improv::WIFI_SETTINGS, getLocalUrl(), false);
|
||||
improv_send_response(data);
|
||||
|
||||
delay(2500);
|
||||
ESP.restart();
|
||||
setupWebserver();
|
||||
}
|
||||
else
|
||||
{
|
||||
queueLedEffect(LED_EFFECT_WIFI_CONNECT_ERROR);
|
||||
|
||||
improv_set_state(improv::STATE_STOPPED);
|
||||
improv_set_error(improv::Error::ERROR_UNABLE_TO_CONNECT);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case improv::Command::GET_DEVICE_INFO:
|
||||
{
|
||||
std::vector<std::string> infos = {// Firmware name
|
||||
"BTClock",
|
||||
// Firmware version
|
||||
"1.0.0",
|
||||
// Hardware chip/variant
|
||||
"ESP32S3",
|
||||
// Device name
|
||||
"BTClock"};
|
||||
std::vector<uint8_t> data =
|
||||
improv::build_rpc_response(improv::GET_DEVICE_INFO, infos, false);
|
||||
improv_send_response(data);
|
||||
break;
|
||||
}
|
||||
|
||||
case improv::Command::GET_WIFI_NETWORKS:
|
||||
{
|
||||
improvGetAvailableWifiNetworks();
|
||||
// std::array<String, NUM_SCREENS> epdContent = {"W", "E", "B", "W", "I",
|
||||
// "F", "I"}; setEpdContent(epdContent);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
improv_set_error(improv::ERROR_UNKNOWN_RPC);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void improv_set_state(improv::State state)
|
||||
{
|
||||
std::vector<uint8_t> data = {'I', 'M', 'P', 'R', 'O', 'V'};
|
||||
data.resize(11);
|
||||
data[6] = improv::IMPROV_SERIAL_VERSION;
|
||||
data[7] = improv::TYPE_CURRENT_STATE;
|
||||
data[8] = 1;
|
||||
data[9] = state;
|
||||
|
||||
uint8_t checksum = 0x00;
|
||||
for (uint8_t d : data)
|
||||
checksum += d;
|
||||
data[10] = checksum;
|
||||
|
||||
Serial.write(data.data(), data.size());
|
||||
}
|
||||
|
||||
void improv_send_response(std::vector<uint8_t> &response)
|
||||
{
|
||||
std::vector<uint8_t> data = {'I', 'M', 'P', 'R', 'O', 'V'};
|
||||
data.resize(9);
|
||||
data[6] = improv::IMPROV_SERIAL_VERSION;
|
||||
data[7] = improv::TYPE_RPC_RESPONSE;
|
||||
data[8] = response.size();
|
||||
data.insert(data.end(), response.begin(), response.end());
|
||||
|
||||
uint8_t checksum = 0x00;
|
||||
for (uint8_t d : data)
|
||||
checksum += d;
|
||||
data.push_back(checksum);
|
||||
|
||||
Serial.write(data.data(), data.size());
|
||||
}
|
||||
|
||||
void improv_set_error(improv::Error error)
|
||||
{
|
||||
std::vector<uint8_t> data = {'I', 'M', 'P', 'R', 'O', 'V'};
|
||||
data.resize(11);
|
||||
data[6] = improv::IMPROV_SERIAL_VERSION;
|
||||
data[7] = improv::TYPE_ERROR_STATE;
|
||||
data[8] = 1;
|
||||
data[9] = error;
|
||||
|
||||
uint8_t checksum = 0x00;
|
||||
for (uint8_t d : data)
|
||||
checksum += d;
|
||||
data[10] = checksum;
|
||||
|
||||
Serial.write(data.data(), data.size());
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info)
|
||||
{
|
||||
static bool first_connect = true;
|
||||
|
@ -842,3 +706,37 @@ int findScreenIndexByValue(int value)
|
|||
}
|
||||
return -1; // Return -1 if value is not found
|
||||
}
|
||||
|
||||
std::vector<std::string> getAvailableCurrencies()
|
||||
{
|
||||
return {CURRENCY_CODE_USD, CURRENCY_CODE_EUR, CURRENCY_CODE_GBP, CURRENCY_CODE_JPY, CURRENCY_CODE_AUD, CURRENCY_CODE_CAD};
|
||||
}
|
||||
|
||||
std::vector<std::string> getActiveCurrencies()
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
|
||||
// Convert Arduino String to std::string
|
||||
std::string stdString = preferences.getString("actCurrencies", DEFAULT_ACTIVE_CURRENCIES).c_str();
|
||||
|
||||
// Use a stringstream to split the string
|
||||
std::stringstream ss(stdString);
|
||||
std::string item;
|
||||
|
||||
// Split the string by comma and add each part to the vector
|
||||
while (std::getline(ss, item, ','))
|
||||
{
|
||||
result.push_back(item);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool isActiveCurrency(std::string ¤cy)
|
||||
{
|
||||
std::vector<std::string> ac = getActiveCurrencies();
|
||||
if (std::find(ac.begin(), ac.end(), currency) != ac.end())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
|
@ -19,6 +19,8 @@
|
|||
#include "lib/nostr_notify.hpp"
|
||||
#include "lib/bitaxe_fetch.hpp"
|
||||
|
||||
#include "lib/v2_notify.hpp"
|
||||
|
||||
#include "lib/price_notify.hpp"
|
||||
#include "lib/screen_handler.hpp"
|
||||
#include "lib/shared.hpp"
|
||||
|
@ -64,6 +66,11 @@ std::vector<std::string> getLocalUrl();
|
|||
// void improv_set_state(improv::State state);
|
||||
// void improv_send_response(std::vector<uint8_t> &response);
|
||||
// void improv_set_error(improv::Error error);
|
||||
//void addCurrencyMappings(const std::vector<std::string>& currencies);
|
||||
std::vector<std::string> getActiveCurrencies();
|
||||
std::vector<std::string> getAvailableCurrencies();
|
||||
|
||||
bool isActiveCurrency(std::string ¤cy);
|
||||
|
||||
void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info);
|
||||
String getHwRev();
|
||||
|
@ -71,4 +78,7 @@ bool isWhiteVersion();
|
|||
String getFsRev();
|
||||
|
||||
void addScreenMapping(int value, const char* name);
|
||||
// void addScreenMapping(int value, const String& name);
|
||||
// void addScreenMapping(int value, const std::string& name);
|
||||
|
||||
int findScreenIndexByValue(int value);
|
|
@ -59,3 +59,5 @@
|
|||
#define DEFAULT_HTTP_AUTH_ENABLED false
|
||||
#define DEFAULT_HTTP_AUTH_USERNAME "btclock"
|
||||
#define DEFAULT_HTTP_AUTH_PASSWORD "satoshi"
|
||||
|
||||
#define DEFAULT_ACTIVE_CURRENCIES "USD,EUR,JPY"
|
|
@ -168,7 +168,7 @@ void handleNostrEventCallback(const String &subId, nostr::SignedNostrEvent *even
|
|||
{
|
||||
if (typeValue.equals("priceUsd"))
|
||||
{
|
||||
processNewPrice(obj["content"].as<uint>());
|
||||
processNewPrice(obj["content"].as<uint>(), CURRENCY_USD);
|
||||
}
|
||||
else if (typeValue.equals("blockHeight"))
|
||||
{
|
||||
|
|
|
@ -29,9 +29,9 @@ void taskPriceFetch(void *pvParameters) {
|
|||
// usdPrice = doc["bitcoin"]["usd"];
|
||||
eurPrice = doc["bitcoin"]["eur"].as<uint>();
|
||||
|
||||
setPrice(eurPrice);
|
||||
setPrice(eurPrice, CURRENCY_EUR);
|
||||
if (workQueue != nullptr && (getCurrentScreen() == SCREEN_BTC_TICKER ||
|
||||
getCurrentScreen() == SCREEN_MSCW_TIME ||
|
||||
getCurrentScreen() == SCREEN_SATS_PER_CURRENCY ||
|
||||
getCurrentScreen() == SCREEN_MARKET_CAP)) {
|
||||
WorkItem priceUpdate = {TASK_PRICE_UPDATE, 0};
|
||||
xQueueSend(workQueue, &priceUpdate, portMAX_DELAY);
|
||||
|
|
|
@ -5,36 +5,6 @@ const char *wsOwnServerV2 = "wss://ws-staging.btclock.dev/api/v2/ws";
|
|||
|
||||
const char *wsServerPrice = "wss://ws.coincap.io/prices?assets=bitcoin";
|
||||
|
||||
// const char* coinCapWsCert = R"(-----BEGIN CERTIFICATE-----
|
||||
// MIIFMjCCBNmgAwIBAgIQBtgXvFyc28MsvQ1HjCnXJTAKBggqhkjOPQQDAjBKMQsw
|
||||
// CQYDVQQGEwJVUzEZMBcGA1UEChMQQ2xvdWRmbGFyZSwgSW5jLjEgMB4GA1UEAxMX
|
||||
// Q2xvdWRmbGFyZSBJbmMgRUNDIENBLTMwHhcNMjMwNTEwMDAwMDAwWhcNMjQwNTA5
|
||||
// MjM1OTU5WjB1MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQG
|
||||
// A1UEBxMNU2FuIEZyYW5jaXNjbzEZMBcGA1UEChMQQ2xvdWRmbGFyZSwgSW5jLjEe
|
||||
// MBwGA1UEAxMVc25pLmNsb3VkZmxhcmVzc2wuY29tMFkwEwYHKoZIzj0CAQYIKoZI
|
||||
// zj0DAQcDQgAEpvFIXzQKHuqTo+IE6c6sB4p0PMXK1KsseEGf2UN/CNRhG5hO7lr8
|
||||
// JtXrPZkawWBysZxOsEoetkPrDHMugCLfXKOCA3QwggNwMB8GA1UdIwQYMBaAFKXO
|
||||
// N+rrsHUOlGeItEX62SQQh5YfMB0GA1UdDgQWBBShsZDJohaR1a5E0Qj7yblZjKDC
|
||||
// gDA6BgNVHREEMzAxggwqLmNvaW5jYXAuaW+CCmNvaW5jYXAuaW+CFXNuaS5jbG91
|
||||
// ZGZsYXJlc3NsLmNvbTAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0lBBYwFAYIKwYBBQUH
|
||||
// AwEGCCsGAQUFBwMCMHsGA1UdHwR0MHIwN6A1oDOGMWh0dHA6Ly9jcmwzLmRpZ2lj
|
||||
// ZXJ0LmNvbS9DbG91ZGZsYXJlSW5jRUNDQ0EtMy5jcmwwN6A1oDOGMWh0dHA6Ly9j
|
||||
// cmw0LmRpZ2ljZXJ0LmNvbS9DbG91ZGZsYXJlSW5jRUNDQ0EtMy5jcmwwPgYDVR0g
|
||||
// BDcwNTAzBgZngQwBAgIwKTAnBggrBgEFBQcCARYbaHR0cDovL3d3dy5kaWdpY2Vy
|
||||
// dC5jb20vQ1BTMHYGCCsGAQUFBwEBBGowaDAkBggrBgEFBQcwAYYYaHR0cDovL29j
|
||||
// c3AuZGlnaWNlcnQuY29tMEAGCCsGAQUFBzAChjRodHRwOi8vY2FjZXJ0cy5kaWdp
|
||||
// Y2VydC5jb20vQ2xvdWRmbGFyZUluY0VDQ0NBLTMuY3J0MAwGA1UdEwEB/wQCMAAw
|
||||
// ggF+BgorBgEEAdZ5AgQCBIIBbgSCAWoBaAB1AO7N0GTV2xrOxVy3nbTNE6Iyh0Z8
|
||||
// vOzew1FIWUZxH7WbAAABiAPnoRAAAAQDAEYwRAIgAP2W09OozuhmKeKKMsaVBcae
|
||||
// o+nPHF1WUWk0i387YYYCIDIM1Wll7/4O3GNx2/Fx9bC6pi69Uya4pLxsCfW3fZMe
|
||||
// AHYASLDja9qmRzQP5WoC+p0w6xxSActW3SyB2bu/qznYhHMAAAGIA+eg+QAABAMA
|
||||
// RzBFAiEAuNpSqrbx47gYBgBMz5M6q0CnV/WMJqWQOxYFKrwfwVACIH3nCs4bKToT
|
||||
// e+MiBrqSDaekixk4kPFEQESO9qHCkWY5AHcA2ra/az+1tiKfm8K7XGvocJFxbLtR
|
||||
// hIU0vaQ9MEjX+6sAAAGIA+eg1gAABAMASDBGAiEAolCFl2IfbOHUPAOxoi4BLclS
|
||||
// v9FVXb7LwIvTuCfyrEQCIQDcvehwhV9XGopKGl17F2LYYKI7hvlO3RmpPZQJt1da
|
||||
// MDAKBggqhkjOPQQDAgNHADBEAiAXRWZ/JVMsfpSFFTHQHUSqRnQ/7cCOWx+9svIy
|
||||
// mYnFZQIgHMEG0Cm7O4cn5KUzKOsTwwK+2U15s/jPUQi2n2IDTEM=
|
||||
// -----END CERTIFICATE-----)";
|
||||
|
||||
// WebsocketsClient client;
|
||||
esp_websocket_client_handle_t clientPrice = NULL;
|
||||
|
@ -42,6 +12,8 @@ esp_websocket_client_config_t config;
|
|||
uint currentPrice = 50000;
|
||||
unsigned long int lastPriceUpdate;
|
||||
bool priceNotifyInit = false;
|
||||
std::map<char, std::uint64_t> currencyMap;
|
||||
std::map<char, unsigned long int> lastUpdateMap;
|
||||
|
||||
void setupPriceNotify()
|
||||
{
|
||||
|
@ -100,48 +72,59 @@ void onWebsocketPriceMessage(esp_websocket_event_data_t *event_data)
|
|||
{
|
||||
if (currentPrice != doc["bitcoin"].as<long>())
|
||||
{
|
||||
processNewPrice(doc["bitcoin"].as<long>());
|
||||
processNewPrice(doc["bitcoin"].as<long>(), CURRENCY_USD);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void processNewPrice(uint newPrice)
|
||||
void processNewPrice(uint newPrice, char currency)
|
||||
{
|
||||
uint minSecPriceUpd = preferences.getUInt(
|
||||
"minSecPriceUpd", DEFAULT_SECONDS_BETWEEN_PRICE_UPDATE);
|
||||
uint currentTime = esp_timer_get_time() / 1000000;
|
||||
|
||||
if (lastPriceUpdate == 0 ||
|
||||
(currentTime - lastPriceUpdate) > minSecPriceUpd)
|
||||
if (lastUpdateMap.find(currency) == lastUpdateMap.end()||
|
||||
(currentTime - lastUpdateMap[currency]) > minSecPriceUpd)
|
||||
{
|
||||
// const unsigned long oldPrice = currentPrice;
|
||||
currentPrice = newPrice;
|
||||
if (lastPriceUpdate == 0 ||
|
||||
(currentTime - lastPriceUpdate) > 120)
|
||||
{
|
||||
preferences.putUInt("lastPrice", currentPrice);
|
||||
}
|
||||
lastPriceUpdate = currentTime;
|
||||
currencyMap[currency] = newPrice;
|
||||
// if (lastUpdateMap[currency] == 0 ||
|
||||
// (currentTime - lastUpdateMap[currency]) > 120)
|
||||
// {
|
||||
// preferences.putUInt("lastPrice", currentPrice);
|
||||
// }
|
||||
lastUpdateMap[currency] = currentTime;
|
||||
// if (abs((int)(oldPrice-currentPrice)) > round(0.0015*oldPrice)) {
|
||||
if (workQueue != nullptr && (getCurrentScreen() == SCREEN_BTC_TICKER ||
|
||||
getCurrentScreen() == SCREEN_MSCW_TIME ||
|
||||
getCurrentScreen() == SCREEN_SATS_PER_CURRENCY ||
|
||||
getCurrentScreen() == SCREEN_MARKET_CAP))
|
||||
{
|
||||
WorkItem priceUpdate = {TASK_PRICE_UPDATE, 0};
|
||||
WorkItem priceUpdate = {TASK_PRICE_UPDATE, currency};
|
||||
xQueueSend(workQueue, &priceUpdate, portMAX_DELAY);
|
||||
}
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
uint getLastPriceUpdate()
|
||||
uint getLastPriceUpdate(char currency)
|
||||
{
|
||||
return lastPriceUpdate;
|
||||
if (lastUpdateMap.find(currency) == lastUpdateMap.end()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return lastUpdateMap[currency];
|
||||
}
|
||||
|
||||
uint getPrice() { return currentPrice; }
|
||||
uint getPrice(char currency) {
|
||||
if (currencyMap.find(currency) == currencyMap.end()) {
|
||||
return 0;
|
||||
}
|
||||
return currencyMap[currency];
|
||||
}
|
||||
|
||||
void setPrice(uint newPrice) { currentPrice = newPrice; }
|
||||
void setPrice(uint newPrice, char currency) {
|
||||
currencyMap[currency] = newPrice;
|
||||
}
|
||||
|
||||
bool isPriceNotifyConnected()
|
||||
{
|
||||
|
|
|
@ -14,14 +14,15 @@ void onWebsocketPriceEvent(void *handler_args, esp_event_base_t base,
|
|||
int32_t event_id, void *event_data);
|
||||
void onWebsocketPriceMessage(esp_websocket_event_data_t *event_data);
|
||||
|
||||
uint getPrice();
|
||||
void setPrice(uint newPrice);
|
||||
uint getPrice(char currency);
|
||||
void setPrice(uint newPrice, char currency);
|
||||
|
||||
void processNewPrice(uint newPrice);
|
||||
//void processNewPrice(uint newPrice);
|
||||
void processNewPrice(uint newPrice, char currency);
|
||||
|
||||
bool isPriceNotifyConnected();
|
||||
void stopPriceNotify();
|
||||
void restartPriceNotify();
|
||||
|
||||
bool getPriceNotifyInit();
|
||||
uint getLastPriceUpdate();
|
||||
uint getLastPriceUpdate(char currency);
|
|
@ -16,6 +16,7 @@ std::string priceString;
|
|||
QueueHandle_t workQueue = NULL;
|
||||
|
||||
uint currentScreen;
|
||||
uint currentCurrency = CURRENCY_USD;
|
||||
|
||||
void workerTask(void *pvParameters) {
|
||||
WorkItem receivedItem;
|
||||
|
@ -40,18 +41,19 @@ void workerTask(void *pvParameters) {
|
|||
}
|
||||
break;
|
||||
case TASK_PRICE_UPDATE: {
|
||||
uint price = getPrice();
|
||||
u_char priceSymbol = '$';
|
||||
if (preferences.getBool("fetchEurPrice", DEFAULT_FETCH_EUR_PRICE)) {
|
||||
priceSymbol = '[';
|
||||
}
|
||||
uint currency = getCurrentCurrency();
|
||||
uint price = getPrice(currency);
|
||||
// u_char priceSymbol = '$';
|
||||
// if (preferences.getBool("fetchEurPrice", DEFAULT_FETCH_EUR_PRICE)) {
|
||||
// priceSymbol = '[';
|
||||
// }
|
||||
if (getCurrentScreen() == SCREEN_BTC_TICKER) {
|
||||
taskEpdContent = parsePriceData(price, priceSymbol, preferences.getBool("suffixPrice", DEFAULT_SUFFIX_PRICE));
|
||||
} else if (getCurrentScreen() == SCREEN_MSCW_TIME) {
|
||||
taskEpdContent = parseSatsPerCurrency(price, priceSymbol, preferences.getBool("useSatsSymbol", DEFAULT_USE_SATS_SYMBOL));
|
||||
taskEpdContent = parsePriceData(price, currency, preferences.getBool("suffixPrice", DEFAULT_SUFFIX_PRICE));
|
||||
} else if (getCurrentScreen() == SCREEN_SATS_PER_CURRENCY) {
|
||||
taskEpdContent = parseSatsPerCurrency(price, currency, preferences.getBool("useSatsSymbol", DEFAULT_USE_SATS_SYMBOL));
|
||||
} else {
|
||||
taskEpdContent =
|
||||
parseMarketCap(getBlockHeight(), price, priceSymbol,
|
||||
parseMarketCap(getBlockHeight(), price, currency,
|
||||
preferences.getBool("mcapBigChar", DEFAULT_MCAP_BIG_CHAR));
|
||||
}
|
||||
|
||||
|
@ -152,7 +154,7 @@ void setupTasks() {
|
|||
xTaskCreate(workerTask, "workerTask", 4096, NULL, tskIDLE_PRIORITY,
|
||||
&workerTaskHandle);
|
||||
|
||||
xTaskCreate(taskScreenRotate, "rotateScreen", 2048, NULL, tskIDLE_PRIORITY,
|
||||
xTaskCreate(taskScreenRotate, "rotateScreen", 4096, NULL, tskIDLE_PRIORITY,
|
||||
&taskScreenRotateTaskHandle);
|
||||
|
||||
waitUntilNoneBusy();
|
||||
|
@ -242,7 +244,7 @@ void setCurrentScreen(uint newScreen) {
|
|||
break;
|
||||
}
|
||||
case SCREEN_MARKET_CAP:
|
||||
case SCREEN_MSCW_TIME:
|
||||
case SCREEN_SATS_PER_CURRENCY:
|
||||
case SCREEN_BTC_TICKER: {
|
||||
WorkItem priceUpdate = {TASK_PRICE_UPDATE, 0};
|
||||
xQueueSend(workQueue, &priceUpdate, portMAX_DELAY);
|
||||
|
@ -270,10 +272,36 @@ void setCurrentScreen(uint newScreen) {
|
|||
if (eventSourceTaskHandle != NULL) xTaskNotifyGive(eventSourceTaskHandle);
|
||||
}
|
||||
|
||||
bool isCurrencySpecific(uint screen) {
|
||||
switch (screen) {
|
||||
case SCREEN_BTC_TICKER:
|
||||
case SCREEN_SATS_PER_CURRENCY:
|
||||
case SCREEN_MARKET_CAP:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void nextScreen() {
|
||||
int currentIndex = findScreenIndexByValue(getCurrentScreen());
|
||||
std::vector<ScreenMapping> screenMappings = getScreenNameMap();
|
||||
|
||||
if (preferences.getBool("ownDataSource", DEFAULT_OWN_DATA_SOURCE) && isCurrencySpecific(getCurrentScreen())) {
|
||||
std::vector<std::string> ac = getActiveCurrencies();
|
||||
std::string curCode = getCurrencyCode(getCurrentCurrency());
|
||||
if (getCurrencyCode(getCurrentCurrency()) != ac.back()) {
|
||||
auto it = std::find(ac.begin(), ac.end(), curCode);
|
||||
if (it != ac.end()) {
|
||||
size_t index = std::distance(ac.begin(), it);
|
||||
setCurrentCurrency(getCurrencyChar(ac.at(index+1)));
|
||||
setCurrentScreen(getCurrentScreen());
|
||||
return;
|
||||
}
|
||||
}
|
||||
setCurrentCurrency(getCurrencyChar(ac.front()));
|
||||
}
|
||||
|
||||
int newCurrentScreen;
|
||||
|
||||
if (currentIndex < screenMappings.size() - 1) {
|
||||
|
@ -302,6 +330,23 @@ void previousScreen() {
|
|||
int currentIndex = findScreenIndexByValue(getCurrentScreen());
|
||||
std::vector<ScreenMapping> screenMappings = getScreenNameMap();
|
||||
|
||||
if (preferences.getBool("ownDataSource", DEFAULT_OWN_DATA_SOURCE) && isCurrencySpecific(getCurrentScreen())) {
|
||||
std::vector<std::string> ac = getActiveCurrencies();
|
||||
std::string curCode = getCurrencyCode(getCurrentCurrency());
|
||||
if (getCurrencyCode(getCurrentCurrency()) != ac.front()) {
|
||||
auto it = std::find(ac.begin(), ac.end(), curCode);
|
||||
if (it != ac.end()) {
|
||||
size_t index = std::distance(ac.begin(), it);
|
||||
setCurrentCurrency(getCurrencyChar(ac.at(index-1)));
|
||||
setCurrentScreen(getCurrentScreen());
|
||||
return;
|
||||
}
|
||||
}
|
||||
setCurrentCurrency(getCurrencyChar(ac.back()));
|
||||
|
||||
}
|
||||
|
||||
|
||||
int newCurrentScreen;
|
||||
|
||||
if (currentIndex > 0) {
|
||||
|
@ -353,3 +398,12 @@ void showSystemStatusScreen() {
|
|||
setCurrentScreen(SCREEN_CUSTOM);
|
||||
setEpdContent(sysStatusEpdContent);
|
||||
}
|
||||
|
||||
void setCurrentCurrency(char currency) {
|
||||
currentCurrency = currency;
|
||||
preferences.putUChar("lastCurrency", currency);
|
||||
}
|
||||
|
||||
uint getCurrentCurrency() {
|
||||
return currentCurrency;
|
||||
}
|
|
@ -60,3 +60,6 @@ void setTimerActive(bool status);
|
|||
void toggleTimerActive();
|
||||
|
||||
void setupTasks();
|
||||
void setCurrentCurrency(char currency);
|
||||
|
||||
uint getCurrentCurrency();
|
|
@ -29,12 +29,29 @@ extern std::mutex mcpMutex;
|
|||
#endif
|
||||
|
||||
const PROGMEM int SCREEN_BLOCK_HEIGHT = 0;
|
||||
const PROGMEM int SCREEN_MSCW_TIME = 1;
|
||||
const PROGMEM int SCREEN_BTC_TICKER = 2;
|
||||
|
||||
const PROGMEM int SCREEN_TIME = 3;
|
||||
const PROGMEM int SCREEN_HALVING_COUNTDOWN = 4;
|
||||
const PROGMEM int SCREEN_MARKET_CAP = 5;
|
||||
const PROGMEM int SCREEN_BLOCK_FEE_RATE = 6;
|
||||
|
||||
const PROGMEM int SCREEN_SATS_PER_CURRENCY = 10;
|
||||
|
||||
const PROGMEM int SCREEN_BTC_TICKER = 20;
|
||||
// const PROGMEM int SCREEN_BTC_TICKER_USD = 20;
|
||||
// const PROGMEM int SCREEN_BTC_TICKER_EUR = 21;
|
||||
// const PROGMEM int SCREEN_BTC_TICKER_GBP = 22;
|
||||
// const PROGMEM int SCREEN_BTC_TICKER_JPY = 23;
|
||||
// const PROGMEM int SCREEN_BTC_TICKER_AUD = 24;
|
||||
// const PROGMEM int SCREEN_BTC_TICKER_CAD = 25;
|
||||
|
||||
const PROGMEM int SCREEN_MARKET_CAP = 30;
|
||||
// const PROGMEM int SCREEN_MARKET_CAP_USD = 30;
|
||||
// const PROGMEM int SCREEN_MARKET_CAP_EUR = 31;
|
||||
// const PROGMEM int SCREEN_MARKET_CAP_GBP = 32;
|
||||
// const PROGMEM int SCREEN_MARKET_CAP_JPY = 33;
|
||||
// const PROGMEM int SCREEN_MARKET_CAP_AUD = 34;
|
||||
// const PROGMEM int SCREEN_MARKET_CAP_CAD = 35;
|
||||
|
||||
const PROGMEM int SCREEN_BITAXE_HASHRATE = 80;
|
||||
const PROGMEM int SCREEN_BITAXE_BESTDIFF = 81;
|
||||
|
||||
|
@ -42,7 +59,7 @@ const PROGMEM int SCREEN_COUNTDOWN = 98;
|
|||
const PROGMEM int SCREEN_CUSTOM = 99;
|
||||
const int SCREEN_COUNT = 7;
|
||||
const PROGMEM int screens[SCREEN_COUNT] = {
|
||||
SCREEN_BLOCK_HEIGHT, SCREEN_MSCW_TIME, SCREEN_BTC_TICKER,
|
||||
SCREEN_BLOCK_HEIGHT, SCREEN_SATS_PER_CURRENCY, SCREEN_BTC_TICKER,
|
||||
SCREEN_TIME, SCREEN_HALVING_COUNTDOWN, SCREEN_MARKET_CAP,
|
||||
SCREEN_BLOCK_FEE_RATE};
|
||||
const int usPerSecond = 1000000;
|
||||
|
|
142
src/lib/v2_notify.cpp
Normal file
142
src/lib/v2_notify.cpp
Normal file
|
@ -0,0 +1,142 @@
|
|||
#include "v2_notify.hpp"
|
||||
|
||||
WebSocketsClient webSocket;
|
||||
TaskHandle_t v2NotifyTaskHandle;
|
||||
|
||||
void setupV2Notify()
|
||||
{
|
||||
String hostname = "ws.btclock.dev";
|
||||
if ( preferences.getBool("stagingSource", DEFAULT_STAGING_SOURCE)) {
|
||||
Serial.println(F("Connecting to V2 staging source"));
|
||||
hostname = "ws-staging.btclock.dev";
|
||||
}
|
||||
|
||||
webSocket.beginSSL(hostname, 443, "/api/v2/ws");
|
||||
webSocket.onEvent(onWebsocketV2Event);
|
||||
webSocket.setReconnectInterval(5000);
|
||||
webSocket.enableHeartbeat(15000, 3000, 2);
|
||||
|
||||
setupV2NotifyTask();
|
||||
}
|
||||
|
||||
void onWebsocketV2Event(WStype_t type, uint8_t * payload, size_t length) {
|
||||
switch(type) {
|
||||
case WStype_DISCONNECTED:
|
||||
Serial.printf("[WSc] Disconnected!\n");
|
||||
break;
|
||||
case WStype_CONNECTED:
|
||||
{
|
||||
Serial.printf("[WSc] Connected to url: %s\n", payload);
|
||||
|
||||
JsonDocument response;
|
||||
|
||||
response["type"] = "subscribe";
|
||||
response["eventType"] = "blockfee";
|
||||
size_t responseLength = measureMsgPack(response);
|
||||
uint8_t* buffer = new uint8_t[responseLength];
|
||||
serializeMsgPack(response, buffer, responseLength);
|
||||
webSocket.sendBIN(buffer, responseLength);
|
||||
delete[] buffer;
|
||||
|
||||
buffer = new uint8_t[responseLength];
|
||||
|
||||
response["type"] = "subscribe";
|
||||
response["eventType"] = "blockheight";
|
||||
responseLength = measureMsgPack(response);
|
||||
buffer = new uint8_t[responseLength];
|
||||
serializeMsgPack(response, buffer, responseLength);
|
||||
webSocket.sendBIN(buffer, responseLength);
|
||||
|
||||
delete[] buffer;
|
||||
|
||||
buffer = new uint8_t[responseLength];
|
||||
|
||||
response["type"] = "subscribe";
|
||||
response["eventType"] = "price";
|
||||
|
||||
JsonArray currenciesArray = response["currencies"].to<JsonArray>();
|
||||
|
||||
for (const auto &str : getActiveCurrencies())
|
||||
{
|
||||
currenciesArray.add(str);
|
||||
}
|
||||
|
||||
// response["currencies"] = currenciesArray;
|
||||
responseLength = measureMsgPack(response);
|
||||
buffer = new uint8_t[responseLength];
|
||||
serializeMsgPack(response, buffer, responseLength);
|
||||
webSocket.sendBIN(buffer, responseLength);
|
||||
break;
|
||||
}
|
||||
case WStype_TEXT:
|
||||
Serial.printf("[WSc] get text: %s\n", payload);
|
||||
|
||||
// send message to server
|
||||
// webSocket.sendTXT("message here");
|
||||
break;
|
||||
case WStype_BIN:
|
||||
{
|
||||
JsonDocument doc;
|
||||
DeserializationError error = deserializeMsgPack(doc, payload, length);
|
||||
|
||||
handleV2Message(doc);
|
||||
break;
|
||||
}
|
||||
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 handleV2Message(JsonDocument doc) {
|
||||
if (doc.containsKey("blockheight"))
|
||||
{
|
||||
uint newBlockHeight = doc["blockheight"].as<uint>();
|
||||
|
||||
if (newBlockHeight == getBlockHeight()) {
|
||||
return;
|
||||
}
|
||||
|
||||
processNewBlock(newBlockHeight);
|
||||
}
|
||||
else if (doc.containsKey("blockfee"))
|
||||
{
|
||||
uint medianFee = doc["blockfee"].as<uint>();
|
||||
|
||||
processNewBlockFee(medianFee);
|
||||
} else if (doc.containsKey("price"))
|
||||
{
|
||||
|
||||
// Iterate through the key-value pairs of the "price" object
|
||||
for (JsonPair kv : doc["price"].as<JsonObject>()) {
|
||||
const char* currency = kv.key().c_str();
|
||||
uint newPrice = kv.value().as<uint>();
|
||||
|
||||
processNewPrice(newPrice, getCurrencyChar(currency));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void taskV2Notify(void *pvParameters) {
|
||||
for(;;) {
|
||||
webSocket.loop();
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
|
||||
void setupV2NotifyTask() {
|
||||
xTaskCreate(taskV2Notify, "v2Notify", (6 * 1024), NULL, tskIDLE_PRIORITY,
|
||||
&v2NotifyTaskHandle);
|
||||
|
||||
}
|
||||
|
||||
bool isV2NotifyConnected()
|
||||
{
|
||||
return webSocket.isConnected();
|
||||
}
|
24
src/lib/v2_notify.hpp
Normal file
24
src/lib/v2_notify.hpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <esp_websocket_client.h>
|
||||
#include "block_notify.hpp"
|
||||
#include <string>
|
||||
|
||||
#include "lib/screen_handler.hpp"
|
||||
|
||||
extern TaskHandle_t v2NotifyTaskHandle;
|
||||
|
||||
void setupV2NotifyTask();
|
||||
void taskV2Notify(void *pvParameters);
|
||||
|
||||
void setupV2Notify();
|
||||
void onWebsocketV2Event(WStype_t type, uint8_t * payload, size_t length);
|
||||
void handleV2Message(JsonDocument doc);
|
||||
|
||||
bool isV2NotifyConnected();
|
||||
// void stopV2Notify();
|
||||
// void restartV2Notify();
|
||||
// bool getPriceNotifyInit();
|
||||
// uint getLastPriceUpdate();
|
|
@ -41,9 +41,10 @@ void setupWebserver()
|
|||
server.on("/api/action/timer_restart", HTTP_GET, onApiActionTimerRestart);
|
||||
|
||||
server.on("/api/settings", HTTP_GET, onApiSettingsGet);
|
||||
server.on("/api/settings", HTTP_POST, onApiSettingsPost);
|
||||
|
||||
server.on("/api/show/screen", HTTP_GET, onApiShowScreen);
|
||||
server.on("/api/show/currency", HTTP_GET, onApiShowCurrency);
|
||||
|
||||
server.on("/api/show/text", HTTP_GET, onApiShowText);
|
||||
|
||||
server.on("/api/screen/next", HTTP_GET, onApiScreenNext);
|
||||
|
@ -88,6 +89,8 @@ void setupWebserver()
|
|||
}
|
||||
|
||||
server.on("/api/restart", HTTP_GET, onApiRestart);
|
||||
server.addRewrite(
|
||||
new OneParamRewrite("/api/show/currency/{c}", "/api/show/currency?c={c}"));
|
||||
server.addRewrite(new OneParamRewrite("/api/lights/color/{color}",
|
||||
"/api/lights/color?c={color}"));
|
||||
server.addRewrite(
|
||||
|
@ -241,10 +244,12 @@ JsonDocument getStatusObject()
|
|||
JsonObject conStatus = root["connectionStatus"].to<JsonObject>();
|
||||
conStatus["price"] = isPriceNotifyConnected();
|
||||
conStatus["blocks"] = isBlockNotifyConnected();
|
||||
conStatus["V2"] = isV2NotifyConnected();
|
||||
|
||||
conStatus["nostr"] = nostrConnected();
|
||||
|
||||
root["rssi"] = WiFi.RSSI();
|
||||
|
||||
root["currency"] = getCurrencyCode(getCurrentCurrency());
|
||||
#ifdef HAS_FRONTLIGHT
|
||||
std::vector<uint16_t> statuses = frontlightGetStatus();
|
||||
uint16_t arr[NUM_SCREENS];
|
||||
|
@ -548,6 +553,23 @@ void onApiSettingsPatch(AsyncWebServerRequest *request, JsonVariant &json)
|
|||
}
|
||||
}
|
||||
|
||||
if (settings.containsKey("actCurrencies"))
|
||||
{
|
||||
String actCurrencies;
|
||||
|
||||
for (JsonVariant cur : settings["actCurrencies"].as<JsonArray>())
|
||||
{
|
||||
if (!actCurrencies.isEmpty())
|
||||
{
|
||||
actCurrencies += ",";
|
||||
}
|
||||
actCurrencies += cur.as<String>();
|
||||
}
|
||||
|
||||
preferences.putString("actCurrencies", actCurrencies.c_str());
|
||||
Serial.printf("Set actCurrencies: %s\n", actCurrencies);
|
||||
}
|
||||
|
||||
if (settings.containsKey("txPower"))
|
||||
{
|
||||
int txPower = settings["txPower"].as<int>();
|
||||
|
@ -696,6 +718,18 @@ void onApiSettingsGet(AsyncWebServerRequest *request)
|
|||
#endif
|
||||
JsonArray screens = root["screens"].to<JsonArray>();
|
||||
|
||||
JsonArray actCurrencies = root["actCurrencies"].to<JsonArray>();
|
||||
for (const auto &str : getActiveCurrencies())
|
||||
{
|
||||
actCurrencies.add(str);
|
||||
}
|
||||
|
||||
JsonArray availableCurrencies = root["availableCurrencies"].to<JsonArray>();
|
||||
for (const auto &str : getAvailableCurrencies())
|
||||
{
|
||||
availableCurrencies.add(str);
|
||||
}
|
||||
|
||||
std::vector<ScreenMapping> screenNameMap = getScreenNameMap();
|
||||
|
||||
for (int i = 0; i < screenNameMap.size(); i++)
|
||||
|
@ -703,7 +737,7 @@ void onApiSettingsGet(AsyncWebServerRequest *request)
|
|||
JsonObject o = screens.add<JsonObject>();
|
||||
String key = "screen" + String(screenNameMap.at(i).value) + "Visible";
|
||||
o["id"] = screenNameMap.at(i).value;
|
||||
o["name"] = screenNameMap.at(i).name;
|
||||
o["name"] = String(screenNameMap.at(i).name);
|
||||
o["enabled"] = preferences.getBool(key.c_str(), true);
|
||||
}
|
||||
|
||||
|
@ -740,230 +774,6 @@ bool processEpdColorSettings(AsyncWebServerRequest *request)
|
|||
return settingsChanged;
|
||||
}
|
||||
|
||||
void onApiSettingsPost(AsyncWebServerRequest *request)
|
||||
{
|
||||
// bool settingsChanged = false;
|
||||
|
||||
// settingsChanged = processEpdColorSettings(request);
|
||||
|
||||
// int headers = request->headers();
|
||||
// int i;
|
||||
// for (i = 0; i < headers; i++)
|
||||
// {
|
||||
// AsyncWebHeader *h = request->getHeader(i);
|
||||
// Serial.printf("HEADER[%s]: %s\n", h->name().c_str(), h->value().c_str());
|
||||
// }
|
||||
|
||||
// int params = request->params();
|
||||
// for (int i = 0; i < params; i++)
|
||||
// {
|
||||
// const AsyncWebParameter *p = request->getParam(i);
|
||||
// if (p->isFile())
|
||||
// { // p->isPost() is also true
|
||||
// Serial.printf("FILE[%s]: %s, size: %u\n", p->name().c_str(),
|
||||
// p->value().c_str(), p->size());
|
||||
// }
|
||||
// else if (p->isPost())
|
||||
// {
|
||||
// Serial.printf("POST[%s]: %s\n", p->name().c_str(), p->value().c_str());
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// Serial.printf("GET[%s]: %s\n", p->name().c_str(), p->value().c_str());
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (request->hasParam("fetchEurPrice", true))
|
||||
// {
|
||||
// const AsyncWebParameter *fetchEurPrice = request->getParam("fetchEurPrice", true);
|
||||
|
||||
// preferences.putBool("fetchEurPrice", fetchEurPrice->value().toInt());
|
||||
// settingsChanged = true;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// preferences.putBool("fetchEurPrice", 0);
|
||||
// settingsChanged = true;
|
||||
// }
|
||||
|
||||
// if (request->hasParam("ledTestOnPower", true))
|
||||
// {
|
||||
// const AsyncWebParameter *ledTestOnPower =
|
||||
// request->getParam("ledTestOnPower", true);
|
||||
|
||||
// preferences.putBool("ledTestOnPower", ledTestOnPower->value().toInt());
|
||||
// settingsChanged = true;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// preferences.putBool("ledTestOnPower", 0);
|
||||
// settingsChanged = true;
|
||||
// }
|
||||
|
||||
// if (request->hasParam("ledFlashOnUpd", true))
|
||||
// {
|
||||
// const AsyncWebParameter *ledFlashOnUpdate =
|
||||
// request->getParam("ledFlashOnUpd", true);
|
||||
|
||||
// preferences.putBool("ledFlashOnUpd", ledFlashOnUpdate->value().toInt());
|
||||
// settingsChanged = true;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// preferences.putBool("ledFlashOnUpd", 0);
|
||||
// settingsChanged = true;
|
||||
// }
|
||||
|
||||
// if (request->hasParam("mdnsEnabled", true))
|
||||
// {
|
||||
// const AsyncWebParameter *mdnsEnabled = request->getParam("mdnsEnabled", true);
|
||||
|
||||
// preferences.putBool("mdnsEnabled", mdnsEnabled->value().toInt());
|
||||
// settingsChanged = true;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// preferences.putBool("mdnsEnabled", 0);
|
||||
// settingsChanged = true;
|
||||
// }
|
||||
|
||||
// if (request->hasParam("otaEnabled", true))
|
||||
// {
|
||||
// const AsyncWebParameter *otaEnabled = request->getParam("otaEnabled", true);
|
||||
|
||||
// preferences.putBool("otaEnabled", otaEnabled->value().toInt());
|
||||
// settingsChanged = true;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// preferences.putBool("otaEnabled", 0);
|
||||
// settingsChanged = true;
|
||||
// }
|
||||
|
||||
// if (request->hasParam("stealFocus", false))
|
||||
// {
|
||||
// const AsyncWebParameter *stealFocusOnBlock =
|
||||
// request->getParam("stealFocus", false);
|
||||
|
||||
// preferences.putBool("stealFocus", stealFocusOnBlock->value().toInt());
|
||||
// settingsChanged = true;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// preferences.putBool("stealFocus", 0);
|
||||
// settingsChanged = true;
|
||||
// }
|
||||
|
||||
// if (request->hasParam("mcapBigChar", true))
|
||||
// {
|
||||
// const AsyncWebParameter *mcapBigChar = request->getParam("mcapBigChar", true);
|
||||
|
||||
// preferences.putBool("mcapBigChar", mcapBigChar->value().toInt());
|
||||
// settingsChanged = true;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// preferences.putBool("mcapBigChar", 0);
|
||||
// settingsChanged = true;
|
||||
// }
|
||||
|
||||
// if (request->hasParam("mempoolInstance", true))
|
||||
// {
|
||||
// const AsyncWebParameter *mempoolInstance =
|
||||
// request->getParam("mempoolInstance", true);
|
||||
|
||||
// preferences.putString("mempoolInstance", mempoolInstance->value().c_str());
|
||||
// settingsChanged = true;
|
||||
// }
|
||||
|
||||
// if (request->hasParam("hostnamePrefix", true))
|
||||
// {
|
||||
// const AsyncWebParameter *hostnamePrefix =
|
||||
// request->getParam("hostnamePrefix", true);
|
||||
|
||||
// preferences.putString("hostnamePrefix", hostnamePrefix->value().c_str());
|
||||
// settingsChanged = true;
|
||||
// }
|
||||
|
||||
// if (request->hasParam("ledBrightness", true))
|
||||
// {
|
||||
// const AsyncWebParameter *ledBrightness = request->getParam("ledBrightness", true);
|
||||
|
||||
// preferences.putUInt("ledBrightness", ledBrightness->value().toInt());
|
||||
// settingsChanged = true;
|
||||
// }
|
||||
|
||||
// if (request->hasParam("fullRefreshMin", true))
|
||||
// {
|
||||
// const AsyncWebParameter *fullRefreshMin =
|
||||
// request->getParam("fullRefreshMin", true);
|
||||
|
||||
// preferences.putUInt("fullRefreshMin", fullRefreshMin->value().toInt());
|
||||
// settingsChanged = true;
|
||||
// }
|
||||
|
||||
// if (request->hasParam("wpTimeout", true))
|
||||
// {
|
||||
// const AsyncWebParameter *wpTimeout = request->getParam("wpTimeout", true);
|
||||
|
||||
// preferences.putUInt("wpTimeout", wpTimeout->value().toInt());
|
||||
// settingsChanged = true;
|
||||
// }
|
||||
|
||||
// std::vector<std::string> screenNameMap = getScreenNameMap();
|
||||
|
||||
// if (request->hasParam("screens"))
|
||||
// {
|
||||
// const AsyncWebParameter *screenParam = request->getParam("screens", true);
|
||||
|
||||
// Serial.printf(screenParam->value().c_str());
|
||||
// }
|
||||
|
||||
// for (int i = 0; i < screenNameMap.size(); i++)
|
||||
// {
|
||||
// String key = "screen[" + String(i) + "]";
|
||||
// String prefKey = "screen" + String(i) + "Visible";
|
||||
// bool visible = false;
|
||||
// if (request->hasParam(key, true))
|
||||
// {
|
||||
// const AsyncWebParameter *screenParam = request->getParam(key, true);
|
||||
// visible = screenParam->value().toInt();
|
||||
// }
|
||||
|
||||
// preferences.putBool(prefKey.c_str(), visible);
|
||||
// }
|
||||
|
||||
// if (request->hasParam("tzOffset", true))
|
||||
// {
|
||||
// const AsyncWebParameter *p = request->getParam("tzOffset", true);
|
||||
// int tzOffsetSeconds = p->value().toInt() * 60;
|
||||
// preferences.putInt("gmtOffset", tzOffsetSeconds);
|
||||
// settingsChanged = true;
|
||||
// }
|
||||
|
||||
// if (request->hasParam("minSecPriceUpd", true))
|
||||
// {
|
||||
// const AsyncWebParameter *p = request->getParam("minSecPriceUpd", true);
|
||||
// int minSecPriceUpd = p->value().toInt();
|
||||
// preferences.putUInt("minSecPriceUpd", minSecPriceUpd);
|
||||
// settingsChanged = true;
|
||||
// }
|
||||
|
||||
// if (request->hasParam("timePerScreen", true))
|
||||
// {
|
||||
// const AsyncWebParameter *p = request->getParam("timePerScreen", true);
|
||||
// uint timerSeconds = p->value().toInt() * 60;
|
||||
// preferences.putUInt("timerSeconds", timerSeconds);
|
||||
// settingsChanged = true;
|
||||
// }
|
||||
|
||||
// request->send(200);
|
||||
// if (settingsChanged)
|
||||
// {
|
||||
// queueLedEffect(LED_FLASH_SUCCESS);
|
||||
// }
|
||||
}
|
||||
|
||||
void onApiSystemStatus(AsyncWebServerRequest *request)
|
||||
{
|
||||
AsyncResponseStream *response =
|
||||
|
@ -1209,6 +1019,30 @@ void eventSourceTask(void *pvParameters)
|
|||
}
|
||||
}
|
||||
|
||||
void onApiShowCurrency(AsyncWebServerRequest *request)
|
||||
{
|
||||
if (request->hasParam("c"))
|
||||
{
|
||||
const AsyncWebParameter *p = request->getParam("c");
|
||||
std::string currency = p->value().c_str();
|
||||
|
||||
if (!isActiveCurrency(currency))
|
||||
{
|
||||
request->send(404);
|
||||
return;
|
||||
}
|
||||
|
||||
char curChar = getCurrencyChar(currency);
|
||||
|
||||
setCurrentCurrency(curChar);
|
||||
setCurrentScreen(getCurrentScreen());
|
||||
|
||||
request->send(200);
|
||||
return;
|
||||
}
|
||||
request->send(404);
|
||||
}
|
||||
|
||||
#ifdef HAS_FRONTLIGHT
|
||||
void onApiFrontlightOn(AsyncWebServerRequest *request)
|
||||
{
|
||||
|
|
|
@ -30,6 +30,8 @@ void onApiScreenNext(AsyncWebServerRequest *request);
|
|||
void onApiScreenPrevious(AsyncWebServerRequest *request);
|
||||
|
||||
void onApiShowScreen(AsyncWebServerRequest *request);
|
||||
void onApiShowCurrency(AsyncWebServerRequest *request);
|
||||
|
||||
void onApiShowText(AsyncWebServerRequest *request);
|
||||
void onApiIdentify(AsyncWebServerRequest *request);
|
||||
|
||||
|
@ -38,7 +40,6 @@ void onApiShowTextAdvanced(AsyncWebServerRequest *request, JsonVariant &json);
|
|||
void onApiActionPause(AsyncWebServerRequest *request);
|
||||
void onApiActionTimerRestart(AsyncWebServerRequest *request);
|
||||
void onApiSettingsGet(AsyncWebServerRequest *request);
|
||||
void onApiSettingsPost(AsyncWebServerRequest *request);
|
||||
void onApiSettingsPatch(AsyncWebServerRequest *request, JsonVariant &json);
|
||||
void onApiFullRefresh(AsyncWebServerRequest *request);
|
||||
|
||||
|
|
|
@ -134,7 +134,7 @@ extern "C" void app_main()
|
|||
}
|
||||
|
||||
// if more than 5 price updates are missed, there is probably something wrong, reconnect
|
||||
if ((getLastPriceUpdate() - currentUptime) > (preferences.getUInt("minSecPriceUpd", DEFAULT_SECONDS_BETWEEN_PRICE_UPDATE) * 5))
|
||||
if ((getLastPriceUpdate(CURRENCY_USD) - currentUptime) > (preferences.getUInt("minSecPriceUpd", DEFAULT_SECONDS_BETWEEN_PRICE_UPDATE) * 5))
|
||||
{
|
||||
Serial.println(F("Detected 5 missed price updates... restarting price handler."));
|
||||
|
||||
|
|
Loading…
Reference in a new issue