From c276d32807acfe280f07f7cd7a56fecbed66ecdd Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Thu, 5 Sep 2024 14:00:15 +0200 Subject: [PATCH] Implement multi-currency support with MsgPack --- data | 2 +- lib/btclock/data_handler.cpp | 38 ++-- lib/btclock/data_handler.hpp | 10 +- src/lib/config.cpp | 350 +++++++++++++---------------------- src/lib/config.hpp | 10 + src/lib/defaults.hpp | 4 +- src/lib/nostr_notify.cpp | 2 +- src/lib/price_fetch.cpp | 4 +- src/lib/price_notify.cpp | 77 +++----- src/lib/price_notify.hpp | 9 +- src/lib/screen_handler.cpp | 76 ++++++-- src/lib/screen_handler.hpp | 3 + src/lib/shared.hpp | 25 ++- src/lib/v2_notify.cpp | 142 ++++++++++++++ src/lib/v2_notify.hpp | 24 +++ src/lib/webserver.cpp | 288 ++++++---------------------- src/lib/webserver.hpp | 3 +- src/main.cpp | 2 +- 18 files changed, 528 insertions(+), 541 deletions(-) create mode 100644 src/lib/v2_notify.cpp create mode 100644 src/lib/v2_notify.hpp diff --git a/data b/data index 34b09a2..2fffb3e 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit 34b09a2d1134d48d7733a3d11a9e6f3f15d080a9 +Subproject commit 2fffb3ef0284b4262ca97a81eb979259186604e5 diff --git a/lib/btclock/data_handler.cpp b/lib/btclock/data_handler.cpp index 56cc6e4..0905e8a 100644 --- a/lib/btclock/data_handler.cpp +++ b/lib/btclock/data_handler.cpp @@ -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 parsePriceData(std::uint32_t price, char currencySymbol, bool useSuffixFormat) { std::array ret; @@ -205,14 +221,8 @@ std::array parseMarketCap(std::uint32_t blockHeight, s std::uint32_t firstIndex = 0; double supply = getSupplyAtBlock(blockHeight); int64_t marketCap = static_cast(supply * double(price)); - if (currencySymbol == '[') - { - ret[0] = "EUR/MCAP"; - } - else - { - ret[0] = "USD/MCAP"; - } + + ret[0] = getCurrencyCode(currencySymbol) + "/MCAP"; if (bigChars) { diff --git a/lib/btclock/data_handler.hpp b/lib/btclock/data_handler.hpp index 6ccb49a..2d563a4 100644 --- a/lib/btclock/data_handler.hpp +++ b/lib/btclock/data_handler.hpp @@ -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 parsePriceData(std::uint32_t price, char currency, bool useSuffixFormat = false); std::array parseSatsPerCurrency(std::uint32_t price, char currencySymbol, bool withSatsSymbol); std::array parseBlockHeight(std::uint32_t blockHeight); @@ -20,4 +27,5 @@ std::array parseMarketCap(std::uint32_t blockHeight, s std::array parseBlockFees(std::uint16_t blockFees); char getCurrencySymbol(char input); -std::string getCurrencyCode(char input); \ No newline at end of file +std::string getCurrencyCode(char input); +char getCurrencyChar(const std::string& input); \ No newline at end of file diff --git a/src/lib/config.cpp b/src/lib/config.cpp index 54c70e0..7a19abe 100644 --- a/src/lib/config.cpp +++ b/src/lib/config.cpp @@ -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& 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 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 data = improv::build_rpc_response( - improv::GET_WIFI_NETWORKS, std::vector{}, 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 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 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 epdContent = {"S", "U", "C", "C", - // "E", "S", "S"}; setEpdContent(epdContent); - - preferences.putBool("wifiConfigured", true); - - improv_set_state(improv::STATE_PROVISIONED); - std::vector 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 infos = {// Firmware name - "BTClock", - // Firmware version - "1.0.0", - // Hardware chip/variant - "ESP32S3", - // Device name - "BTClock"}; - std::vector 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 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 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 &response) -{ - std::vector 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 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 getAvailableCurrencies() +{ + return {CURRENCY_CODE_USD, CURRENCY_CODE_EUR, CURRENCY_CODE_GBP, CURRENCY_CODE_JPY, CURRENCY_CODE_AUD, CURRENCY_CODE_CAD}; +} + +std::vector getActiveCurrencies() +{ + std::vector 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 ac = getActiveCurrencies(); + if (std::find(ac.begin(), ac.end(), currency) != ac.end()) + { + return true; + } + return false; +} \ No newline at end of file diff --git a/src/lib/config.hpp b/src/lib/config.hpp index 6888154..8b7434a 100644 --- a/src/lib/config.hpp +++ b/src/lib/config.hpp @@ -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 getLocalUrl(); // void improv_set_state(improv::State state); // void improv_send_response(std::vector &response); // void improv_set_error(improv::Error error); +//void addCurrencyMappings(const std::vector& currencies); +std::vector getActiveCurrencies(); +std::vector 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); \ No newline at end of file diff --git a/src/lib/defaults.hpp b/src/lib/defaults.hpp index ac95653..39e7406 100644 --- a/src/lib/defaults.hpp +++ b/src/lib/defaults.hpp @@ -58,4 +58,6 @@ #define DEFAULT_HTTP_AUTH_ENABLED false #define DEFAULT_HTTP_AUTH_USERNAME "btclock" -#define DEFAULT_HTTP_AUTH_PASSWORD "satoshi" \ No newline at end of file +#define DEFAULT_HTTP_AUTH_PASSWORD "satoshi" + +#define DEFAULT_ACTIVE_CURRENCIES "USD,EUR,JPY" \ No newline at end of file diff --git a/src/lib/nostr_notify.cpp b/src/lib/nostr_notify.cpp index b0adfd3..f9ac019 100644 --- a/src/lib/nostr_notify.cpp +++ b/src/lib/nostr_notify.cpp @@ -168,7 +168,7 @@ void handleNostrEventCallback(const String &subId, nostr::SignedNostrEvent *even { if (typeValue.equals("priceUsd")) { - processNewPrice(obj["content"].as()); + processNewPrice(obj["content"].as(), CURRENCY_USD); } else if (typeValue.equals("blockHeight")) { diff --git a/src/lib/price_fetch.cpp b/src/lib/price_fetch.cpp index d5e6e10..d2ddf67 100644 --- a/src/lib/price_fetch.cpp +++ b/src/lib/price_fetch.cpp @@ -29,9 +29,9 @@ void taskPriceFetch(void *pvParameters) { // usdPrice = doc["bitcoin"]["usd"]; eurPrice = doc["bitcoin"]["eur"].as(); - 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); diff --git a/src/lib/price_notify.cpp b/src/lib/price_notify.cpp index 062062a..f5ad972 100644 --- a/src/lib/price_notify.cpp +++ b/src/lib/price_notify.cpp @@ -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 currencyMap; +std::map lastUpdateMap; void setupPriceNotify() { @@ -100,48 +72,59 @@ void onWebsocketPriceMessage(esp_websocket_event_data_t *event_data) { if (currentPrice != doc["bitcoin"].as()) { - processNewPrice(doc["bitcoin"].as()); + processNewPrice(doc["bitcoin"].as(), 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() { diff --git a/src/lib/price_notify.hpp b/src/lib/price_notify.hpp index 485b096..541f899 100644 --- a/src/lib/price_notify.hpp +++ b/src/lib/price_notify.hpp @@ -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(); \ No newline at end of file +uint getLastPriceUpdate(char currency); \ No newline at end of file diff --git a/src/lib/screen_handler.cpp b/src/lib/screen_handler.cpp index 9335020..95a8682 100644 --- a/src/lib/screen_handler.cpp +++ b/src/lib/screen_handler.cpp @@ -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 screenMappings = getScreenNameMap(); + if (preferences.getBool("ownDataSource", DEFAULT_OWN_DATA_SOURCE) && isCurrencySpecific(getCurrentScreen())) { + std::vector 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 screenMappings = getScreenNameMap(); + if (preferences.getBool("ownDataSource", DEFAULT_OWN_DATA_SOURCE) && isCurrencySpecific(getCurrentScreen())) { + std::vector 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) { @@ -352,4 +397,13 @@ void showSystemStatusScreen() { (int)round(ESP.getHeapSize() / 1024); setCurrentScreen(SCREEN_CUSTOM); setEpdContent(sysStatusEpdContent); +} + +void setCurrentCurrency(char currency) { + currentCurrency = currency; + preferences.putUChar("lastCurrency", currency); +} + +uint getCurrentCurrency() { + return currentCurrency; } \ No newline at end of file diff --git a/src/lib/screen_handler.hpp b/src/lib/screen_handler.hpp index a3ecaf8..a5c8f8e 100644 --- a/src/lib/screen_handler.hpp +++ b/src/lib/screen_handler.hpp @@ -60,3 +60,6 @@ void setTimerActive(bool status); void toggleTimerActive(); void setupTasks(); +void setCurrentCurrency(char currency); + +uint getCurrentCurrency(); \ No newline at end of file diff --git a/src/lib/shared.hpp b/src/lib/shared.hpp index 4a2ca68..5c111df 100644 --- a/src/lib/shared.hpp +++ b/src/lib/shared.hpp @@ -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; diff --git a/src/lib/v2_notify.cpp b/src/lib/v2_notify.cpp new file mode 100644 index 0000000..a2f9744 --- /dev/null +++ b/src/lib/v2_notify.cpp @@ -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(); + + 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(); + + if (newBlockHeight == getBlockHeight()) { + return; + } + + processNewBlock(newBlockHeight); + } + else if (doc.containsKey("blockfee")) + { + uint medianFee = doc["blockfee"].as(); + + processNewBlockFee(medianFee); + } else if (doc.containsKey("price")) + { + + // Iterate through the key-value pairs of the "price" object + for (JsonPair kv : doc["price"].as()) { + const char* currency = kv.key().c_str(); + uint newPrice = kv.value().as(); + + 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(); +} diff --git a/src/lib/v2_notify.hpp b/src/lib/v2_notify.hpp new file mode 100644 index 0000000..d3e4907 --- /dev/null +++ b/src/lib/v2_notify.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include +#include +#include +#include "block_notify.hpp" +#include + +#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(); \ No newline at end of file diff --git a/src/lib/webserver.cpp b/src/lib/webserver.cpp index f735877..97441d4 100644 --- a/src/lib/webserver.cpp +++ b/src/lib/webserver.cpp @@ -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(); 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 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()) + { + if (!actCurrencies.isEmpty()) + { + actCurrencies += ","; + } + actCurrencies += cur.as(); + } + + preferences.putString("actCurrencies", actCurrencies.c_str()); + Serial.printf("Set actCurrencies: %s\n", actCurrencies); + } + if (settings.containsKey("txPower")) { int txPower = settings["txPower"].as(); @@ -696,6 +718,18 @@ void onApiSettingsGet(AsyncWebServerRequest *request) #endif JsonArray screens = root["screens"].to(); + JsonArray actCurrencies = root["actCurrencies"].to(); + for (const auto &str : getActiveCurrencies()) + { + actCurrencies.add(str); + } + + JsonArray availableCurrencies = root["availableCurrencies"].to(); + for (const auto &str : getAvailableCurrencies()) + { + availableCurrencies.add(str); + } + std::vector screenNameMap = getScreenNameMap(); for (int i = 0; i < screenNameMap.size(); i++) @@ -703,7 +737,7 @@ void onApiSettingsGet(AsyncWebServerRequest *request) JsonObject o = screens.add(); 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 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) { diff --git a/src/lib/webserver.hpp b/src/lib/webserver.hpp index b47d115..3f71ac9 100644 --- a/src/lib/webserver.hpp +++ b/src/lib/webserver.hpp @@ -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); diff --git a/src/main.cpp b/src/main.cpp index ef8594e..0839e9d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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."));