diff --git a/data b/data index 8389ed8..732dd26 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit 8389ed8e36a9a1a7a39ca31f53324a0949aedb1d +Subproject commit 732dd260ea708841f0e15ee1ee64a3d5115cd475 diff --git a/dependencies.lock b/dependencies.lock index 2274b85..c338e6c 100644 --- a/dependencies.lock +++ b/dependencies.lock @@ -4,6 +4,6 @@ dependencies: source: type: idf version: 4.4.7 -manifest_hash: 1d4ef353a86901733b106a1897b186dbf9fc091a4981f0560ea2f6899b7a3d44 +manifest_hash: cd2f3ee15e776d949eb4ea4eddc8f39b30c2a7905050850eed01ab4928143cff target: esp32s3 version: 1.0.0 diff --git a/include/timezone_data.hpp b/include/timezone_data.hpp deleted file mode 100644 index 1ebb5db..0000000 --- a/include/timezone_data.hpp +++ /dev/null @@ -1,721 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace timezone_data { - -// Enum for unique timezone strings -enum class TimezoneValue : uint8_t { - plus000plus02_2M350_1M1050_3, - plus01_1, - plus02_2, - plus0330_330, - plus03_3, - plus0430_430, - plus04_4, - plus0530_530, - plus0545_545, - plus05_5, - plus0630_630, - plus06_6, - plus07_7, - plus0845_845, - plus08_8, - plus09_9, - plus1030_1030plus11_11M1010M410, - plus10_10, - plus11_11, - plus11_11plus12M1010M410_3, - plus1245_1245plus1345M950_245M410_345, - plus12_12, - plus13_13, - plus14_14, - _011, - _011plus00M350_0M1050_1, - _022, - _022_01M350__1M1050_0, - _033, - _033_02M320M1110, - _044, - _044_03M1010_0M340_0, - _044_03M916_24M416_24, - _055, - _066, - _066_05M916_22M416_22, - _077, - _088, - _0930930, - _099, - _1010, - _1111, - _1212, - ACST_930, - ACST_930ACDTM1010M410_3, - AEST_10, - AEST_10AEDTM1010M410_3, - AKST9AKDTM320M1110, - AST4, - AST4ADTM320M1110, - AWST_8, - CAT_2, - CET_1, - CET_1CESTM350M1050_3, - CST_8, - CST5CDTM320_0M1110_1, - CST6, - CST6CDTM320M1110, - ChST_10, - EAT_3, - EET_2, - EET_2EESTM344_50M1044_50, - EET_2EESTM350M1050_3, - EET_2EESTM350_0M1050_0, - EET_2EESTM350_3M1050_4, - EET_2EESTM455_0M1054_24, - EST5, - EST5EDTM320M1110, - GMT0, - GMT0BSTM350_1M1050, - HKT_8, - HST10, - HST10HDTM320M1110, - IST_1GMT0M1050M350_1, - IST_2IDTM344_26M1050, - IST_530, - JST_9, - KST_9, - MSK_3, - MST7, - MST7MDTM320M1110, - NST330NDTM320M1110, - NZST_12NZDTM950M410_3, - PKT_5, - PST_8, - PST8PDTM320M1110, - SAST_2, - SST11, - UTC0, - WAT_1, - WET0WESTM350_1M1050, - WIB_7, - WIT_9, - WITA_8, -}; - -// Key-value pair type -using TimezoneEntry = std::pair; - -// Lookup table -constexpr std::array TIMEZONE_DATA = {{ - {"Africa/Abidjan", TimezoneValue::GMT0}, - {"Africa/Accra", TimezoneValue::GMT0}, - {"Africa/Addis_Ababa", TimezoneValue::EAT_3}, - {"Africa/Algiers", TimezoneValue::CET_1}, - {"Africa/Asmara", TimezoneValue::EAT_3}, - {"Africa/Bamako", TimezoneValue::GMT0}, - {"Africa/Bangui", TimezoneValue::WAT_1}, - {"Africa/Banjul", TimezoneValue::GMT0}, - {"Africa/Bissau", TimezoneValue::GMT0}, - {"Africa/Blantyre", TimezoneValue::CAT_2}, - {"Africa/Brazzaville", TimezoneValue::WAT_1}, - {"Africa/Bujumbura", TimezoneValue::CAT_2}, - {"Africa/Cairo", TimezoneValue::EET_2EESTM455_0M1054_24}, - {"Africa/Casablanca", TimezoneValue::plus01_1}, - {"Africa/Ceuta", TimezoneValue::CET_1CESTM350M1050_3}, - {"Africa/Conakry", TimezoneValue::GMT0}, - {"Africa/Dakar", TimezoneValue::GMT0}, - {"Africa/Dar_es_Salaam", TimezoneValue::EAT_3}, - {"Africa/Djibouti", TimezoneValue::EAT_3}, - {"Africa/Douala", TimezoneValue::WAT_1}, - {"Africa/El_Aaiun", TimezoneValue::plus01_1}, - {"Africa/Freetown", TimezoneValue::GMT0}, - {"Africa/Gaborone", TimezoneValue::CAT_2}, - {"Africa/Harare", TimezoneValue::CAT_2}, - {"Africa/Johannesburg", TimezoneValue::SAST_2}, - {"Africa/Juba", TimezoneValue::CAT_2}, - {"Africa/Kampala", TimezoneValue::EAT_3}, - {"Africa/Khartoum", TimezoneValue::CAT_2}, - {"Africa/Kigali", TimezoneValue::CAT_2}, - {"Africa/Kinshasa", TimezoneValue::WAT_1}, - {"Africa/Lagos", TimezoneValue::WAT_1}, - {"Africa/Libreville", TimezoneValue::WAT_1}, - {"Africa/Lome", TimezoneValue::GMT0}, - {"Africa/Luanda", TimezoneValue::WAT_1}, - {"Africa/Lubumbashi", TimezoneValue::CAT_2}, - {"Africa/Lusaka", TimezoneValue::CAT_2}, - {"Africa/Malabo", TimezoneValue::WAT_1}, - {"Africa/Maputo", TimezoneValue::CAT_2}, - {"Africa/Maseru", TimezoneValue::SAST_2}, - {"Africa/Mbabane", TimezoneValue::SAST_2}, - {"Africa/Mogadishu", TimezoneValue::EAT_3}, - {"Africa/Monrovia", TimezoneValue::GMT0}, - {"Africa/Nairobi", TimezoneValue::EAT_3}, - {"Africa/Ndjamena", TimezoneValue::WAT_1}, - {"Africa/Niamey", TimezoneValue::WAT_1}, - {"Africa/Nouakchott", TimezoneValue::GMT0}, - {"Africa/Ouagadougou", TimezoneValue::GMT0}, - {"Africa/Porto-Novo", TimezoneValue::WAT_1}, - {"Africa/Sao_Tome", TimezoneValue::GMT0}, - {"Africa/Tripoli", TimezoneValue::EET_2}, - {"Africa/Tunis", TimezoneValue::CET_1}, - {"Africa/Windhoek", TimezoneValue::CAT_2}, - {"America/Adak", TimezoneValue::HST10HDTM320M1110}, - {"America/Anchorage", TimezoneValue::AKST9AKDTM320M1110}, - {"America/Anguilla", TimezoneValue::AST4}, - {"America/Antigua", TimezoneValue::AST4}, - {"America/Araguaina", TimezoneValue::_033}, - {"America/Argentina/Buenos_Aires", TimezoneValue::_033}, - {"America/Argentina/Catamarca", TimezoneValue::_033}, - {"America/Argentina/Cordoba", TimezoneValue::_033}, - {"America/Argentina/Jujuy", TimezoneValue::_033}, - {"America/Argentina/La_Rioja", TimezoneValue::_033}, - {"America/Argentina/Mendoza", TimezoneValue::_033}, - {"America/Argentina/Rio_Gallegos", TimezoneValue::_033}, - {"America/Argentina/Salta", TimezoneValue::_033}, - {"America/Argentina/San_Juan", TimezoneValue::_033}, - {"America/Argentina/San_Luis", TimezoneValue::_033}, - {"America/Argentina/Tucuman", TimezoneValue::_033}, - {"America/Argentina/Ushuaia", TimezoneValue::_033}, - {"America/Aruba", TimezoneValue::AST4}, - {"America/Asuncion", TimezoneValue::_044_03M1010_0M340_0}, - {"America/Atikokan", TimezoneValue::EST5}, - {"America/Bahia", TimezoneValue::_033}, - {"America/Bahia_Banderas", TimezoneValue::CST6}, - {"America/Barbados", TimezoneValue::AST4}, - {"America/Belem", TimezoneValue::_033}, - {"America/Belize", TimezoneValue::CST6}, - {"America/Blanc-Sablon", TimezoneValue::AST4}, - {"America/Boa_Vista", TimezoneValue::_044}, - {"America/Bogota", TimezoneValue::_055}, - {"America/Boise", TimezoneValue::MST7MDTM320M1110}, - {"America/Cambridge_Bay", TimezoneValue::MST7MDTM320M1110}, - {"America/Campo_Grande", TimezoneValue::_044}, - {"America/Cancun", TimezoneValue::EST5}, - {"America/Caracas", TimezoneValue::_044}, - {"America/Cayenne", TimezoneValue::_033}, - {"America/Cayman", TimezoneValue::EST5}, - {"America/Chicago", TimezoneValue::CST6CDTM320M1110}, - {"America/Chihuahua", TimezoneValue::CST6}, - {"America/Costa_Rica", TimezoneValue::CST6}, - {"America/Creston", TimezoneValue::MST7}, - {"America/Cuiaba", TimezoneValue::_044}, - {"America/Curacao", TimezoneValue::AST4}, - {"America/Danmarkshavn", TimezoneValue::GMT0}, - {"America/Dawson", TimezoneValue::MST7}, - {"America/Dawson_Creek", TimezoneValue::MST7}, - {"America/Denver", TimezoneValue::MST7MDTM320M1110}, - {"America/Detroit", TimezoneValue::EST5EDTM320M1110}, - {"America/Dominica", TimezoneValue::AST4}, - {"America/Edmonton", TimezoneValue::MST7MDTM320M1110}, - {"America/Eirunepe", TimezoneValue::_055}, - {"America/El_Salvador", TimezoneValue::CST6}, - {"America/Fort_Nelson", TimezoneValue::MST7}, - {"America/Fortaleza", TimezoneValue::_033}, - {"America/Glace_Bay", TimezoneValue::AST4ADTM320M1110}, - {"America/Godthab", TimezoneValue::_022_01M350__1M1050_0}, - {"America/Goose_Bay", TimezoneValue::AST4ADTM320M1110}, - {"America/Grand_Turk", TimezoneValue::EST5EDTM320M1110}, - {"America/Grenada", TimezoneValue::AST4}, - {"America/Guadeloupe", TimezoneValue::AST4}, - {"America/Guatemala", TimezoneValue::CST6}, - {"America/Guayaquil", TimezoneValue::_055}, - {"America/Guyana", TimezoneValue::_044}, - {"America/Halifax", TimezoneValue::AST4ADTM320M1110}, - {"America/Havana", TimezoneValue::CST5CDTM320_0M1110_1}, - {"America/Hermosillo", TimezoneValue::MST7}, - {"America/Indiana/Indianapolis", TimezoneValue::EST5EDTM320M1110}, - {"America/Indiana/Knox", TimezoneValue::CST6CDTM320M1110}, - {"America/Indiana/Marengo", TimezoneValue::EST5EDTM320M1110}, - {"America/Indiana/Petersburg", TimezoneValue::EST5EDTM320M1110}, - {"America/Indiana/Tell_City", TimezoneValue::CST6CDTM320M1110}, - {"America/Indiana/Vevay", TimezoneValue::EST5EDTM320M1110}, - {"America/Indiana/Vincennes", TimezoneValue::EST5EDTM320M1110}, - {"America/Indiana/Winamac", TimezoneValue::EST5EDTM320M1110}, - {"America/Inuvik", TimezoneValue::MST7MDTM320M1110}, - {"America/Iqaluit", TimezoneValue::EST5EDTM320M1110}, - {"America/Jamaica", TimezoneValue::EST5}, - {"America/Juneau", TimezoneValue::AKST9AKDTM320M1110}, - {"America/Kentucky/Louisville", TimezoneValue::EST5EDTM320M1110}, - {"America/Kentucky/Monticello", TimezoneValue::EST5EDTM320M1110}, - {"America/Kralendijk", TimezoneValue::AST4}, - {"America/La_Paz", TimezoneValue::_044}, - {"America/Lima", TimezoneValue::_055}, - {"America/Los_Angeles", TimezoneValue::PST8PDTM320M1110}, - {"America/Lower_Princes", TimezoneValue::AST4}, - {"America/Maceio", TimezoneValue::_033}, - {"America/Managua", TimezoneValue::CST6}, - {"America/Manaus", TimezoneValue::_044}, - {"America/Marigot", TimezoneValue::AST4}, - {"America/Martinique", TimezoneValue::AST4}, - {"America/Matamoros", TimezoneValue::CST6CDTM320M1110}, - {"America/Mazatlan", TimezoneValue::MST7}, - {"America/Menominee", TimezoneValue::CST6CDTM320M1110}, - {"America/Merida", TimezoneValue::CST6}, - {"America/Metlakatla", TimezoneValue::AKST9AKDTM320M1110}, - {"America/Mexico_City", TimezoneValue::CST6}, - {"America/Miquelon", TimezoneValue::_033_02M320M1110}, - {"America/Moncton", TimezoneValue::AST4ADTM320M1110}, - {"America/Monterrey", TimezoneValue::CST6}, - {"America/Montevideo", TimezoneValue::_033}, - {"America/Montreal", TimezoneValue::EST5EDTM320M1110}, - {"America/Montserrat", TimezoneValue::AST4}, - {"America/Nassau", TimezoneValue::EST5EDTM320M1110}, - {"America/New_York", TimezoneValue::EST5EDTM320M1110}, - {"America/Nipigon", TimezoneValue::EST5EDTM320M1110}, - {"America/Nome", TimezoneValue::AKST9AKDTM320M1110}, - {"America/Noronha", TimezoneValue::_022}, - {"America/North_Dakota/Beulah", TimezoneValue::CST6CDTM320M1110}, - {"America/North_Dakota/Center", TimezoneValue::CST6CDTM320M1110}, - {"America/North_Dakota/New_Salem", TimezoneValue::CST6CDTM320M1110}, - {"America/Nuuk", TimezoneValue::_022_01M350__1M1050_0}, - {"America/Ojinaga", TimezoneValue::CST6CDTM320M1110}, - {"America/Panama", TimezoneValue::EST5}, - {"America/Pangnirtung", TimezoneValue::EST5EDTM320M1110}, - {"America/Paramaribo", TimezoneValue::_033}, - {"America/Phoenix", TimezoneValue::MST7}, - {"America/Port-au-Prince", TimezoneValue::EST5EDTM320M1110}, - {"America/Port_of_Spain", TimezoneValue::AST4}, - {"America/Porto_Velho", TimezoneValue::_044}, - {"America/Puerto_Rico", TimezoneValue::AST4}, - {"America/Punta_Arenas", TimezoneValue::_033}, - {"America/Rainy_River", TimezoneValue::CST6CDTM320M1110}, - {"America/Rankin_Inlet", TimezoneValue::CST6CDTM320M1110}, - {"America/Recife", TimezoneValue::_033}, - {"America/Regina", TimezoneValue::CST6}, - {"America/Resolute", TimezoneValue::CST6CDTM320M1110}, - {"America/Rio_Branco", TimezoneValue::_055}, - {"America/Santarem", TimezoneValue::_033}, - {"America/Santiago", TimezoneValue::_044_03M916_24M416_24}, - {"America/Santo_Domingo", TimezoneValue::AST4}, - {"America/Sao_Paulo", TimezoneValue::_033}, - {"America/Scoresbysund", TimezoneValue::_022_01M350__1M1050_0}, - {"America/Sitka", TimezoneValue::AKST9AKDTM320M1110}, - {"America/St_Barthelemy", TimezoneValue::AST4}, - {"America/St_Johns", TimezoneValue::NST330NDTM320M1110}, - {"America/St_Kitts", TimezoneValue::AST4}, - {"America/St_Lucia", TimezoneValue::AST4}, - {"America/St_Thomas", TimezoneValue::AST4}, - {"America/St_Vincent", TimezoneValue::AST4}, - {"America/Swift_Current", TimezoneValue::CST6}, - {"America/Tegucigalpa", TimezoneValue::CST6}, - {"America/Thule", TimezoneValue::AST4ADTM320M1110}, - {"America/Thunder_Bay", TimezoneValue::EST5EDTM320M1110}, - {"America/Tijuana", TimezoneValue::PST8PDTM320M1110}, - {"America/Toronto", TimezoneValue::EST5EDTM320M1110}, - {"America/Tortola", TimezoneValue::AST4}, - {"America/Vancouver", TimezoneValue::PST8PDTM320M1110}, - {"America/Whitehorse", TimezoneValue::MST7}, - {"America/Winnipeg", TimezoneValue::CST6CDTM320M1110}, - {"America/Yakutat", TimezoneValue::AKST9AKDTM320M1110}, - {"America/Yellowknife", TimezoneValue::MST7MDTM320M1110}, - {"Antarctica/Casey", TimezoneValue::plus08_8}, - {"Antarctica/Davis", TimezoneValue::plus07_7}, - {"Antarctica/DumontDUrville", TimezoneValue::plus10_10}, - {"Antarctica/Macquarie", TimezoneValue::AEST_10AEDTM1010M410_3}, - {"Antarctica/Mawson", TimezoneValue::plus05_5}, - {"Antarctica/McMurdo", TimezoneValue::NZST_12NZDTM950M410_3}, - {"Antarctica/Palmer", TimezoneValue::_033}, - {"Antarctica/Rothera", TimezoneValue::_033}, - {"Antarctica/Syowa", TimezoneValue::plus03_3}, - {"Antarctica/Troll", TimezoneValue::plus000plus02_2M350_1M1050_3}, - {"Antarctica/Vostok", TimezoneValue::plus05_5}, - {"Arctic/Longyearbyen", TimezoneValue::CET_1CESTM350M1050_3}, - {"Asia/Aden", TimezoneValue::plus03_3}, - {"Asia/Almaty", TimezoneValue::plus05_5}, - {"Asia/Amman", TimezoneValue::plus03_3}, - {"Asia/Anadyr", TimezoneValue::plus12_12}, - {"Asia/Aqtau", TimezoneValue::plus05_5}, - {"Asia/Aqtobe", TimezoneValue::plus05_5}, - {"Asia/Ashgabat", TimezoneValue::plus05_5}, - {"Asia/Atyrau", TimezoneValue::plus05_5}, - {"Asia/Baghdad", TimezoneValue::plus03_3}, - {"Asia/Bahrain", TimezoneValue::plus03_3}, - {"Asia/Baku", TimezoneValue::plus04_4}, - {"Asia/Bangkok", TimezoneValue::plus07_7}, - {"Asia/Barnaul", TimezoneValue::plus07_7}, - {"Asia/Beirut", TimezoneValue::EET_2EESTM350_0M1050_0}, - {"Asia/Bishkek", TimezoneValue::plus06_6}, - {"Asia/Brunei", TimezoneValue::plus08_8}, - {"Asia/Chita", TimezoneValue::plus09_9}, - {"Asia/Choibalsan", TimezoneValue::plus08_8}, - {"Asia/Colombo", TimezoneValue::plus0530_530}, - {"Asia/Damascus", TimezoneValue::plus03_3}, - {"Asia/Dhaka", TimezoneValue::plus06_6}, - {"Asia/Dili", TimezoneValue::plus09_9}, - {"Asia/Dubai", TimezoneValue::plus04_4}, - {"Asia/Dushanbe", TimezoneValue::plus05_5}, - {"Asia/Famagusta", TimezoneValue::EET_2EESTM350_3M1050_4}, - {"Asia/Gaza", TimezoneValue::EET_2EESTM344_50M1044_50}, - {"Asia/Hebron", TimezoneValue::EET_2EESTM344_50M1044_50}, - {"Asia/Ho_Chi_Minh", TimezoneValue::plus07_7}, - {"Asia/Hong_Kong", TimezoneValue::HKT_8}, - {"Asia/Hovd", TimezoneValue::plus07_7}, - {"Asia/Irkutsk", TimezoneValue::plus08_8}, - {"Asia/Jakarta", TimezoneValue::WIB_7}, - {"Asia/Jayapura", TimezoneValue::WIT_9}, - {"Asia/Jerusalem", TimezoneValue::IST_2IDTM344_26M1050}, - {"Asia/Kabul", TimezoneValue::plus0430_430}, - {"Asia/Kamchatka", TimezoneValue::plus12_12}, - {"Asia/Karachi", TimezoneValue::PKT_5}, - {"Asia/Kathmandu", TimezoneValue::plus0545_545}, - {"Asia/Khandyga", TimezoneValue::plus09_9}, - {"Asia/Kolkata", TimezoneValue::IST_530}, - {"Asia/Krasnoyarsk", TimezoneValue::plus07_7}, - {"Asia/Kuala_Lumpur", TimezoneValue::plus08_8}, - {"Asia/Kuching", TimezoneValue::plus08_8}, - {"Asia/Kuwait", TimezoneValue::plus03_3}, - {"Asia/Macau", TimezoneValue::CST_8}, - {"Asia/Magadan", TimezoneValue::plus11_11}, - {"Asia/Makassar", TimezoneValue::WITA_8}, - {"Asia/Manila", TimezoneValue::PST_8}, - {"Asia/Muscat", TimezoneValue::plus04_4}, - {"Asia/Nicosia", TimezoneValue::EET_2EESTM350_3M1050_4}, - {"Asia/Novokuznetsk", TimezoneValue::plus07_7}, - {"Asia/Novosibirsk", TimezoneValue::plus07_7}, - {"Asia/Omsk", TimezoneValue::plus06_6}, - {"Asia/Oral", TimezoneValue::plus05_5}, - {"Asia/Phnom_Penh", TimezoneValue::plus07_7}, - {"Asia/Pontianak", TimezoneValue::WIB_7}, - {"Asia/Pyongyang", TimezoneValue::KST_9}, - {"Asia/Qatar", TimezoneValue::plus03_3}, - {"Asia/Qyzylorda", TimezoneValue::plus05_5}, - {"Asia/Riyadh", TimezoneValue::plus03_3}, - {"Asia/Sakhalin", TimezoneValue::plus11_11}, - {"Asia/Samarkand", TimezoneValue::plus05_5}, - {"Asia/Seoul", TimezoneValue::KST_9}, - {"Asia/Shanghai", TimezoneValue::CST_8}, - {"Asia/Singapore", TimezoneValue::plus08_8}, - {"Asia/Srednekolymsk", TimezoneValue::plus11_11}, - {"Asia/Taipei", TimezoneValue::CST_8}, - {"Asia/Tashkent", TimezoneValue::plus05_5}, - {"Asia/Tbilisi", TimezoneValue::plus04_4}, - {"Asia/Tehran", TimezoneValue::plus0330_330}, - {"Asia/Thimphu", TimezoneValue::plus06_6}, - {"Asia/Tokyo", TimezoneValue::JST_9}, - {"Asia/Tomsk", TimezoneValue::plus07_7}, - {"Asia/Ulaanbaatar", TimezoneValue::plus08_8}, - {"Asia/Urumqi", TimezoneValue::plus06_6}, - {"Asia/Ust-Nera", TimezoneValue::plus10_10}, - {"Asia/Vientiane", TimezoneValue::plus07_7}, - {"Asia/Vladivostok", TimezoneValue::plus10_10}, - {"Asia/Yakutsk", TimezoneValue::plus09_9}, - {"Asia/Yangon", TimezoneValue::plus0630_630}, - {"Asia/Yekaterinburg", TimezoneValue::plus05_5}, - {"Asia/Yerevan", TimezoneValue::plus04_4}, - {"Atlantic/Azores", TimezoneValue::_011plus00M350_0M1050_1}, - {"Atlantic/Bermuda", TimezoneValue::AST4ADTM320M1110}, - {"Atlantic/Canary", TimezoneValue::WET0WESTM350_1M1050}, - {"Atlantic/Cape_Verde", TimezoneValue::_011}, - {"Atlantic/Faroe", TimezoneValue::WET0WESTM350_1M1050}, - {"Atlantic/Madeira", TimezoneValue::WET0WESTM350_1M1050}, - {"Atlantic/Reykjavik", TimezoneValue::GMT0}, - {"Atlantic/South_Georgia", TimezoneValue::_022}, - {"Atlantic/St_Helena", TimezoneValue::GMT0}, - {"Atlantic/Stanley", TimezoneValue::_033}, - {"Australia/Adelaide", TimezoneValue::ACST_930ACDTM1010M410_3}, - {"Australia/Brisbane", TimezoneValue::AEST_10}, - {"Australia/Broken_Hill", TimezoneValue::ACST_930ACDTM1010M410_3}, - {"Australia/Currie", TimezoneValue::AEST_10AEDTM1010M410_3}, - {"Australia/Darwin", TimezoneValue::ACST_930}, - {"Australia/Eucla", TimezoneValue::plus0845_845}, - {"Australia/Hobart", TimezoneValue::AEST_10AEDTM1010M410_3}, - {"Australia/Lindeman", TimezoneValue::AEST_10}, - {"Australia/Lord_Howe", TimezoneValue::plus1030_1030plus11_11M1010M410}, - {"Australia/Melbourne", TimezoneValue::AEST_10AEDTM1010M410_3}, - {"Australia/Perth", TimezoneValue::AWST_8}, - {"Australia/Sydney", TimezoneValue::AEST_10AEDTM1010M410_3}, - {"Etc/GMT", TimezoneValue::GMT0}, - {"Etc/GMT+0", TimezoneValue::GMT0}, - {"Etc/GMT+1", TimezoneValue::_011}, - {"Etc/GMT+10", TimezoneValue::_1010}, - {"Etc/GMT+11", TimezoneValue::_1111}, - {"Etc/GMT+12", TimezoneValue::_1212}, - {"Etc/GMT+2", TimezoneValue::_022}, - {"Etc/GMT+3", TimezoneValue::_033}, - {"Etc/GMT+4", TimezoneValue::_044}, - {"Etc/GMT+5", TimezoneValue::_055}, - {"Etc/GMT+6", TimezoneValue::_066}, - {"Etc/GMT+7", TimezoneValue::_077}, - {"Etc/GMT+8", TimezoneValue::_088}, - {"Etc/GMT+9", TimezoneValue::_099}, - {"Etc/GMT-0", TimezoneValue::GMT0}, - {"Etc/GMT-1", TimezoneValue::plus01_1}, - {"Etc/GMT-10", TimezoneValue::plus10_10}, - {"Etc/GMT-11", TimezoneValue::plus11_11}, - {"Etc/GMT-12", TimezoneValue::plus12_12}, - {"Etc/GMT-13", TimezoneValue::plus13_13}, - {"Etc/GMT-14", TimezoneValue::plus14_14}, - {"Etc/GMT-2", TimezoneValue::plus02_2}, - {"Etc/GMT-3", TimezoneValue::plus03_3}, - {"Etc/GMT-4", TimezoneValue::plus04_4}, - {"Etc/GMT-5", TimezoneValue::plus05_5}, - {"Etc/GMT-6", TimezoneValue::plus06_6}, - {"Etc/GMT-7", TimezoneValue::plus07_7}, - {"Etc/GMT-8", TimezoneValue::plus08_8}, - {"Etc/GMT-9", TimezoneValue::plus09_9}, - {"Etc/GMT0", TimezoneValue::GMT0}, - {"Etc/Greenwich", TimezoneValue::GMT0}, - {"Etc/UCT", TimezoneValue::UTC0}, - {"Etc/UTC", TimezoneValue::UTC0}, - {"Etc/Universal", TimezoneValue::UTC0}, - {"Etc/Zulu", TimezoneValue::UTC0}, - {"Europe/Amsterdam", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Andorra", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Astrakhan", TimezoneValue::plus04_4}, - {"Europe/Athens", TimezoneValue::EET_2EESTM350_3M1050_4}, - {"Europe/Belgrade", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Berlin", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Bratislava", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Brussels", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Bucharest", TimezoneValue::EET_2EESTM350_3M1050_4}, - {"Europe/Budapest", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Busingen", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Chisinau", TimezoneValue::EET_2EESTM350M1050_3}, - {"Europe/Copenhagen", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Dublin", TimezoneValue::IST_1GMT0M1050M350_1}, - {"Europe/Gibraltar", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Guernsey", TimezoneValue::GMT0BSTM350_1M1050}, - {"Europe/Helsinki", TimezoneValue::EET_2EESTM350_3M1050_4}, - {"Europe/Isle_of_Man", TimezoneValue::GMT0BSTM350_1M1050}, - {"Europe/Istanbul", TimezoneValue::plus03_3}, - {"Europe/Jersey", TimezoneValue::GMT0BSTM350_1M1050}, - {"Europe/Kaliningrad", TimezoneValue::EET_2}, - {"Europe/Kiev", TimezoneValue::EET_2EESTM350_3M1050_4}, - {"Europe/Kirov", TimezoneValue::MSK_3}, - {"Europe/Lisbon", TimezoneValue::WET0WESTM350_1M1050}, - {"Europe/Ljubljana", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/London", TimezoneValue::GMT0BSTM350_1M1050}, - {"Europe/Luxembourg", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Madrid", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Malta", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Mariehamn", TimezoneValue::EET_2EESTM350_3M1050_4}, - {"Europe/Minsk", TimezoneValue::plus03_3}, - {"Europe/Monaco", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Moscow", TimezoneValue::MSK_3}, - {"Europe/Oslo", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Paris", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Podgorica", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Prague", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Riga", TimezoneValue::EET_2EESTM350_3M1050_4}, - {"Europe/Rome", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Samara", TimezoneValue::plus04_4}, - {"Europe/San_Marino", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Sarajevo", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Saratov", TimezoneValue::plus04_4}, - {"Europe/Simferopol", TimezoneValue::MSK_3}, - {"Europe/Skopje", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Sofia", TimezoneValue::EET_2EESTM350_3M1050_4}, - {"Europe/Stockholm", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Tallinn", TimezoneValue::EET_2EESTM350_3M1050_4}, - {"Europe/Tirane", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Ulyanovsk", TimezoneValue::plus04_4}, - {"Europe/Uzhgorod", TimezoneValue::EET_2EESTM350_3M1050_4}, - {"Europe/Vaduz", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Vatican", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Vienna", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Vilnius", TimezoneValue::EET_2EESTM350_3M1050_4}, - {"Europe/Volgograd", TimezoneValue::MSK_3}, - {"Europe/Warsaw", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Zagreb", TimezoneValue::CET_1CESTM350M1050_3}, - {"Europe/Zaporozhye", TimezoneValue::EET_2EESTM350_3M1050_4}, - {"Europe/Zurich", TimezoneValue::CET_1CESTM350M1050_3}, - {"Indian/Antananarivo", TimezoneValue::EAT_3}, - {"Indian/Chagos", TimezoneValue::plus06_6}, - {"Indian/Christmas", TimezoneValue::plus07_7}, - {"Indian/Cocos", TimezoneValue::plus0630_630}, - {"Indian/Comoro", TimezoneValue::EAT_3}, - {"Indian/Kerguelen", TimezoneValue::plus05_5}, - {"Indian/Mahe", TimezoneValue::plus04_4}, - {"Indian/Maldives", TimezoneValue::plus05_5}, - {"Indian/Mauritius", TimezoneValue::plus04_4}, - {"Indian/Mayotte", TimezoneValue::EAT_3}, - {"Indian/Reunion", TimezoneValue::plus04_4}, - {"Pacific/Apia", TimezoneValue::plus13_13}, - {"Pacific/Auckland", TimezoneValue::NZST_12NZDTM950M410_3}, - {"Pacific/Bougainville", TimezoneValue::plus11_11}, - {"Pacific/Chatham", TimezoneValue::plus1245_1245plus1345M950_245M410_345}, - {"Pacific/Chuuk", TimezoneValue::plus10_10}, - {"Pacific/Easter", TimezoneValue::_066_05M916_22M416_22}, - {"Pacific/Efate", TimezoneValue::plus11_11}, - {"Pacific/Enderbury", TimezoneValue::plus13_13}, - {"Pacific/Fakaofo", TimezoneValue::plus13_13}, - {"Pacific/Fiji", TimezoneValue::plus12_12}, - {"Pacific/Funafuti", TimezoneValue::plus12_12}, - {"Pacific/Galapagos", TimezoneValue::_066}, - {"Pacific/Gambier", TimezoneValue::_099}, - {"Pacific/Guadalcanal", TimezoneValue::plus11_11}, - {"Pacific/Guam", TimezoneValue::ChST_10}, - {"Pacific/Honolulu", TimezoneValue::HST10}, - {"Pacific/Kiritimati", TimezoneValue::plus14_14}, - {"Pacific/Kosrae", TimezoneValue::plus11_11}, - {"Pacific/Kwajalein", TimezoneValue::plus12_12}, - {"Pacific/Majuro", TimezoneValue::plus12_12}, - {"Pacific/Marquesas", TimezoneValue::_0930930}, - {"Pacific/Midway", TimezoneValue::SST11}, - {"Pacific/Nauru", TimezoneValue::plus12_12}, - {"Pacific/Niue", TimezoneValue::_1111}, - {"Pacific/Norfolk", TimezoneValue::plus11_11plus12M1010M410_3}, - {"Pacific/Noumea", TimezoneValue::plus11_11}, - {"Pacific/Pago_Pago", TimezoneValue::SST11}, - {"Pacific/Palau", TimezoneValue::plus09_9}, - {"Pacific/Pitcairn", TimezoneValue::_088}, - {"Pacific/Pohnpei", TimezoneValue::plus11_11}, - {"Pacific/Port_Moresby", TimezoneValue::plus10_10}, - {"Pacific/Rarotonga", TimezoneValue::_1010}, - {"Pacific/Saipan", TimezoneValue::ChST_10}, - {"Pacific/Tahiti", TimezoneValue::_1010}, - {"Pacific/Tarawa", TimezoneValue::plus12_12}, - {"Pacific/Tongatapu", TimezoneValue::plus13_13}, - {"Pacific/Wake", TimezoneValue::plus12_12}, - {"Pacific/Wallis", TimezoneValue::plus12_12}, -}}; - -// Helper function to find timezone value -inline TimezoneValue find_timezone_value(std::string_view key) { - for (const auto& entry : TIMEZONE_DATA) { - if (entry.first == key) { - return entry.second; - } - } - return TimezoneValue::plus000plus02_2M350_1M1050_3; // Default fallback -} - -// Overload for String -inline TimezoneValue find_timezone_value(const String& key) { - return find_timezone_value(std::string_view(key.c_str())); -} - -// Overload for std::string -inline TimezoneValue find_timezone_value(const std::string& key) { - return find_timezone_value(std::string_view(key)); -} - -// Helper function to convert TimezoneValue to string representation -inline String get_timezone_string(TimezoneValue value) { - for (const auto& entry : TIMEZONE_DATA) { - if (entry.second == value) { - return String(entry.first.data(), entry.first.length()); - } - } - return String("GMT0"); // Default fallback -} - -// Overload for std::string -inline std::string get_timezone_string_std(TimezoneValue value) { - for (const auto& entry : TIMEZONE_DATA) { - if (entry.second == value) { - return std::string(entry.first.data(), entry.first.length()); - } - } - return "GMT0"; // Default fallback -} - -// Helper function to convert TimezoneValue enum to its string representation -inline String get_timezone_value_string(TimezoneValue value) { - switch (value) { - case TimezoneValue::plus000plus02_2M350_1M1050_3: return String("+00:00+02:00,M3.5.0/1,M10.5.0/3"); - case TimezoneValue::plus01_1: return String("+01:00"); - case TimezoneValue::plus02_2: return String("+02:00"); - case TimezoneValue::plus0330_330: return String("+03:30"); - case TimezoneValue::plus03_3: return String("+03:00"); - case TimezoneValue::plus0430_430: return String("+04:30"); - case TimezoneValue::plus04_4: return String("+04:00"); - case TimezoneValue::plus0530_530: return String("+05:30"); - case TimezoneValue::plus0545_545: return String("+05:45"); - case TimezoneValue::plus05_5: return String("+05:00"); - case TimezoneValue::plus0630_630: return String("+06:30"); - case TimezoneValue::plus06_6: return String("+06:00"); - case TimezoneValue::plus07_7: return String("+07:00"); - case TimezoneValue::plus0845_845: return String("+08:45"); - case TimezoneValue::plus08_8: return String("+08:00"); - case TimezoneValue::plus09_9: return String("+09:00"); - case TimezoneValue::plus1030_1030plus11_11M1010M410: return String("+10:30+11:00,M10.1.0,M4.1.0"); - case TimezoneValue::plus10_10: return String("+10:00"); - case TimezoneValue::plus11_11: return String("+11:00"); - case TimezoneValue::plus11_11plus12M1010M410_3: return String("+11:00+12:00,M10.1.0,M4.1.0/3"); - case TimezoneValue::plus1245_1245plus1345M950_245M410_345: return String("+12:45+13:45,M9.5.0/2:45,M4.1.0/3:45"); - case TimezoneValue::plus12_12: return String("+12:00"); - case TimezoneValue::plus13_13: return String("+13:00"); - case TimezoneValue::plus14_14: return String("+14:00"); - case TimezoneValue::_011: return String("-01:00"); - case TimezoneValue::_011plus00M350_0M1050_1: return String("-01:00+00:00,M3.5.0/0,M10.5.0/1"); - case TimezoneValue::_022: return String("-02:00"); - case TimezoneValue::_022_01M350__1M1050_0: return String("-02:00-01:00,M3.5.0/-1,M10.5.0/0"); - case TimezoneValue::_033: return String("-03:00"); - case TimezoneValue::_033_02M320M1110: return String("-03:00-02:00,M3.2.0,M11.1.0"); - case TimezoneValue::_044: return String("-04:00"); - case TimezoneValue::_044_03M1010_0M340_0: return String("-04:00-03:00,M10.1.0/0,M3.4.0/0"); - case TimezoneValue::_044_03M916_24M416_24: return String("-04:00-03:00,M9.1.6/24,M4.1.6/24"); - case TimezoneValue::_055: return String("-05:00"); - case TimezoneValue::_066: return String("-06:00"); - case TimezoneValue::_066_05M916_22M416_22: return String("-06:00-05:00,M9.1.6/22,M4.1.6/22"); - case TimezoneValue::_077: return String("-07:00"); - case TimezoneValue::_088: return String("-08:00"); - case TimezoneValue::_0930930: return String("-09:30"); - case TimezoneValue::_099: return String("-09:00"); - case TimezoneValue::_1010: return String("-10:00"); - case TimezoneValue::_1111: return String("-11:00"); - case TimezoneValue::_1212: return String("-12:00"); - case TimezoneValue::ACST_930: return String("ACST-9:30"); - case TimezoneValue::ACST_930ACDTM1010M410_3: return String("ACST-9:30ACDT,M10.1.0,M4.1.0/3"); - case TimezoneValue::AEST_10: return String("AEST-10"); - case TimezoneValue::AEST_10AEDTM1010M410_3: return String("AEST-10AEDT,M10.1.0,M4.1.0/3"); - case TimezoneValue::AKST9AKDTM320M1110: return String("AKST9AKDT,M3.2.0,M11.1.0"); - case TimezoneValue::AST4: return String("AST4"); - case TimezoneValue::AST4ADTM320M1110: return String("AST4ADT,M3.2.0,M11.1.0"); - case TimezoneValue::AWST_8: return String("AWST-8"); - case TimezoneValue::CAT_2: return String("CAT-2"); - case TimezoneValue::CET_1: return String("CET-1"); - case TimezoneValue::CET_1CESTM350M1050_3: return String("CET-1CEST,M3.5.0,M10.5.0/3"); - case TimezoneValue::CST_8: return String("CST-8"); - case TimezoneValue::CST5CDTM320_0M1110_1: return String("CST5CDT,M3.2.0/0,M11.1.0/1"); - case TimezoneValue::CST6: return String("CST6"); - case TimezoneValue::CST6CDTM320M1110: return String("CST6CDT,M3.2.0,M11.1.0"); - case TimezoneValue::ChST_10: return String("ChST-10"); - case TimezoneValue::EAT_3: return String("EAT-3"); - case TimezoneValue::EET_2: return String("EET-2"); - case TimezoneValue::EET_2EESTM344_50M1044_50: return String("EET-2EEST,M3.4.4/50,M10.4.4/50"); - case TimezoneValue::EET_2EESTM350M1050_3: return String("EET-2EEST,M3.5.0,M10.5.0/3"); - case TimezoneValue::EET_2EESTM350_0M1050_0: return String("EET-2EEST,M3.5.0/0,M10.5.0/0"); - case TimezoneValue::EET_2EESTM350_3M1050_4: return String("EET-2EEST,M3.5.0/3,M10.5.0/4"); - case TimezoneValue::EET_2EESTM455_0M1054_24: return String("EET-2EEST,M4.5.5/0,M10.5.4/24"); - case TimezoneValue::EST5: return String("EST5"); - case TimezoneValue::EST5EDTM320M1110: return String("EST5EDT,M3.2.0,M11.1.0"); - case TimezoneValue::GMT0: return String("GMT0"); - case TimezoneValue::GMT0BSTM350_1M1050: return String("GMT0BST,M3.5.0/1,M10.5.0"); - case TimezoneValue::HKT_8: return String("HKT-8"); - case TimezoneValue::HST10: return String("HST10"); - case TimezoneValue::HST10HDTM320M1110: return String("HST10HDT,M3.2.0,M11.1.0"); - case TimezoneValue::IST_1GMT0M1050M350_1: return String("IST-1GMT0,M10.5.0,M3.5.0/1"); - case TimezoneValue::IST_2IDTM344_26M1050: return String("IST-2IDT,M3.4.4/26,M10.5.0"); - case TimezoneValue::IST_530: return String("IST-5:30"); - case TimezoneValue::JST_9: return String("JST-9"); - case TimezoneValue::KST_9: return String("KST-9"); - case TimezoneValue::MSK_3: return String("MSK-3"); - case TimezoneValue::MST7: return String("MST7"); - case TimezoneValue::MST7MDTM320M1110: return String("MST7MDT,M3.2.0,M11.1.0"); - case TimezoneValue::NST330NDTM320M1110: return String("NST3:30NDT,M3.2.0,M11.1.0"); - case TimezoneValue::NZST_12NZDTM950M410_3: return String("NZST-12NZDT,M9.5.0,M4.1.0/3"); - case TimezoneValue::PKT_5: return String("PKT-5"); - case TimezoneValue::PST_8: return String("PST-8"); - case TimezoneValue::PST8PDTM320M1110: return String("PST8PDT,M3.2.0,M11.1.0"); - case TimezoneValue::SAST_2: return String("SAST-2"); - case TimezoneValue::SST11: return String("SST11"); - case TimezoneValue::UTC0: return String("UTC0"); - case TimezoneValue::WAT_1: return String("WAT-1"); - case TimezoneValue::WET0WESTM350_1M1050: return String("WET0WEST,M3.5.0/1,M10.5.0"); - case TimezoneValue::WIB_7: return String("WIB-7"); - case TimezoneValue::WIT_9: return String("WIT-9"); - case TimezoneValue::WITA_8: return String("WITA-8"); - default: return String("GMT0"); // Default fallback - } -} - -// Overload for std::string -inline std::string get_timezone_value_string_std(TimezoneValue value) { - return std::string(get_timezone_value_string(value).c_str()); -} - -} // namespace timezone_data diff --git a/platformio.ini b/platformio.ini index 98192fe..d46c153 100644 --- a/platformio.ini +++ b/platformio.ini @@ -15,7 +15,7 @@ default_envs = lolin_s3_mini_213epd, lolin_s3_mini_29epd, btclock_rev_b_213epd, [env] [btclock_base] -platform = espressif32 @ ^6.10.0 +platform = espressif32 @ ^6.9.0 framework = arduino, espidf monitor_speed = 115200 monitor_filters = esp32_exception_decoder, colorize @@ -30,17 +30,17 @@ build_flags = -DLAST_BUILD_TIME=$UNIX_TIME -DARDUINO_USB_CDC_ON_BOOT -DCORE_DEBUG_LEVEL=0 - -D CONFIG_ASYNC_TCP_STACK_SIZE=16384 + -D DEFAULT_BOOT_TEXT=\"BTCLOCK\" -fexceptions build_unflags = -Werror=all -fno-exceptions lib_deps = - https://github.com/joltwallet/esp_littlefs.git#v1.16.4 - bblanchon/ArduinoJson@^7.4.1 - esp32async/ESPAsyncWebServer @ 3.7.7 - robtillaart/MCP23017@^0.9.1 - adafruit/Adafruit NeoPixel@^1.12.5 + https://github.com/joltwallet/esp_littlefs.git + bblanchon/ArduinoJson@^7.2.1 + mathieucarbou/ESPAsyncWebServer @ 3.4.5 + robtillaart/MCP23017@^0.8.0 + adafruit/Adafruit NeoPixel@^1.12.3 https://github.com/dsbaars/universal_pin#feature/mcp23017_rt https://github.com/dsbaars/GxEPD2#universal_pin https://github.com/tzapu/WiFiManager.git#v2.0.17 @@ -79,10 +79,9 @@ build_flags = -D I2C_SDA_PIN=35 -D I2C_SCK_PIN=36 -D HAS_FRONTLIGHT - -D PCA_OE_PIN=48 + -D PCA_OE_PIN=45 -D PCA_I2C_ADDR=0x42 -D IS_HW_REV_B - lib_deps = ${btclock_base.lib_deps} robtillaart/PCA9685@^0.7.1 @@ -101,7 +100,6 @@ build_flags = -D USE_QR -D VERSION_EPD_2_13 -D HW_REV=\"REV_A_EPD_2_13\" - -D CONFIG_ARDUINO_MAIN_TASK_STACK_SIZE=16384 platform_packages = platformio/tool-mklittlefs@^1.203.210628 earlephilhower/tool-mklittlefs-rp2040-earlephilhower@^5.100300.230216 @@ -114,7 +112,6 @@ build_flags = -D USE_QR -D VERSION_EPD_2_13 -D HW_REV=\"REV_B_EPD_2_13\" - -D CONFIG_ARDUINO_MAIN_TASK_STACK_SIZE=16384 platform_packages = platformio/tool-mklittlefs@^1.203.210628 earlephilhower/tool-mklittlefs-rp2040-earlephilhower@^5.100300.230216 diff --git a/src/lib/block_notify.cpp b/src/lib/block_notify.cpp index 20e6267..60b3034 100644 --- a/src/lib/block_notify.cpp +++ b/src/lib/block_notify.cpp @@ -1,14 +1,14 @@ #include "block_notify.hpp" +#include "led_handler.hpp" -// Initialize static members -esp_websocket_client_handle_t BlockNotify::wsClient = nullptr; -uint32_t BlockNotify::currentBlockHeight = 878000; -uint16_t BlockNotify::blockMedianFee = 1; -bool BlockNotify::notifyInit = false; -unsigned long int BlockNotify::lastBlockUpdate = 0; -TaskHandle_t BlockNotify::taskHandle = nullptr; +char *wsServer; +esp_websocket_client_handle_t blockNotifyClient = NULL; +uint32_t currentBlockHeight = 873400; +uint16_t blockMedianFee = 1; +bool blockNotifyInit = false; +unsigned long int lastBlockUpdate; -const char* BlockNotify::mempoolWsCert = R"EOF( +const char *mempoolWsCert = R"EOF( -----BEGIN CERTIFICATE----- MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl @@ -43,153 +43,144 @@ VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG jjxDah2nGN59PRbxYvnKkKj9 -----END CERTIFICATE----- - ------BEGIN CERTIFICATE----- -MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw -TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh -cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 -WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu -ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY -MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc -h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ -0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U -A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW -T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH -B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC -B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv -KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn -OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn -jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw -qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI -rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV -HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq -hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL -ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ -3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK -NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 -ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur -TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC -jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc -oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq -4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA -mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d -emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= ------END CERTIFICATE----- )EOF"; -void BlockNotify::onWebsocketEvent(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) { - esp_websocket_event_data_t *data = (esp_websocket_event_data_t *)event_data; - BlockNotify& instance = BlockNotify::getInstance(); +void setupBlockNotify() +{ + IPAddress result; - switch (event_id) { - case WEBSOCKET_EVENT_CONNECTED: - { - notifyInit = true; - Serial.print(F("Connected to ")); - Serial.println(preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE)); + int dnsErr = -1; + String mempoolInstance = + preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE); - JsonDocument doc; - doc["action"] = "want"; - JsonArray dataArray = doc.createNestedArray("data"); - dataArray.add("blocks"); - dataArray.add("mempool-blocks"); - - String sub; - serializeJson(doc, sub); - esp_websocket_client_send_text(wsClient, sub.c_str(), sub.length(), portMAX_DELAY); - break; - } - case WEBSOCKET_EVENT_DATA: - instance.onWebsocketMessage(data); - break; + while (dnsErr != 1 && !strchr(mempoolInstance.c_str(), ':')) + { + dnsErr = WiFi.hostByName(mempoolInstance.c_str(), result); - case WEBSOCKET_EVENT_DISCONNECTED: - Serial.println(F("Mempool.space WS Connection Closed")); - break; - - case WEBSOCKET_EVENT_ERROR: - Serial.println(F("Mempool.space WS Connection Error")); - break; + if (dnsErr != 1) + { + Serial.print(mempoolInstance); + Serial.println(F("mempool DNS could not be resolved")); + WiFi.reconnect(); + vTaskDelay(pdMS_TO_TICKS(1000)); } + } + + // Get current block height through regular API + int blockFetch = getBlockFetch(); + + if (blockFetch > currentBlockHeight) + currentBlockHeight = blockFetch; + + if (currentBlockHeight != -1) + { + lastBlockUpdate = esp_timer_get_time() / 1000000; + } + + if (workQueue != nullptr) + { + WorkItem blockUpdate = {TASK_BLOCK_UPDATE, 0}; + xQueueSend(workQueue, &blockUpdate, portMAX_DELAY); + } + + // std::strcpy(wsServer, String("wss://" + mempoolInstance + + // "/api/v1/ws").c_str()); + + const String protocol = preferences.getBool("mempoolSecure", DEFAULT_MEMPOOL_SECURE) ? "wss" : "ws"; + + String mempoolUri = protocol + "://" + preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE) + "/api/v1/ws"; + + esp_websocket_client_config_t config = { + // .uri = "wss://mempool.space/api/v1/ws", + .task_stack = (6*1024), + .user_agent = USER_AGENT + }; + + if (preferences.getBool("mempoolSecure", DEFAULT_MEMPOOL_SECURE)) { + config.cert_pem = mempoolWsCert; + } + + config.uri = mempoolUri.c_str(); + + Serial.printf("Connecting to %s\r\n", preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE)); + + blockNotifyClient = esp_websocket_client_init(&config); + esp_websocket_register_events(blockNotifyClient, WEBSOCKET_EVENT_ANY, + onWebsocketBlockEvent, blockNotifyClient); + esp_websocket_client_start(blockNotifyClient); } -void BlockNotify::onWebsocketMessage(esp_websocket_event_data_t *data) { - JsonDocument doc; - JsonDocument filter; - filter["block"]["height"] = true; - filter["mempool-blocks"][0]["medianFee"] = true; +void onWebsocketBlockEvent(void *handler_args, esp_event_base_t base, + int32_t event_id, void *event_data) +{ + esp_websocket_event_data_t *data = (esp_websocket_event_data_t *)event_data; + const String sub = "{\"action\": \"want\", \"data\":[\"blocks\", \"mempool-blocks\"]}"; + switch (event_id) + { + case WEBSOCKET_EVENT_CONNECTED: + blockNotifyInit = true; - deserializeJson(doc, (char*)data->data_ptr, DeserializationOption::Filter(filter)); + Serial.println(F("Connected to Mempool.space WebSocket")); - if (doc["block"].is()) { - JsonObject block = doc["block"]; - if (block["height"].as() != currentBlockHeight) { - processNewBlock(block["height"].as()); - } - } - else if (doc["mempool-blocks"].is()) { - JsonArray blockInfo = doc["mempool-blocks"].as(); - uint medianFee = (uint)round(blockInfo[0]["medianFee"].as()); - processNewBlockFee(medianFee); + Serial.println(sub); + if (esp_websocket_client_send_text(blockNotifyClient, sub.c_str(), + sub.length(), portMAX_DELAY) == -1) + { + Serial.println(F("Mempool.space WS Block Subscribe Error")); } + + break; + case WEBSOCKET_EVENT_DATA: + onWebsocketBlockMessage(data); + break; + case WEBSOCKET_EVENT_ERROR: + Serial.println(F("Mempool.space WS Connnection error")); + break; + case WEBSOCKET_EVENT_DISCONNECTED: + Serial.println(F("Mempool.space WS Connnection Closed")); + break; + } } -void BlockNotify::setup() { - IPAddress result; - int dnsErr = -1; - String mempoolInstance = preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE); +void onWebsocketBlockMessage(esp_websocket_event_data_t *event_data) +{ + JsonDocument doc; - while (dnsErr != 1 && !strchr(mempoolInstance.c_str(), ':')) { - dnsErr = WiFi.hostByName(mempoolInstance.c_str(), result); + JsonDocument filter; + filter["block"]["height"] = true; + filter["mempool-blocks"][0]["medianFee"] = true; - if (dnsErr != 1) { - Serial.print(mempoolInstance); - Serial.println(F("mempool DNS could not be resolved")); - WiFi.reconnect(); - vTaskDelay(pdMS_TO_TICKS(1000)); - } + deserializeJson(doc, (char *)event_data->data_ptr, DeserializationOption::Filter(filter)); + + // if (error) { + // Serial.print("deserializeJson() failed: "); + // Serial.println(error.c_str()); + // return; + // } + + if (doc["block"].is()) + { + JsonObject block = doc["block"]; + + if (block["height"].as() == currentBlockHeight) { + return; } - // Get current block height through regular API - int blockFetch = fetchLatestBlock(); + processNewBlock(block["height"].as()); + } + else if (doc["mempool-blocks"].is()) + { + JsonArray blockInfo = doc["mempool-blocks"].as(); - if (blockFetch > currentBlockHeight) - currentBlockHeight = blockFetch; + uint medianFee = (uint)round(blockInfo[0]["medianFee"].as()); - if (currentBlockHeight != -1) { - lastBlockUpdate = esp_timer_get_time() / 1000000; - } + processNewBlockFee(medianFee); + } - if (workQueue != nullptr) { - WorkItem blockUpdate = {TASK_BLOCK_UPDATE, 0}; - xQueueSend(workQueue, &blockUpdate, portMAX_DELAY); - } - - const bool useSSL = preferences.getBool("mempoolSecure", DEFAULT_MEMPOOL_SECURE); - const String protocol = useSSL ? "wss" : "ws"; - String wsUri = protocol + "://" + mempoolInstance + "/api/v1/ws"; - - esp_websocket_client_config_t config = { - .task_stack = (6*1024), - .user_agent = USER_AGENT - }; - - if (useSSL) { - config.cert_pem = mempoolWsCert; - } - - config.uri = wsUri.c_str(); - - Serial.printf("Connecting to %s\r\n", mempoolInstance.c_str()); - - wsClient = esp_websocket_client_init(&config); - esp_websocket_register_events(wsClient, WEBSOCKET_EVENT_ANY, onWebsocketEvent, wsClient); - esp_websocket_client_start(wsClient); + doc.clear(); } - - -void BlockNotify::processNewBlock(uint32_t newBlockHeight) { +void processNewBlock(uint32_t newBlockHeight) { if (newBlockHeight <= currentBlockHeight) { return; @@ -229,69 +220,76 @@ void BlockNotify::processNewBlock(uint32_t newBlockHeight) { } } -void BlockNotify::processNewBlockFee(uint16_t newBlockFee) { - if (blockMedianFee == newBlockFee) +void processNewBlockFee(uint16_t newBlockFee) { + if (blockMedianFee == newBlockFee) { - return; + return; } + // Serial.printf("New median fee: %d\r\n", medianFee); blockMedianFee = newBlockFee; if (workQueue != nullptr) { - WorkItem blockUpdate = {TASK_FEE_UPDATE, 0}; - xQueueSend(workQueue, &blockUpdate, portMAX_DELAY); + WorkItem blockUpdate = {TASK_FEE_UPDATE, 0}; + xQueueSend(workQueue, &blockUpdate, portMAX_DELAY); } } -uint32_t BlockNotify::getBlockHeight() const { - return currentBlockHeight; -} +uint32_t getBlockHeight() { return currentBlockHeight; } -void BlockNotify::setBlockHeight(uint32_t newBlockHeight) +void setBlockHeight(uint32_t newBlockHeight) { - currentBlockHeight = newBlockHeight; + currentBlockHeight = newBlockHeight; } -uint16_t BlockNotify::getBlockMedianFee() const { - return blockMedianFee; -} +uint16_t getBlockMedianFee() { return blockMedianFee; } -void BlockNotify::setBlockMedianFee(uint16_t newBlockMedianFee) +void setBlockMedianFee(uint16_t newBlockMedianFee) { - blockMedianFee = newBlockMedianFee; + blockMedianFee = newBlockMedianFee; } -bool BlockNotify::isConnected() const +bool isBlockNotifyConnected() { - if (wsClient == NULL) - return false; - return esp_websocket_client_is_connected(wsClient); + if (blockNotifyClient == NULL) + return false; + return esp_websocket_client_is_connected(blockNotifyClient); } -bool BlockNotify::isInitialized() const +bool getBlockNotifyInit() { - return notifyInit; + return blockNotifyInit; } -void BlockNotify::stop() +void stopBlockNotify() { - if (wsClient == NULL) - return; + if (blockNotifyClient == NULL) + return; - esp_websocket_client_close(wsClient, portMAX_DELAY); - esp_websocket_client_stop(wsClient); - esp_websocket_client_destroy(wsClient); - wsClient = NULL; + esp_websocket_client_close(blockNotifyClient, pdMS_TO_TICKS(5000)); + esp_websocket_client_stop(blockNotifyClient); + esp_websocket_client_destroy(blockNotifyClient); + + blockNotifyClient = NULL; } -void BlockNotify::restart() +void restartBlockNotify() { - stop(); - setup(); + stopBlockNotify(); + + if (blockNotifyClient == NULL) { + setupBlockNotify(); + return; + } + + // esp_websocket_client_close(blockNotifyClient, pdMS_TO_TICKS(5000)); + // esp_websocket_client_stop(blockNotifyClient); + // esp_websocket_client_start(blockNotifyClient); } -int BlockNotify::fetchLatestBlock() { + +int getBlockFetch() { try { String mempoolInstance = preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE); const String protocol = preferences.getBool("mempoolSecure", DEFAULT_MEMPOOL_SECURE) ? "https" : "http"; @@ -314,12 +312,12 @@ int BlockNotify::fetchLatestBlock() { return 2203; // B-T-C } -uint BlockNotify::getLastBlockUpdate() const +uint getLastBlockUpdate() { - return lastBlockUpdate; + return lastBlockUpdate; } -void BlockNotify::setLastBlockUpdate(uint lastUpdate) +void setLastBlockUpdate(uint lastUpdate) { - lastBlockUpdate = lastUpdate; + lastBlockUpdate = lastUpdate; } \ No newline at end of file diff --git a/src/lib/block_notify.hpp b/src/lib/block_notify.hpp index 15aabee..9c41bf0 100644 --- a/src/lib/block_notify.hpp +++ b/src/lib/block_notify.hpp @@ -5,6 +5,7 @@ #include #include #include + #include #include @@ -13,53 +14,28 @@ #include "lib/timers.hpp" #include "lib/shared.hpp" -class BlockNotify { -public: - static BlockNotify& getInstance() { - static BlockNotify instance; - return instance; - } +// using namespace websockets; - // Delete copy constructor and assignment operator - BlockNotify(const BlockNotify&) = delete; - void operator=(const BlockNotify&) = delete; +void setupBlockNotify(); - // Block notification setup and control - void setup(); - void stop(); - void restart(); - bool isConnected() const; - bool isInitialized() const; +void onWebsocketBlockEvent(void *handler_args, esp_event_base_t base, + int32_t event_id, void *event_data); +void onWebsocketBlockMessage(esp_websocket_event_data_t *event_data); - // Block height management - void setBlockHeight(uint32_t newBlockHeight); - uint32_t getBlockHeight() const; +void setBlockHeight(uint32_t newBlockHeight); +uint32_t getBlockHeight(); - // Block fee management - void setBlockMedianFee(uint16_t blockMedianFee); - uint16_t getBlockMedianFee() const; +void setBlockMedianFee(uint16_t blockMedianFee); +uint16_t getBlockMedianFee(); - // Block processing - void processNewBlock(uint32_t newBlockHeight); - void processNewBlockFee(uint16_t newBlockFee); +bool isBlockNotifyConnected(); +void stopBlockNotify(); +void restartBlockNotify(); - // Block fetch and update tracking - int fetchLatestBlock(); - uint getLastBlockUpdate() const; - void setLastBlockUpdate(uint lastUpdate); +void processNewBlock(uint32_t newBlockHeight); +void processNewBlockFee(uint16_t newBlockFee); -private: - BlockNotify() = default; // Private constructor for singleton - - void setupTask(); - static void onWebsocketEvent(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data); - void onWebsocketMessage(esp_websocket_event_data_t *data); - - static const char* mempoolWsCert; - static esp_websocket_client_handle_t wsClient; - static uint32_t currentBlockHeight; - static uint16_t blockMedianFee; - static bool notifyInit; - static unsigned long int lastBlockUpdate; - static TaskHandle_t taskHandle; -}; \ No newline at end of file +bool getBlockNotifyInit(); +uint32_t getLastBlockUpdate(); +int getBlockFetch(); +void setLastBlockUpdate(uint32_t lastUpdate); diff --git a/src/lib/config.cpp b/src/lib/config.cpp index 908a5a6..ab891ca 100644 --- a/src/lib/config.cpp +++ b/src/lib/config.cpp @@ -1,7 +1,7 @@ #include "config.hpp" -#include "led_handler.hpp" -#define MAX_ATTEMPTS_WIFI_CONNECTION 20 +// Global instance definitions +PriceNotify::PriceNotifyManager priceManager; // zlib_turbo zt; @@ -132,25 +132,9 @@ void setup() void setupWifi() { WiFi.onEvent(WiFiEvent); - - // wifi_country_t country = { - // .cc = "NL", - // .schan = 1, - // .nchan = 13, - // .policy = WIFI_COUNTRY_POLICY_MANUAL - // }; - - // esp_err_t err = esp_wifi_set_country(&country); - // if (err != ESP_OK) { - // Serial.printf("Failed to set country: %d\n", err); - // } - WiFi.setAutoConnect(true); WiFi.setAutoReconnect(true); WiFi.begin(); - - - if (preferences.getInt("txPower", DEFAULT_TX_POWER)) { if (WiFi.setTxPower( @@ -188,7 +172,6 @@ void setupWifi() wm.setConfigPortalTimeout(preferences.getUInt("wpTimeout", DEFAULT_WP_TIMEOUT)); wm.setWiFiAutoReconnect(false); wm.setDebugOutput(false); - wm.setCountry("NL"); wm.setConfigPortalBlocking(true); wm.setAPCallback([&](WiFiManager *wifiManager) @@ -259,7 +242,7 @@ void setupWifi() void syncTime() { - configTime(0, 0, + configTime(preferences.getInt("gmtOffset", DEFAULT_TIME_OFFSET_SECONDS), 0, NTP_SERVER); struct tm timeinfo; @@ -267,32 +250,23 @@ void syncTime() { auto& ledHandler = getLedHandler(); ledHandler.queueEffect(LED_EFFECT_CONFIGURING); - configTime(0, 0, + configTime(preferences.getInt("gmtOffset", DEFAULT_TIME_OFFSET_SECONDS), 0, NTP_SERVER); delay(500); Serial.println(F("Retry set time")); } - setTimezone(get_timezone_value_string(timezone_data::find_timezone_value(preferences.getString("tzString", DEFAULT_TZ_STRING)))); - lastTimeSync = esp_timer_get_time() / 1000000; } -void setTimezone(String timezone) { - Serial.printf(" Setting Timezone to %s\n",timezone.c_str()); - setenv("TZ",timezone.c_str(),1); // Now adjust the TZ. Clock settings are adjusted to show the new local time - tzset(); -} - - void setupPreferences() { preferences.begin("btclock", false); EPDManager::getInstance().setForegroundColor(preferences.getUInt("fgColor", DEFAULT_FG_COLOR)); EPDManager::getInstance().setBackgroundColor(preferences.getUInt("bgColor", DEFAULT_BG_COLOR)); - BlockNotify::getInstance().setBlockHeight(preferences.getUInt("blockHeight", INITIAL_BLOCK_HEIGHT)); - setPrice(preferences.getUInt("lastPrice", INITIAL_LAST_PRICE), CURRENCY_USD); + setBlockHeight(preferences.getUInt("blockHeight", INITIAL_BLOCK_HEIGHT)); + priceManager.processNewPrice(preferences.getUInt("lastPrice", INITIAL_LAST_PRICE), CURRENCY_USD); if (!preferences.isKey("enableDebugLog")) { preferences.putBool("enableDebugLog", DEFAULT_ENABLE_DEBUG_LOG); @@ -399,8 +373,16 @@ void setupWebsocketClients(void *pvParameters) } else if (dataSource == THIRD_PARTY_SOURCE) { - BlockNotify::getInstance().setup(); + setupBlockNotify(); setupPriceNotify(); + + // Create task for price manager loop + xTaskCreate([](void* param) { + for (;;) { + priceManager.loop(); + vTaskDelay(10 / portTICK_PERIOD_MS); + } + }, "priceManagerLoop", (6 * 1024), NULL, tskIDLE_PRIORITY, NULL); } vTaskDelete(NULL); @@ -779,3 +761,17 @@ DataSourceType getDataSource() { void setDataSource(DataSourceType source) { preferences.putUChar("dataSource", static_cast(source)); } + +void setupPriceNotify() { + priceManager.init(PriceNotify::PriceSource::COINCAP); + priceManager.onPriceUpdate([](PriceNotify::Currency currency, uint64_t price) { + if (workQueue != nullptr && (ScreenHandler::getCurrentScreen() == SCREEN_BTC_TICKER || + ScreenHandler::getCurrentScreen() == SCREEN_SATS_PER_CURRENCY || + ScreenHandler::getCurrentScreen() == SCREEN_MARKET_CAP)) + { + WorkItem priceUpdate = {TASK_PRICE_UPDATE, static_cast(currency)}; + xQueueSend(workQueue, &priceUpdate, portMAX_DELAY); + } + }); + priceManager.connect(); +} diff --git a/src/lib/config.hpp b/src/lib/config.hpp index c8d5336..8cfc741 100644 --- a/src/lib/config.hpp +++ b/src/lib/config.hpp @@ -1,108 +1,88 @@ #pragma once -#include #include +#include #include -#include -#include -#include +#include #include +#include + #include -#include - -#include "lib/block_notify.hpp" -#include "lib/button_handler.hpp" -#include "lib/epd.hpp" -// #include "lib/improv.hpp" -#include "lib/led_handler.hpp" -#include "lib/ota.hpp" -#include "lib/nostr_notify.hpp" -#include "lib/bitaxe_fetch.hpp" -#include "lib/mining_pool_stats_fetch.hpp" - -#include "lib/v2_notify.hpp" - -#include "lib/price_notify.hpp" -#include "lib/screen_handler.hpp" -#include "lib/shared.hpp" -#include "lib/webserver.hpp" -#ifdef HAS_FRONTLIGHT -#include "PCA9685.h" -#include "BH1750.h" -#endif +#include +#include +#include "block_notify.hpp" +#include "led_handler.hpp" +#include "nostr_notify.hpp" +#include "price_notify/price_notify.hpp" +#include "screen_handler.hpp" #include "shared.hpp" -#include "defaults.hpp" -#include "timezone_data.hpp" -#define NTP_SERVER "pool.ntp.org" -#define DEFAULT_TIME_OFFSET_SECONDS 3600 -#ifndef MCP_DEV_ADDR -#define MCP_DEV_ADDR 0x20 +#include "timers.hpp" +#include "v2_notify.hpp" +#include "webserver.hpp" +#include "button_handler.hpp" +#include "bitaxe_fetch.hpp" +#include "mining_pool_stats_fetch.hpp" +#include "epd.hpp" + +#ifdef HAS_FRONTLIGHT +#include +#include #endif +extern Preferences preferences; +extern QueueHandle_t workQueue; +extern PriceNotify::PriceNotifyManager priceManager; -void setup(); -void syncTime(); -void setTimezone(String timezone); -uint getLastTimeSync(); -void setupPreferences(); -void setupWebsocketClients(void *pvParameters); -void setupHardware(); +void setupConfig(); void setupWifi(); +void setupOTA(); +void setupMDNS(); +void setupDataSources(); +void stopDataSources(); +void restartDataSources(); +void setupTasks(); void setupTimers(); +void setupLittleFS(); +void setupPreferences(); +void setupWDT(); +void setupWorkQueue(); +void setupLedHandler(); +void setupScreenHandler(); +void setupWebserver(); +void setupNostrNotify(); +void setupBlockNotify(); +void setupV2Notify(); +void setupPriceNotify(); +void setupHardware(); void finishSetup(); -void setupMcp(); +void syncTime(); + +void handleOTA(); +void handleWifi(); +void handleWDT(); +void handleWorkQueue(); + +void setWifiTxPower(int8_t power); +void onWifiEvent(WiFiEvent_t event); +void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info); + +DataSourceType getDataSource(); +void setDataSource(DataSourceType source); +String getMyHostname(); +uint getLastTimeSync(); +bool debugLogEnabled(); + #ifdef HAS_FRONTLIGHT -extern BH1750 bh1750; -extern bool hasLuxSensor; float getLightLevel(); bool hasLightLevel(); + +extern PCA9685 flArray; #endif -String getMyHostname(); -std::vector getScreenNameMap(); - -std::vector getLocalUrl(); -// bool improv_connectWifi(std::string ssid, std::string password); -// void improvGetAvailableWifiNetworks(); -// bool onImprovCommandCallback(improv::ImprovCommand cmd); -// void onImprovErrorCallback(improv::Error err); -// 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(); bool isWhiteVersion(); String getFsRev(); -bool debugLogEnabled(); - -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); -String replaceAmbiguousChars(String input); -const char* getFirmwareFilename(); -const char* getWebUiFilename(); -// void loadIcons(); - -extern Preferences preferences; -extern MCP23017 mcp1; -#ifdef IS_BTCLOCK_V8 -extern MCP23017 mcp2; -#endif - -#ifdef HAS_FRONTLIGHT -extern PCA9685 flArray; -#endif - -// Expose DataSourceType enum -extern DataSourceType getDataSource(); -extern void setDataSource(DataSourceType source); \ No newline at end of file +#define NTP_SERVER "pool.ntp.org" +#define DEFAULT_TIME_OFFSET_SECONDS 3600 \ No newline at end of file diff --git a/src/lib/defaults.hpp b/src/lib/defaults.hpp index 00d8bc0..29d7eee 100644 --- a/src/lib/defaults.hpp +++ b/src/lib/defaults.hpp @@ -24,7 +24,6 @@ #define DEFAULT_V2_SOURCE_CURRENCY CURRENCY_USD #define DEFAULT_TIME_OFFSET_SECONDS 3600 -#define DEFAULT_TZ_STRING "Europe/Amsterdam" #define DEFAULT_HOSTNAME_PREFIX "btclock" #define DEFAULT_MEMPOOL_INSTANCE "mempool.space" @@ -47,8 +46,8 @@ #define DEFAULT_LUX_LIGHT_TOGGLE 128 #define DEFAULT_FL_OFF_WHEN_DARK true -#define DEFAULT_FL_ALWAYS_ON true -#define DEFAULT_FL_FLASH_ON_UPDATE true +#define DEFAULT_FL_ALWAYS_ON false +#define DEFAULT_FL_FLASH_ON_UPDATE false #define DEFAULT_LED_STATUS false #define DEFAULT_TIMER_ACTIVE true @@ -61,7 +60,6 @@ #define DEFAULT_MINING_POOL_STATS_ENABLED false #define DEFAULT_MINING_POOL_NAME "ocean" #define DEFAULT_MINING_POOL_USER "38Qkkei3SuF1Eo45BaYmRHUneRD54yyTFy" // Random actual Ocean hasher -#define DEFAULT_LOCAL_POOL_ENDPOINT "umbrel.local:2019" #define DEFAULT_ZAP_NOTIFY_ENABLED false #define DEFAULT_ZAP_NOTIFY_PUBKEY "b5127a08cf33616274800a4387881a9f98e04b9c37116e92de5250498635c422" diff --git a/src/lib/globals.hpp b/src/lib/globals.hpp new file mode 100644 index 0000000..dbe52f6 --- /dev/null +++ b/src/lib/globals.hpp @@ -0,0 +1,6 @@ +#pragma once + +#include "price_notify/price_notify.hpp" + +// Global instances +extern PriceNotify::PriceNotifyManager priceManager; \ No newline at end of file diff --git a/src/lib/led_handler.cpp b/src/lib/led_handler.cpp index 453b067..3cdb953 100644 --- a/src/lib/led_handler.cpp +++ b/src/lib/led_handler.cpp @@ -535,7 +535,7 @@ void LedHandler::frontlightSetBrightness(uint brightness) { } for (int ledPin = 0; ledPin <= NUM_SCREENS; ledPin++) { - flArray.setPWM(ledPin + 1, 0, brightness); + flArray.setPWM(ledPin, 0, brightness); } } @@ -543,7 +543,7 @@ std::vector LedHandler::frontlightGetStatus() { std::vector statuses; for (int ledPin = 1; ledPin <= NUM_SCREENS; ledPin++) { uint16_t a = 0, b = 0; - flArray.getPWM(ledPin + 1, &a, &b); + flArray.getPWM(ledPin, &a, &b); statuses.push_back(round(b - a / 4096)); } return statuses; @@ -576,7 +576,7 @@ void LedHandler::frontlightFadeInAll(int flDelayTime, bool staggered) { } else { for (int dutyCycle = 0; dutyCycle <= maxBrightness; dutyCycle += FL_FADE_STEP) { for (int ledPin = 0; ledPin <= NUM_SCREENS; ledPin++) { - flArray.setPWM(ledPin + 1, 0, dutyCycle); + flArray.setPWM(ledPin, 0, dutyCycle); } vTaskDelay(pdMS_TO_TICKS(flDelayTime)); } @@ -611,7 +611,7 @@ void LedHandler::frontlightFadeOutAll(int flDelayTime, bool staggered) { } else { for (int dutyCycle = preferences.getUInt("flMaxBrightness"); dutyCycle >= 0; dutyCycle -= FL_FADE_STEP) { for (int ledPin = 0; ledPin <= NUM_SCREENS; ledPin++) { - flArray.setPWM(ledPin + 1, 0, dutyCycle); + flArray.setPWM(ledPin, 0, dutyCycle); } vTaskDelay(pdMS_TO_TICKS(flDelayTime)); } @@ -628,7 +628,7 @@ void LedHandler::frontlightFadeIn(uint num, int flDelayTime) { } for (int dutyCycle = 0; dutyCycle <= preferences.getUInt("flMaxBrightness"); dutyCycle += 5) { - flArray.setPWM(num + 1, 0, dutyCycle); + flArray.setPWM(num, 0, dutyCycle); vTaskDelay(pdMS_TO_TICKS(flDelayTime)); } } @@ -639,7 +639,7 @@ void LedHandler::frontlightFadeOut(uint num, int flDelayTime) { } for (int dutyCycle = preferences.getUInt("flMaxBrightness"); dutyCycle >= 0; dutyCycle -= 5) { - flArray.setPWM(num + 1, 0, dutyCycle); + flArray.setPWM(num, 0, dutyCycle); vTaskDelay(pdMS_TO_TICKS(flDelayTime)); } } diff --git a/src/lib/mining_pool/pool_factory.cpp b/src/lib/mining_pool/pool_factory.cpp index 45bc6ce..c353996 100644 --- a/src/lib/mining_pool/pool_factory.cpp +++ b/src/lib/mining_pool/pool_factory.cpp @@ -5,7 +5,6 @@ const char* PoolFactory::MINING_POOL_NAME_NODERUNNERS = "noderunners"; const char* PoolFactory::MINING_POOL_NAME_BRAIINS = "braiins"; const char* PoolFactory::MINING_POOL_NAME_SATOSHI_RADIO = "satoshi_radio"; const char* PoolFactory::MINING_POOL_NAME_PUBLIC_POOL = "public_pool"; -const char* PoolFactory::MINING_POOL_NAME_LOCAL_PUBLIC_POOL = "local_public_pool"; const char* PoolFactory::MINING_POOL_NAME_GOBRRR_POOL = "gobrrr_pool"; const char* PoolFactory::MINING_POOL_NAME_CKPOOL = "ckpool"; const char* PoolFactory::MINING_POOL_NAME_EU_CKPOOL = "eu_ckpool"; @@ -18,7 +17,6 @@ std::unique_ptr PoolFactory::createPool(const std::string& {MINING_POOL_NAME_BRAIINS, []() { return std::make_unique(); }}, {MINING_POOL_NAME_SATOSHI_RADIO, []() { return std::make_unique(); }}, {MINING_POOL_NAME_PUBLIC_POOL, []() { return std::make_unique(); }}, - {MINING_POOL_NAME_LOCAL_PUBLIC_POOL, []() { return std::make_unique(); }}, {MINING_POOL_NAME_GOBRRR_POOL, []() { return std::make_unique(); }}, {MINING_POOL_NAME_CKPOOL, []() { return std::make_unique(); }}, {MINING_POOL_NAME_EU_CKPOOL, []() { return std::make_unique(); }} diff --git a/src/lib/mining_pool/pool_factory.hpp b/src/lib/mining_pool/pool_factory.hpp index 9885d74..951dbe5 100644 --- a/src/lib/mining_pool/pool_factory.hpp +++ b/src/lib/mining_pool/pool_factory.hpp @@ -10,7 +10,6 @@ #include "ocean/ocean_pool.hpp" #include "satoshi_radio/satoshi_radio_pool.hpp" #include "public_pool/public_pool.hpp" -#include "public_pool/local_public_pool.hpp" #include "gobrrr_pool/gobrrr_pool.hpp" #include "ckpool/ckpool.hpp" #include "ckpool/eu_ckpool.hpp" @@ -29,7 +28,6 @@ class PoolFactory { MINING_POOL_NAME_SATOSHI_RADIO, MINING_POOL_NAME_BRAIINS, MINING_POOL_NAME_PUBLIC_POOL, - MINING_POOL_NAME_LOCAL_PUBLIC_POOL, MINING_POOL_NAME_GOBRRR_POOL, MINING_POOL_NAME_CKPOOL, MINING_POOL_NAME_EU_CKPOOL @@ -57,7 +55,6 @@ class PoolFactory { static const char* MINING_POOL_NAME_BRAIINS; static const char* MINING_POOL_NAME_SATOSHI_RADIO; static const char* MINING_POOL_NAME_PUBLIC_POOL; - static const char* MINING_POOL_NAME_LOCAL_PUBLIC_POOL; static const char* MINING_POOL_NAME_GOBRRR_POOL; static const char* MINING_POOL_NAME_CKPOOL; static const char* MINING_POOL_NAME_EU_CKPOOL; diff --git a/src/lib/mining_pool/public_pool/local_public_pool.cpp b/src/lib/mining_pool/public_pool/local_public_pool.cpp deleted file mode 100644 index 048197c..0000000 --- a/src/lib/mining_pool/public_pool/local_public_pool.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "local_public_pool.hpp" -#include "lib/shared.hpp" -#include "lib/defaults.hpp" - -std::string LocalPublicPool::getEndpoint() const { - return preferences.getString("localPoolEndpoint", DEFAULT_LOCAL_POOL_ENDPOINT).c_str(); -} - -std::string LocalPublicPool::getApiUrl() const { - return "http://" + getEndpoint() + "/api/client/" + poolUser; -} \ No newline at end of file diff --git a/src/lib/mining_pool/public_pool/local_public_pool.hpp b/src/lib/mining_pool/public_pool/local_public_pool.hpp deleted file mode 100644 index a9e37ad..0000000 --- a/src/lib/mining_pool/public_pool/local_public_pool.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include "public_pool.hpp" - -class LocalPublicPool : public PublicPool { -public: - std::string getApiUrl() const override; - std::string getDisplayLabel() const override { return "LOCAL/POOL"; } -private: - std::string getEndpoint() const; -}; \ No newline at end of file diff --git a/src/lib/nostr_notify.cpp b/src/lib/nostr_notify.cpp index dda24e1..154b4a2 100644 --- a/src/lib/nostr_notify.cpp +++ b/src/lib/nostr_notify.cpp @@ -1,5 +1,6 @@ #include "nostr_notify.hpp" #include "led_handler.hpp" +#include "globals.hpp" std::vector pools; nostr::Transport *transport; @@ -41,7 +42,7 @@ void setupNostrNotify(bool asDatasource, bool zapNotify) {relay}, {// First filter { - {"kinds", {"12203"}}, + {"kinds", {"1"}}, {"since", {String(getMinutesAgo(60))}}, {"authors", {pubKey}}, }}, @@ -79,9 +80,8 @@ void nostrTask(void *pvParameters) { DataSourceType dataSource = getDataSource(); if(dataSource == NOSTR_SOURCE) { - auto& blockNotify = BlockNotify::getInstance(); - int blockFetch = blockNotify.fetchLatestBlock(); - blockNotify.processNewBlock(blockFetch); + int blockFetch = getBlockFetch(); + processNewBlock(blockFetch); } while (1) @@ -146,7 +146,6 @@ void handleNostrEventCallback(const String &subId, nostr::SignedNostrEvent *even // Use direct value access instead of multiple comparisons String typeValue; uint medianFee = 0; - uint blockHeight = 0; for (JsonArray tag : tags) { if (tag.size() != 2) continue; @@ -167,31 +166,20 @@ void handleNostrEventCallback(const String &subId, nostr::SignedNostrEvent *even medianFee = tag[1].as(); } break; - case 'b': // blockHeight - if (strcmp(key, "block") == 0) { - blockHeight = tag[1].as(); - } - break; } } // Process the data if (!typeValue.isEmpty()) { if (typeValue == "priceUsd") { - processNewPrice(obj["content"].as(), CURRENCY_USD); - if (blockHeight != 0) { - auto& blockNotify = BlockNotify::getInstance(); - blockNotify.processNewBlock(blockHeight); - } + priceManager.processNewPrice(obj["content"].as(), static_cast(CURRENCY_USD)); } else if (typeValue == "blockHeight") { - auto& blockNotify = BlockNotify::getInstance(); - blockNotify.processNewBlock(obj["content"].as()); + processNewBlock(obj["content"].as()); } if (medianFee != 0) { - auto& blockNotify = BlockNotify::getInstance(); - blockNotify.processNewBlockFee(medianFee); + processNewBlockFee(medianFee); } } } diff --git a/src/lib/nostr_notify.hpp b/src/lib/nostr_notify.hpp index ebbe2cb..035e02b 100644 --- a/src/lib/nostr_notify.hpp +++ b/src/lib/nostr_notify.hpp @@ -10,8 +10,8 @@ #include "NostrEvent.h" #include "NostrPool.h" -#include "price_notify.hpp" #include "block_notify.hpp" +#include "price_notify/price_notify.hpp" #include "lib/timers.hpp" void setupNostrNotify(bool asDatasource, bool zapNotify); diff --git a/src/lib/ota.cpp b/src/lib/ota.cpp index 2d94d48..59497c7 100644 --- a/src/lib/ota.cpp +++ b/src/lib/ota.cpp @@ -74,8 +74,8 @@ void onOTAStart() ButtonHandler::suspendTask(); // stopWebServer(); - auto& blockNotify = BlockNotify::getInstance(); - blockNotify.stop(); + stopBlockNotify(); + stopPriceNotify(); } void handleOTATask(void *parameter) diff --git a/src/lib/price_notify.cpp b/src/lib/price_notify.cpp deleted file mode 100644 index c851e85..0000000 --- a/src/lib/price_notify.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include "price_notify.hpp" - -const char *wsServerPrice = "wss://ws.kraken.com/v2"; - -WebSocketsClient webSocket; -uint currentPrice = 90000; -unsigned long int lastPriceUpdate; -bool priceNotifyInit = false; -std::map currencyMap; -std::map lastUpdateMap; -TaskHandle_t priceNotifyTaskHandle; - -void onWebsocketPriceEvent(WStype_t type, uint8_t * payload, size_t length); - -void setupPriceNotify() -{ - webSocket.beginSSL("ws.kraken.com", 443, "/v2"); - webSocket.onEvent([](WStype_t type, uint8_t * payload, size_t length) { - onWebsocketPriceEvent(type, payload, length); - }); - webSocket.setReconnectInterval(5000); - webSocket.enableHeartbeat(15000, 3000, 2); - - setupPriceNotifyTask(); -} - -void onWebsocketPriceEvent(WStype_t type, uint8_t * payload, size_t length) { - switch(type) { - case WStype_DISCONNECTED: - Serial.println(F("Price WS Connection Closed")); - break; - case WStype_CONNECTED: - { - Serial.println("Connected to " + String(wsServerPrice)); - - JsonDocument doc; - doc["method"] = "subscribe"; - JsonObject params = doc["params"].to(); - params["channel"] = "ticker"; - params["symbol"][0] = "BTC/USD"; - - webSocket.sendTXT(doc.as().c_str()); - break; - } - case WStype_TEXT: - { - JsonDocument doc; - deserializeJson(doc, (char *)payload); - - if (doc["data"][0].is()) - { - float price = doc["data"][0]["last"].as(); - uint roundedPrice = round(price); - if (currentPrice != roundedPrice) - { - processNewPrice(roundedPrice, CURRENCY_USD); - } - } - break; - } - case WStype_BIN: - 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 processNewPrice(uint newPrice, char currency) -{ - uint minSecPriceUpd = preferences.getUInt( - "minSecPriceUpd", DEFAULT_SECONDS_BETWEEN_PRICE_UPDATE); - uint currentTime = esp_timer_get_time() / 1000000; - - if (lastUpdateMap.find(currency) == lastUpdateMap.end() || - (currentTime - lastUpdateMap[currency]) > minSecPriceUpd) - { - currencyMap[currency] = newPrice; - - // Store price in preferences if enough time has passed - if (lastUpdateMap[currency] == 0 || (currentTime - lastUpdateMap[currency]) > 120) - { - String prefKey = String("lastPrice_") + getCurrencyCode(currency).c_str(); - preferences.putUInt(prefKey.c_str(), newPrice); - } - - lastUpdateMap[currency] = currentTime; - - if (workQueue != nullptr && (ScreenHandler::getCurrentScreen() == SCREEN_BTC_TICKER || - ScreenHandler::getCurrentScreen() == SCREEN_SATS_PER_CURRENCY || - ScreenHandler::getCurrentScreen() == SCREEN_MARKET_CAP)) - { - WorkItem priceUpdate = {TASK_PRICE_UPDATE, currency}; - xQueueSend(workQueue, &priceUpdate, portMAX_DELAY); - } - } -} - -void loadStoredPrices() -{ - // Load prices for all supported currencies - std::vector currencies = getAvailableCurrencies(); - - for (const std::string ¤cy : currencies) { - // Get first character as the currency identifier - String prefKey = String("lastPrice_") + currency.c_str(); - uint storedPrice = preferences.getUInt(prefKey.c_str(), 0); - - if (storedPrice > 0) { - currencyMap[getCurrencyChar(currency)] = storedPrice; - // Initialize lastUpdateMap to 0 so next update will store immediately - lastUpdateMap[getCurrencyChar(currency)] = 0; - } - } -} - -uint getLastPriceUpdate(char currency) -{ - if (lastUpdateMap.find(currency) == lastUpdateMap.end()) - { - return 0; - } - - return lastUpdateMap[currency]; -} - -uint getPrice(char currency) -{ - if (currencyMap.find(currency) == currencyMap.end()) - { - return 0; - } - return currencyMap[currency]; -} - -void setPrice(uint newPrice, char currency) -{ - currencyMap[currency] = newPrice; -} - -bool isPriceNotifyConnected() -{ - return webSocket.isConnected(); -} - -bool getPriceNotifyInit() -{ - return priceNotifyInit; -} - -void stopPriceNotify() -{ - webSocket.disconnect(); - if (priceNotifyTaskHandle != NULL) { - vTaskDelete(priceNotifyTaskHandle); - priceNotifyTaskHandle = NULL; - } -} - -void restartPriceNotify() -{ - stopPriceNotify(); - setupPriceNotify(); -} - -void taskPriceNotify(void *pvParameters) -{ - for (;;) - { - webSocket.loop(); - vTaskDelay(10 / portTICK_PERIOD_MS); - } -} - -void setupPriceNotifyTask() -{ - xTaskCreate(taskPriceNotify, "priceNotify", (6 * 1024), NULL, tskIDLE_PRIORITY, - &priceNotifyTaskHandle); -} \ No newline at end of file diff --git a/src/lib/price_notify.hpp b/src/lib/price_notify.hpp deleted file mode 100644 index 6c8c6df..0000000 --- a/src/lib/price_notify.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include "lib/screen_handler.hpp" - -extern TaskHandle_t priceNotifyTaskHandle; - -void setupPriceNotify(); -void setupPriceNotifyTask(); -void taskPriceNotify(void *pvParameters); - -void onWebsocketPriceEvent(WStype_t type, uint8_t * payload, size_t length); - -uint getPrice(char currency); -void setPrice(uint newPrice, char currency); - -void processNewPrice(uint newPrice, char currency); - -bool isPriceNotifyConnected(); -void stopPriceNotify(); -void restartPriceNotify(); - -bool getPriceNotifyInit(); -uint getLastPriceUpdate(char currency); -void loadStoredPrices(); \ No newline at end of file diff --git a/src/lib/price_notify/interfaces/price_source.hpp b/src/lib/price_notify/interfaces/price_source.hpp new file mode 100644 index 0000000..7762737 --- /dev/null +++ b/src/lib/price_notify/interfaces/price_source.hpp @@ -0,0 +1,53 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace PriceNotify { + +using std::function; +using std::map; +using std::string; + +// Convert char-based currency to enum for type safety +enum class Currency : char { + USD = '$', + EUR = '[', + GBP = ']', + JPY = '^', + AUD = '_', + CAD = '`' +}; + +class IPriceSource { +public: + virtual ~IPriceSource() = default; + + // Initialize and connect to the websocket + virtual void connect() = 0; + + // Disconnect and cleanup + virtual void disconnect() = 0; + + // Check connection status + virtual bool isConnected() const = 0; + + // Get the last known price for a currency + virtual uint64_t getPrice(Currency currency) const = 0; + + // Get the last update timestamp for a currency + virtual uint32_t getLastUpdate(Currency currency) const = 0; + + // Set callback for price updates + virtual void onPriceUpdate(function callback) = 0; + + // Process websocket loop - should be called regularly + virtual void loop() = 0; + +protected: + function priceUpdateCallback; +}; +} // namespace PriceNotify \ No newline at end of file diff --git a/src/lib/price_notify/price_notify.cpp b/src/lib/price_notify/price_notify.cpp new file mode 100644 index 0000000..7381e00 --- /dev/null +++ b/src/lib/price_notify/price_notify.cpp @@ -0,0 +1,115 @@ +#include "price_notify.hpp" +#include + +using std::make_unique; + +namespace PriceNotify { + +PriceNotifyManager::PriceNotifyManager() : currentSource(PriceSource::NONE) {} + +PriceNotifyManager::~PriceNotifyManager() { + disconnect(); +} + +void PriceNotifyManager::init(PriceSource source) { + currentSource = source; + setPriceSource(source); +} + +void PriceNotifyManager::connect() { + if (priceSource) { + priceSource->connect(); + } +} + +void PriceNotifyManager::disconnect() { + if (priceSource) { + priceSource->disconnect(); + } +} + +bool PriceNotifyManager::isConnected() const { + return priceSource ? priceSource->isConnected() : false; +} + +uint64_t PriceNotifyManager::getPrice(Currency currency) const { + if (priceSource) { + return priceSource->getPrice(currency); + } + // If no price source, return from internal storage + auto it = prices.find(currency); + return (it != prices.end()) ? it->second : 0; +} + +uint32_t PriceNotifyManager::getLastUpdate(Currency currency) const { + if (priceSource) { + return priceSource->getLastUpdate(currency); + } + // If no price source, return from internal storage + auto it = lastUpdates.find(currency); + return (it != lastUpdates.end()) ? it->second : 0; +} + +void PriceNotifyManager::onPriceUpdate(function callback) { + userCallback = callback; + if (priceSource) { + priceSource->onPriceUpdate([this](Currency currency, uint64_t price) { + if (userCallback) { + userCallback(currency, price); + } + }); + } +} + +void PriceNotifyManager::loop() { + if (priceSource) { + priceSource->loop(); + } +} + +void PriceNotifyManager::setPriceSource(PriceSource source) { + // Store the callback before destroying the old source + auto oldCallback = userCallback; + + // Disconnect and destroy old source + if (priceSource) { + priceSource->disconnect(); + } + + // Create new source + switch (source) { + case PriceSource::COINCAP: + priceSource = make_unique(); + break; + case PriceSource::KRAKEN: + priceSource = make_unique(); + break; + case PriceSource::NONE: + priceSource.reset(); + break; + } + + currentSource = source; + + // Restore callback if it exists + if (oldCallback) { + onPriceUpdate(oldCallback); + } +} + +PriceSource PriceNotifyManager::getCurrentSource() const { + return currentSource; +} + +void PriceNotifyManager::processNewPrice(uint64_t price, Currency currency) { + // Store the price and timestamp + prices[currency] = price; + lastUpdates[currency] = esp_timer_get_time() / 1000000; // Current time in seconds + + // If we have a callback, notify about the price update + if (userCallback) { + userCallback(currency, price); + } +} + +} // namespace PriceNotify \ No newline at end of file diff --git a/src/lib/price_notify/price_notify.hpp b/src/lib/price_notify/price_notify.hpp new file mode 100644 index 0000000..0959a4c --- /dev/null +++ b/src/lib/price_notify/price_notify.hpp @@ -0,0 +1,68 @@ +#pragma once + +#include "interfaces/price_source.hpp" +#include "sources/coincap_source.hpp" +#include "sources/kraken_source.hpp" +#include +#include + +namespace PriceNotify { + +using std::unique_ptr; +using std::function; +using std::map; + +enum class PriceSource { + NONE, // Added for when no explicit source is set + COINCAP, + KRAKEN +}; + +class PriceNotifyManager { +public: + PriceNotifyManager(); + ~PriceNotifyManager(); + + // Initialize with a specific price source + void init(PriceSource source); + + // Connect to the price source + void connect(); + + // Disconnect from the price source + void disconnect(); + + // Check if connected to price source + bool isConnected() const; + + // Get the last known price for a currency + uint64_t getPrice(Currency currency) const; + + // Get the last update timestamp for a currency + uint32_t getLastUpdate(Currency currency) const; + + // Set callback for price updates + void onPriceUpdate(function callback); + + // Process websocket loop - should be called regularly + void loop(); + + // Change the price source + void setPriceSource(PriceSource source); + + // Get current price source + PriceSource getCurrentSource() const; + + // Process new price from external source + void processNewPrice(uint64_t price, Currency currency); + +private: + unique_ptr priceSource; + PriceSource currentSource; + function userCallback; + + // For storing prices when no explicit source is set + map prices; + map lastUpdates; +}; +} // namespace PriceNotify \ No newline at end of file diff --git a/src/lib/price_notify/sources/coincap_source.cpp b/src/lib/price_notify/sources/coincap_source.cpp new file mode 100644 index 0000000..63691a9 --- /dev/null +++ b/src/lib/price_notify/sources/coincap_source.cpp @@ -0,0 +1,96 @@ +#include "coincap_source.hpp" + +namespace PriceNotify { + +CoinCapSource* CoinCapSource::instance = nullptr; + +CoinCapSource::CoinCapSource() : connected(false) { + instance = this; +} + +CoinCapSource::~CoinCapSource() { + disconnect(); + instance = nullptr; +} + +void CoinCapSource::connect() { + webSocket.beginSSL("ws.coincap.io", 443, "/prices?assets=bitcoin"); + webSocket.onEvent([](WStype_t type, uint8_t* payload, size_t length) { + if (instance) { + instance->handleWebSocketEvent(type, payload, length); + } + }); + webSocket.setReconnectInterval(5000); + webSocket.enableHeartbeat(15000, 3000, 2); +} + +void CoinCapSource::disconnect() { + webSocket.disconnect(); + connected = false; +} + +bool CoinCapSource::isConnected() const { + return connected && webSocket.isConnected(); +} + +uint64_t CoinCapSource::getPrice(Currency currency) const { + auto it = prices.find(currency); + return (it != prices.end()) ? it->second : 0; +} + +uint32_t CoinCapSource::getLastUpdate(Currency currency) const { + auto it = lastUpdates.find(currency); + return (it != lastUpdates.end()) ? it->second : 0; +} + +void CoinCapSource::onPriceUpdate(function callback) { + priceUpdateCallback = callback; +} + +void CoinCapSource::loop() { + webSocket.loop(); +} + +void CoinCapSource::handleWebSocketEvent(WStype_t type, uint8_t* payload, size_t length) { + switch(type) { + case WStype_DISCONNECTED: + Serial.println(F("CoinCap WS Disconnected")); + connected = false; + break; + + case WStype_CONNECTED: + Serial.println(F("CoinCap WS Connected")); + connected = true; + break; + + case WStype_TEXT: + processMessage((char*)payload); + break; + + default: + break; + } +} + +void CoinCapSource::processMessage(const char* payload) { + StaticJsonDocument<512> doc; + DeserializationError error = deserializeJson(doc, payload); + + if (error) { + Serial.println(F("Failed to parse CoinCap message")); + return; + } + + if (doc["bitcoin"].is()) { + uint64_t price = doc["bitcoin"].as(); + uint32_t timestamp = esp_timer_get_time() / 1000000; // Current time in seconds + + prices[Currency::USD] = price; + lastUpdates[Currency::USD] = timestamp; + + if (priceUpdateCallback) { + priceUpdateCallback(Currency::USD, price); + } + } +} +} // namespace PriceNotify \ No newline at end of file diff --git a/src/lib/price_notify/sources/coincap_source.hpp b/src/lib/price_notify/sources/coincap_source.hpp new file mode 100644 index 0000000..3aa1b8c --- /dev/null +++ b/src/lib/price_notify/sources/coincap_source.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include "../interfaces/price_source.hpp" +#include +#include + +namespace PriceNotify { + +using std::map; + +class CoinCapSource : public IPriceSource { +public: + CoinCapSource(); + ~CoinCapSource() override; + + void connect() override; + void disconnect() override; + bool isConnected() const override; + uint64_t getPrice(Currency currency) const override; + uint32_t getLastUpdate(Currency currency) const override; + void onPriceUpdate(function callback) override; + void loop() override; + +private: + static void onWebSocketEvent(WStype_t type, uint8_t* payload, size_t length); + void handleWebSocketEvent(WStype_t type, uint8_t* payload, size_t length); + void processMessage(const char* payload); + + WebSocketsClient webSocket; + map prices; + map lastUpdates; + bool connected; + + static CoinCapSource* instance; // For callback handling +}; +} // namespace PriceNotify \ No newline at end of file diff --git a/src/lib/price_notify/sources/kraken_source.cpp b/src/lib/price_notify/sources/kraken_source.cpp new file mode 100644 index 0000000..69ca9b6 --- /dev/null +++ b/src/lib/price_notify/sources/kraken_source.cpp @@ -0,0 +1,118 @@ +#include "kraken_source.hpp" + +namespace PriceNotify { + +KrakenSource* KrakenSource::instance = nullptr; + +KrakenSource::KrakenSource() : connected(false) { + instance = this; +} + +KrakenSource::~KrakenSource() { + disconnect(); + instance = nullptr; +} + +void KrakenSource::connect() { + webSocket.beginSSL("ws.kraken.com", 443, "/ws"); + webSocket.onEvent([](WStype_t type, uint8_t* payload, size_t length) { + if (instance) { + instance->handleWebSocketEvent(type, payload, length); + } + }); + webSocket.setReconnectInterval(5000); + webSocket.enableHeartbeat(15000, 3000, 2); +} + +void KrakenSource::disconnect() { + webSocket.disconnect(); + connected = false; +} + +bool KrakenSource::isConnected() const { + return connected && webSocket.isConnected(); +} + +uint64_t KrakenSource::getPrice(Currency currency) const { + auto it = prices.find(currency); + return (it != prices.end()) ? it->second : 0; +} + +uint32_t KrakenSource::getLastUpdate(Currency currency) const { + auto it = lastUpdates.find(currency); + return (it != lastUpdates.end()) ? it->second : 0; +} + +void KrakenSource::onPriceUpdate(function callback) { + priceUpdateCallback = callback; +} + +void KrakenSource::loop() { + webSocket.loop(); +} + +void KrakenSource::handleWebSocketEvent(WStype_t type, uint8_t* payload, size_t length) { + switch(type) { + case WStype_DISCONNECTED: + Serial.println(F("Kraken WS Disconnected")); + connected = false; + break; + + case WStype_CONNECTED: + Serial.println(F("Kraken WS Connected")); + connected = true; + subscribe(); + break; + + case WStype_TEXT: + processMessage((char*)payload); + break; + + default: + break; + } +} + +void KrakenSource::subscribe() { + // Subscribe to XBT/USD ticker + StaticJsonDocument<256> doc; + doc["event"] = "subscribe"; + JsonArray pair = doc.createNestedArray("pair"); + pair.add("XBT/USD"); + doc["subscription"]["name"] = "ticker"; + + String message; + serializeJson(doc, message); + webSocket.sendTXT(message); +} + +void KrakenSource::processMessage(const char* payload) { + StaticJsonDocument<512> doc; + DeserializationError error = deserializeJson(doc, payload); + + if (error) { + Serial.println(F("Failed to parse Kraken message")); + return; + } + + // Check if it's a ticker update (array format) + if (doc.is() && doc.size() >= 4) { + JsonArray arr = doc.as(); + if (arr[2] == "ticker" && arr[3] == "XBT/USD") { + JsonObject tickerData = arr[1].as(); + if (tickerData.containsKey("c")) { + // Get the first value from the "c" array which is the last trade price + uint64_t price = tickerData["c"][0].as() * 100; // Convert to cents + uint32_t timestamp = esp_timer_get_time() / 1000000; // Current time in seconds + + prices[Currency::USD] = price; + lastUpdates[Currency::USD] = timestamp; + + if (priceUpdateCallback) { + priceUpdateCallback(Currency::USD, price); + } + } + } + } +} +} // namespace PriceNotify \ No newline at end of file diff --git a/src/lib/price_notify/sources/kraken_source.hpp b/src/lib/price_notify/sources/kraken_source.hpp new file mode 100644 index 0000000..89701c0 --- /dev/null +++ b/src/lib/price_notify/sources/kraken_source.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include "../interfaces/price_source.hpp" +#include +#include + +namespace PriceNotify { + +using std::map; + +class KrakenSource : public IPriceSource { +public: + KrakenSource(); + ~KrakenSource() override; + + void connect() override; + void disconnect() override; + bool isConnected() const override; + uint64_t getPrice(Currency currency) const override; + uint32_t getLastUpdate(Currency currency) const override; + void onPriceUpdate(function callback) override; + void loop() override; + +private: + static void onWebSocketEvent(WStype_t type, uint8_t* payload, size_t length); + void handleWebSocketEvent(WStype_t type, uint8_t* payload, size_t length); + void processMessage(const char* payload); + void subscribe(); + + WebSocketsClient webSocket; + map prices; + map lastUpdates; + bool connected; + + static KrakenSource* instance; // For callback handling +}; +} // namespace PriceNotify \ No newline at end of file diff --git a/src/lib/screen_handler.cpp b/src/lib/screen_handler.cpp index 75e59aa..07cc626 100644 --- a/src/lib/screen_handler.cpp +++ b/src/lib/screen_handler.cpp @@ -241,7 +241,7 @@ void workerTask(void *pvParameters) { case TASK_PRICE_UPDATE: { uint currency = ScreenHandler::getCurrentCurrency(); - uint price = getPrice(currency); + uint price = priceManager.getPrice(static_cast(currency)); if (currentScreenValue == SCREEN_BTC_TICKER) { taskEpdContent = parsePriceData(price, currency, preferences.getBool("suffixPrice", DEFAULT_SUFFIX_PRICE), @@ -251,8 +251,9 @@ void workerTask(void *pvParameters) { } else if (currentScreenValue == SCREEN_SATS_PER_CURRENCY) { taskEpdContent = parseSatsPerCurrency(price, currency, preferences.getBool("useSatsSymbol", DEFAULT_USE_SATS_SYMBOL)); } else { - auto& blockNotify = BlockNotify::getInstance(); - taskEpdContent = parseMarketCap(blockNotify.getBlockHeight(), price, currency, preferences.getBool("mcapBigChar", DEFAULT_MCAP_BIG_CHAR)); + taskEpdContent = + parseMarketCap(getBlockHeight(), price, currency, + preferences.getBool("mcapBigChar", DEFAULT_MCAP_BIG_CHAR)); } EPDManager::getInstance().setContent(taskEpdContent); @@ -260,19 +261,16 @@ void workerTask(void *pvParameters) { } case TASK_FEE_UPDATE: { if (currentScreenValue == SCREEN_BLOCK_FEE_RATE) { - auto& blockNotify = BlockNotify::getInstance(); - taskEpdContent = parseBlockFees(static_cast(blockNotify.getBlockMedianFee())); + taskEpdContent = parseBlockFees(static_cast(getBlockMedianFee())); EPDManager::getInstance().setContent(taskEpdContent); } break; } case TASK_BLOCK_UPDATE: { if (currentScreenValue != SCREEN_HALVING_COUNTDOWN) { - auto& blockNotify = BlockNotify::getInstance(); - taskEpdContent = parseBlockHeight(blockNotify.getBlockHeight()); + taskEpdContent = parseBlockHeight(getBlockHeight()); } else { - auto& blockNotify = BlockNotify::getInstance(); - taskEpdContent = parseHalvingCountdown(blockNotify.getBlockHeight(), preferences.getBool("useBlkCountdown", DEFAULT_USE_BLOCK_COUNTDOWN)); + taskEpdContent = parseHalvingCountdown(getBlockHeight(), preferences.getBool("useBlkCountdown", DEFAULT_USE_BLOCK_COUNTDOWN)); } if (currentScreenValue == SCREEN_HALVING_COUNTDOWN || diff --git a/src/lib/shared.cpp b/src/lib/shared.cpp index b8efc0c..aa67768 100644 --- a/src/lib/shared.cpp +++ b/src/lib/shared.cpp @@ -40,39 +40,39 @@ // "MrY=\n" // "-----END CERTIFICATE-----\n"; -// const char* isrg_root_x1cert = R"EOF( -// -----BEGIN CERTIFICATE----- -// MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw -// TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh -// cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 -// WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu -// ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY -// MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc -// h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ -// 0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U -// A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW -// T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH -// B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC -// B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv -// KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn -// OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn -// jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw -// qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI -// rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV -// HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq -// hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL -// ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ -// 3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK -// NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 -// ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur -// TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC -// jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc -// oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq -// 4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA -// mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d -// emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= -// -----END CERTIFICATE----- -// )EOF"; +const char* isrg_root_x1cert = R"EOF( +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 +WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu +ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc +h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ +0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U +A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW +T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH +B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC +B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv +KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn +OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn +jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw +qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI +rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq +hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ +3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK +NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 +ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur +TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC +jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc +oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq +4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA +mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d +emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- +)EOF"; #ifdef TEST_SCREENS diff --git a/src/lib/shared.hpp b/src/lib/shared.hpp index 54d48aa..479a52f 100644 --- a/src/lib/shared.hpp +++ b/src/lib/shared.hpp @@ -68,7 +68,7 @@ const int usPerSecond = 1000000; const int usPerMinute = 60 * usPerSecond; // extern const char *github_root_ca; -// extern const char *isrg_root_x1cert; +extern const char *isrg_root_x1cert; extern const uint8_t rootca_crt_bundle_start[] asm("_binary_x509_crt_bundle_start"); // extern const uint8_t ocean_logo_comp[] asm("_binary_ocean_gz_start"); diff --git a/src/lib/v2_notify.cpp b/src/lib/v2_notify.cpp index b915518..85d70b3 100644 --- a/src/lib/v2_notify.cpp +++ b/src/lib/v2_notify.cpp @@ -1,4 +1,5 @@ #include "v2_notify.hpp" +#include "globals.hpp" using namespace V2Notify; @@ -127,42 +128,34 @@ namespace V2Notify void handleV2Message(JsonDocument doc) { - if (doc["blockheight"].is()) + if (doc["blockheight"].is()) { uint newBlockHeight = doc["blockheight"].as(); - if (newBlockHeight == BlockNotify::getInstance().getBlockHeight()) + if (newBlockHeight == getBlockHeight()) { return; } - if (debugLogEnabled()) { - Serial.print(F("processNewBlock ")); - Serial.println(newBlockHeight); - } - BlockNotify::getInstance().processNewBlock(newBlockHeight); + processNewBlock(newBlockHeight); } - else if (doc["blockfee"].is()) + else if (doc["blockfee"].is()) { uint medianFee = doc["blockfee"].as(); - if (debugLogEnabled()) { - Serial.print(F("processNewBlockFee ")); - Serial.println(medianFee); - } - - BlockNotify::getInstance().processNewBlockFee(medianFee); + processNewBlockFee(medianFee); } else if (doc["price"].is()) { - // 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)); + // Convert currency string to PriceNotify::Currency using data_handler's conversion + char currencyChar = getCurrencyChar(currency); + priceManager.processNewPrice(newPrice, static_cast(currencyChar)); } } } @@ -172,7 +165,7 @@ namespace V2Notify for (;;) { webSocket.loop(); - vTaskDelay(pdMS_TO_TICKS(10)); + vTaskDelay(10 / portTICK_PERIOD_MS); } } diff --git a/src/lib/webserver.cpp b/src/lib/webserver.cpp index 6ee62f9..74d7165 100644 --- a/src/lib/webserver.cpp +++ b/src/lib/webserver.cpp @@ -5,7 +5,7 @@ static const char* JSON_CONTENT = "application/json"; static const char *const PROGMEM strSettings[] = { - "hostnamePrefix", "mempoolInstance", "nostrPubKey", "nostrRelay", "bitaxeHostname", "miningPoolName", "miningPoolUser", "nostrZapPubkey", "httpAuthUser", "httpAuthPass", "gitReleaseUrl", "poolLogosUrl", "ceEndpoint", "fontName", "localPoolEndpoint", "tzString"}; + "hostnamePrefix", "mempoolInstance", "nostrPubKey", "nostrRelay", "bitaxeHostname", "miningPoolName", "miningPoolUser", "nostrZapPubkey", "httpAuthUser", "httpAuthPass", "gitReleaseUrl", "poolLogosUrl", "ceEndpoint", "fontName"}; static const char *const PROGMEM uintSettings[] = {"minSecPriceUpd", "fullRefreshMin", "ledBrightness", "flMaxBrightness", "flEffectDelay", "luxLightToggle", "wpTimeout"}; @@ -30,8 +30,7 @@ TaskHandle_t eventSourceTaskHandle; void setupWebserver() { events.onConnect([](AsyncEventSourceClient *client) - { client->send("welcome", NULL, millis(), 1000); - }); + { client->send("welcome", NULL, millis(), 1000); }); server.addHandler(&events); AsyncStaticWebHandler &staticHandler = server.serveStatic("/", LittleFS, "/").setDefaultFile("index.html"); @@ -247,9 +246,8 @@ JsonDocument getStatusObject() JsonObject conStatus = root["connectionStatus"].to(); - conStatus["price"] = isPriceNotifyConnected(); - auto& blockNotify = BlockNotify::getInstance(); - conStatus["blocks"] = blockNotify.isConnected(); + conStatus["price"] = priceManager.isConnected(); + conStatus["blocks"] = isBlockNotifyConnected(); conStatus["V2"] = V2Notify::isV2NotifyConnected(); conStatus["nostr"] = nostrConnected(); @@ -298,13 +296,7 @@ JsonDocument getLedStatusObject() uint blue = pixColor & 0xFF; char hexColor[8]; snprintf(hexColor, sizeof(hexColor), "#%02X%02X%02X", red, green, blue); - // colors.add(hexColor); - - JsonObject object = colors.add(); - object["red"] = red; - object["green"] = green; - object["blue"] = blue; - object["hex"] = hexColor; + colors.add(hexColor); } return root; @@ -313,18 +305,14 @@ JsonDocument getLedStatusObject() void eventSourceUpdate() { if (!events.count()) return; - static JsonDocument doc; - doc.clear(); - - JsonDocument root = getStatusObject(); - - root["leds"] = getLedStatusObject()["data"]; + JsonDocument doc = getStatusObject(); + doc["leds"] = getLedStatusObject()["data"]; // Get current EPD content directly as array std::array epdContent = EPDManager::getInstance().getCurrentContent(); // Add EPD content arrays - JsonArray data = root["data"].to(); + JsonArray data = doc["data"].to(); // Copy array elements directly for(const auto& content : epdContent) { @@ -332,7 +320,7 @@ void eventSourceUpdate() { } String buffer; - serializeJson(root, buffer); + serializeJson(doc, buffer); events.send(buffer.c_str(), "status"); } @@ -698,9 +686,8 @@ void onApiSettingsGet(AsyncWebServerRequest *request) root["fullRefreshMin"] = preferences.getUInt("fullRefreshMin", DEFAULT_MINUTES_FULL_REFRESH); root["wpTimeout"] = preferences.getUInt("wpTimeout", DEFAULT_WP_TIMEOUT); - //root["tzOffset"] = preferences.getInt("gmtOffset", DEFAULT_TIME_OFFSET_SECONDS) / 60; - root["tzString"] = preferences.getString("tzString", DEFAULT_TZ_STRING); - + root["tzOffset"] = preferences.getInt("gmtOffset", DEFAULT_TIME_OFFSET_SECONDS) / 60; + // Add data source settings root["dataSource"] = preferences.getUChar("dataSource", DEFAULT_DATA_SOURCE); @@ -708,9 +695,6 @@ void onApiSettingsGet(AsyncWebServerRequest *request) root["mempoolInstance"] = preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE); root["mempoolSecure"] = preferences.getBool("mempoolSecure", DEFAULT_MEMPOOL_SECURE); - // Local pool settings - root["localPoolEndpoint"] = preferences.getString("localPoolEndpoint", DEFAULT_LOCAL_POOL_ENDPOINT); - // Nostr settings (used for NOSTR_SOURCE or when zapNotify is enabled) root["nostrPubKey"] = preferences.getString("nostrPubKey", DEFAULT_NOSTR_NPUB); root["nostrRelay"] = preferences.getString("nostrRelay", DEFAULT_NOSTR_RELAY); @@ -922,7 +906,7 @@ void onApiStopDataSources(AsyncWebServerRequest *request) request->beginResponseStream(JSON_CONTENT); stopPriceNotify(); - BlockNotify::getInstance().stop(); + stopBlockNotify(); request->send(response); } @@ -933,7 +917,9 @@ void onApiRestartDataSources(AsyncWebServerRequest *request) request->beginResponseStream(JSON_CONTENT); restartPriceNotify(); - BlockNotify::getInstance().restart(); + restartBlockNotify(); + // setupPriceNotify(); + // setupBlockNotify(); request->send(response); } diff --git a/src/lib/webserver.hpp b/src/lib/webserver.hpp index ddd6b73..39e66d1 100644 --- a/src/lib/webserver.hpp +++ b/src/lib/webserver.hpp @@ -11,7 +11,7 @@ #include "lib/block_notify.hpp" #include "lib/led_handler.hpp" -#include "lib/price_notify.hpp" + #include "lib/screen_handler.hpp" #include "webserver/OneParamRewrite.hpp" #include "lib/mining_pool/pool_factory.hpp" diff --git a/src/main.cpp b/src/main.cpp index 08d38ad..c892a03 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -19,7 +19,6 @@ #include "ESPAsyncWebServer.h" #include "lib/config.hpp" #include "lib/led_handler.hpp" -#include "lib/block_notify.hpp" uint wifiLostConnection; uint priceNotifyLostConnection = 0; @@ -50,8 +49,7 @@ void handleBlockNotifyDisconnection() { if ((getUptime() - blockNotifyLostConnection) > 300) { // 5 minutes timeout Serial.println(F("Block notification connection lost for 5 minutes, restarting handler...")); - auto& blockNotify = BlockNotify::getInstance(); - blockNotify.restart(); + restartBlockNotify(); blockNotifyLostConnection = 0; } } @@ -94,14 +92,13 @@ void checkWiFiConnection() { void checkMissedBlocks() { Serial.println(F("Long time (45 min) since last block, checking if I missed anything...")); - auto& blockNotify = BlockNotify::getInstance(); - int currentBlock = blockNotify.fetchLatestBlock(); + int currentBlock = getBlockFetch(); if (currentBlock != -1) { - if (currentBlock != blockNotify.getBlockHeight()) { + if (currentBlock != getBlockHeight()) { Serial.println(F("Detected stuck block height... restarting block handler.")); - blockNotify.restart(); + restartBlockNotify(); } - blockNotify.setLastBlockUpdate(getUptime()); + setLastBlockUpdate(getUptime()); } } @@ -114,10 +111,9 @@ void monitorDataConnections() { } // Block notification monitoring - auto& blockNotify = BlockNotify::getInstance(); - if (blockNotify.isInitialized() && !blockNotify.isConnected()) { + if (getBlockNotifyInit() && !isBlockNotifyConnected()) { handleBlockNotifyDisconnection(); - } else if (blockNotifyLostConnection > 0 && blockNotify.isConnected()) { + } else if (blockNotifyLostConnection > 0 && isBlockNotifyConnected()) { blockNotifyLostConnection = 0; } @@ -129,7 +125,7 @@ void monitorDataConnections() { } // Check for missed blocks - if ((blockNotify.getLastBlockUpdate() - getUptime()) > 45 * 60) { + if ((getLastBlockUpdate() - getUptime()) > 45 * 60) { checkMissedBlocks(); } }