diff --git a/.github/workflows/release-factory.yml b/.github/workflows/release-factory.yml index e2501044b..6ad59e65d 100644 --- a/.github/workflows/release-factory.yml +++ b/.github/workflows/release-factory.yml @@ -1,4 +1,4 @@ -name: Build & Release Factory Images [Release] +name: Build & Release Factory Images with Webhook [Release] on: release: diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index f72e4e7e7..c589c8830 100755 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -28,6 +28,7 @@ SRCS "./tasks/power_management_task.c" "./tasks/statistics_task.c" "./tasks/hashrate_monitor_task.c" + "./tasks/webhook_task.c" "./thermal/EMC2101.c" "./thermal/EMC2103.c" "./thermal/EMC2302.c" @@ -67,6 +68,7 @@ PRIV_REQUIRES "esp_psram" "esp_timer" "esp_wifi" + "esp_http_client" "json" "nvs_flash" "spiffs" diff --git a/main/http_server/axe-os/src/app/components/edit/edit.component.html b/main/http_server/axe-os/src/app/components/edit/edit.component.html index 528d92831..3275e5cfc 100644 --- a/main/http_server/axe-os/src/app/components/edit/edit.component.html +++ b/main/http_server/axe-os/src/app/components/edit/edit.component.html @@ -168,6 +168,36 @@

Statistics

+

Webhook

+ +
+
+
+ +
+ +
+ +
+ +
+ +
+
+ +
+ +
+ + +
+
+
+
diff --git a/main/http_server/axe-os/src/app/components/edit/edit.component.ts b/main/http_server/axe-os/src/app/components/edit/edit.component.ts index 95dee8fc3..691e38a43 100644 --- a/main/http_server/axe-os/src/app/components/edit/edit.component.ts +++ b/main/http_server/axe-os/src/app/components/edit/edit.component.ts @@ -172,6 +172,13 @@ export class EditComponent implements OnInit, OnDestroy, OnChanges { Validators.required, Validators.min(0), Validators.max(this.statsFrequencyMaxValue) + ]], + webhookEnabled: [(info as any).webhookEnabled == 1 || (info as any).webhookEnabled === true, [Validators.required]], + webhookUrl: [{value: (info as any).webhookUrl || '', disabled: !((info as any).webhookEnabled == 1 || (info as any).webhookEnabled === true)}, [Validators.required]], + webhookInterval: [{value: (info as any).webhookInterval || 60, disabled: !((info as any).webhookEnabled == 1 || (info as any).webhookEnabled === true)}, [ + Validators.required, + Validators.min(10), + Validators.max(3600) ]] }); @@ -190,6 +197,19 @@ export class EditComponent implements OnInit, OnDestroy, OnChanges { } }); + this.form.controls['webhookEnabled'].valueChanges.pipe( + startWith(this.form.controls['webhookEnabled'].value), + takeUntil(this.destroy$) + ).subscribe(webhookEnabled => { + if (webhookEnabled) { + this.form.controls['webhookUrl'].enable(); + this.form.controls['webhookInterval'].enable(); + } else { + this.form.controls['webhookUrl'].disable(); + this.form.controls['webhookInterval'].disable(); + } + }); + // Add custom value to predefined steps if (DISPLAY_TIMEOUT_STEPS.filter(x => x === info.displayTimeout).length === 0) { DISPLAY_TIMEOUT_STEPS.push(info.displayTimeout); @@ -341,7 +361,10 @@ export class EditComponent implements OnInit, OnDestroy, OnChanges { 'manualFanSpeed', 'temptarget', 'overheat_mode', - 'statsFrequency' + 'statsFrequency', + 'webhookEnabled', + 'webhookUrl', + 'webhookInterval' ]; } diff --git a/main/http_server/http_server.c b/main/http_server/http_server.c index d0ef19b25..80a15af70 100644 --- a/main/http_server/http_server.c +++ b/main/http_server/http_server.c @@ -794,171 +794,12 @@ static esp_err_t GET_system_info(httpd_req_t * req) return ESP_OK; } - char * ssid = nvs_config_get_string(NVS_CONFIG_WIFI_SSID); - char * hostname = nvs_config_get_string(NVS_CONFIG_HOSTNAME); - char * ipv4 = GLOBAL_STATE->SYSTEM_MODULE.ip_addr_str; - char * ipv6 = GLOBAL_STATE->SYSTEM_MODULE.ipv6_addr_str; - char * stratumURL = nvs_config_get_string(NVS_CONFIG_STRATUM_URL); - char * fallbackStratumURL = nvs_config_get_string(NVS_CONFIG_FALLBACK_STRATUM_URL); - char * stratumUser = nvs_config_get_string(NVS_CONFIG_STRATUM_USER); - char * fallbackStratumUser = nvs_config_get_string(NVS_CONFIG_FALLBACK_STRATUM_USER); - char * stratumCert = nvs_config_get_string(NVS_CONFIG_STRATUM_CERT); - char * fallbackStratumCert = nvs_config_get_string(NVS_CONFIG_FALLBACK_STRATUM_CERT); - char * display = nvs_config_get_string(NVS_CONFIG_DISPLAY); - float frequency = nvs_config_get_float(NVS_CONFIG_ASIC_FREQUENCY); - - uint8_t mac[6]; - esp_wifi_get_mac(WIFI_IF_STA, mac); - char formattedMac[18]; - snprintf(formattedMac, sizeof(formattedMac), "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); - - int8_t wifi_rssi = -90; - get_wifi_current_rssi(&wifi_rssi); - - cJSON * root = cJSON_CreateObject(); - cJSON_AddFloatToObject(root, "power", GLOBAL_STATE->POWER_MANAGEMENT_MODULE.power); - cJSON_AddFloatToObject(root, "voltage", GLOBAL_STATE->POWER_MANAGEMENT_MODULE.voltage); - cJSON_AddFloatToObject(root, "current", GLOBAL_STATE->POWER_MANAGEMENT_MODULE.current); - cJSON_AddFloatToObject(root, "temp", GLOBAL_STATE->POWER_MANAGEMENT_MODULE.chip_temp_avg); - cJSON_AddFloatToObject(root, "temp2", GLOBAL_STATE->POWER_MANAGEMENT_MODULE.chip_temp2_avg); - cJSON_AddFloatToObject(root, "vrTemp", GLOBAL_STATE->POWER_MANAGEMENT_MODULE.vr_temp); - cJSON_AddNumberToObject(root, "maxPower", GLOBAL_STATE->DEVICE_CONFIG.family.max_power); - cJSON_AddNumberToObject(root, "nominalVoltage", GLOBAL_STATE->DEVICE_CONFIG.family.nominal_voltage); - cJSON_AddFloatToObject(root, "hashRate", GLOBAL_STATE->SYSTEM_MODULE.current_hashrate); - cJSON_AddFloatToObject(root, "hashRate_1m", GLOBAL_STATE->SYSTEM_MODULE.hashrate_1m); - cJSON_AddFloatToObject(root, "hashRate_10m", GLOBAL_STATE->SYSTEM_MODULE.hashrate_10m); - cJSON_AddFloatToObject(root, "hashRate_1h", GLOBAL_STATE->SYSTEM_MODULE.hashrate_1h); - cJSON_AddFloatToObject(root, "expectedHashrate", GLOBAL_STATE->POWER_MANAGEMENT_MODULE.expected_hashrate); - cJSON_AddFloatToObject(root, "errorPercentage", GLOBAL_STATE->SYSTEM_MODULE.error_percentage); - cJSON_AddNumberToObject(root, "bestDiff", GLOBAL_STATE->SYSTEM_MODULE.best_nonce_diff); - cJSON_AddNumberToObject(root, "bestSessionDiff", GLOBAL_STATE->SYSTEM_MODULE.best_session_nonce_diff); - cJSON_AddNumberToObject(root, "poolDifficulty", GLOBAL_STATE->pool_difficulty); - - cJSON_AddNumberToObject(root, "isUsingFallbackStratum", GLOBAL_STATE->SYSTEM_MODULE.is_using_fallback); - cJSON_AddStringToObject(root, "poolConnectionInfo", GLOBAL_STATE->SYSTEM_MODULE.pool_connection_info); - - cJSON_AddNumberToObject(root, "isPSRAMAvailable", GLOBAL_STATE->psram_is_available); - - cJSON_AddNumberToObject(root, "freeHeap", esp_get_free_heap_size()); - - cJSON_AddNumberToObject(root, "freeHeapInternal", heap_caps_get_free_size(MALLOC_CAP_INTERNAL)); - cJSON_AddNumberToObject(root, "freeHeapSpiram", heap_caps_get_free_size(MALLOC_CAP_SPIRAM)); - - cJSON_AddNumberToObject(root, "coreVoltage", nvs_config_get_u16(NVS_CONFIG_ASIC_VOLTAGE)); - cJSON_AddNumberToObject(root, "coreVoltageActual", GLOBAL_STATE->POWER_MANAGEMENT_MODULE.core_voltage); - cJSON_AddNumberToObject(root, "frequency", frequency); - cJSON_AddStringToObject(root, "ssid", ssid); - cJSON_AddStringToObject(root, "macAddr", formattedMac); - cJSON_AddStringToObject(root, "hostname", hostname); - cJSON_AddStringToObject(root, "ipv4", ipv4); - cJSON_AddStringToObject(root, "ipv6", ipv6); - cJSON_AddStringToObject(root, "wifiStatus", GLOBAL_STATE->SYSTEM_MODULE.wifi_status); - cJSON_AddNumberToObject(root, "wifiRSSI", wifi_rssi); - cJSON_AddNumberToObject(root, "apEnabled", GLOBAL_STATE->SYSTEM_MODULE.ap_enabled); - cJSON_AddNumberToObject(root, "sharesAccepted", GLOBAL_STATE->SYSTEM_MODULE.shares_accepted); - cJSON_AddNumberToObject(root, "sharesRejected", GLOBAL_STATE->SYSTEM_MODULE.shares_rejected); - - cJSON *error_array = cJSON_CreateArray(); - cJSON_AddItemToObject(root, "sharesRejectedReasons", error_array); - - for (int i = 0; i < GLOBAL_STATE->SYSTEM_MODULE.rejected_reason_stats_count; i++) { - cJSON *error_obj = cJSON_CreateObject(); - cJSON_AddStringToObject(error_obj, "message", GLOBAL_STATE->SYSTEM_MODULE.rejected_reason_stats[i].message); - cJSON_AddNumberToObject(error_obj, "count", GLOBAL_STATE->SYSTEM_MODULE.rejected_reason_stats[i].count); - cJSON_AddItemToArray(error_array, error_obj); - } - - cJSON_AddNumberToObject(root, "uptimeSeconds", (esp_timer_get_time() - GLOBAL_STATE->SYSTEM_MODULE.start_time) / 1000000); - cJSON_AddNumberToObject(root, "smallCoreCount", GLOBAL_STATE->DEVICE_CONFIG.family.asic.small_core_count); - cJSON_AddStringToObject(root, "ASICModel", GLOBAL_STATE->DEVICE_CONFIG.family.asic.name); - cJSON_AddStringToObject(root, "stratumURL", stratumURL); - cJSON_AddNumberToObject(root, "stratumPort", nvs_config_get_u16(NVS_CONFIG_STRATUM_PORT)); - cJSON_AddStringToObject(root, "stratumUser", stratumUser); - cJSON_AddNumberToObject(root, "stratumSuggestedDifficulty", nvs_config_get_u16(NVS_CONFIG_STRATUM_DIFFICULTY)); - cJSON_AddNumberToObject(root, "stratumExtranonceSubscribe", nvs_config_get_bool(NVS_CONFIG_STRATUM_EXTRANONCE_SUBSCRIBE)); - cJSON_AddNumberToObject(root, "stratumTLS", nvs_config_get_u16(NVS_CONFIG_STRATUM_TLS)); - cJSON_AddStringToObject(root, "stratumCert", stratumCert); - cJSON_AddStringToObject(root, "fallbackStratumURL", fallbackStratumURL); - cJSON_AddNumberToObject(root, "fallbackStratumPort", nvs_config_get_u16(NVS_CONFIG_FALLBACK_STRATUM_PORT)); - cJSON_AddStringToObject(root, "fallbackStratumUser", fallbackStratumUser); - cJSON_AddNumberToObject(root, "fallbackStratumSuggestedDifficulty", nvs_config_get_u16(NVS_CONFIG_FALLBACK_STRATUM_DIFFICULTY)); - cJSON_AddNumberToObject(root, "fallbackStratumExtranonceSubscribe", nvs_config_get_bool(NVS_CONFIG_FALLBACK_STRATUM_EXTRANONCE_SUBSCRIBE)); - cJSON_AddNumberToObject(root, "fallbackStratumTLS", nvs_config_get_u16(NVS_CONFIG_FALLBACK_STRATUM_TLS)); - cJSON_AddStringToObject(root, "fallbackStratumCert", fallbackStratumCert); - cJSON_AddNumberToObject(root, "responseTime", GLOBAL_STATE->SYSTEM_MODULE.response_time); - - cJSON_AddStringToObject(root, "version", GLOBAL_STATE->SYSTEM_MODULE.version); - cJSON_AddStringToObject(root, "axeOSVersion", GLOBAL_STATE->SYSTEM_MODULE.axeOSVersion); - - cJSON_AddStringToObject(root, "idfVersion", esp_get_idf_version()); - cJSON_AddStringToObject(root, "boardVersion", GLOBAL_STATE->DEVICE_CONFIG.board_version); - cJSON_AddStringToObject(root, "resetReason", esp_reset_reason_to_string(esp_reset_reason())); - cJSON_AddStringToObject(root, "runningPartition", esp_ota_get_running_partition()->label); - - cJSON_AddNumberToObject(root, "overheat_mode", nvs_config_get_bool(NVS_CONFIG_OVERHEAT_MODE)); - cJSON_AddNumberToObject(root, "overclockEnabled", nvs_config_get_bool(NVS_CONFIG_OVERCLOCK_ENABLED)); - cJSON_AddStringToObject(root, "display", display); - cJSON_AddNumberToObject(root, "rotation", nvs_config_get_u16(NVS_CONFIG_ROTATION)); - cJSON_AddNumberToObject(root, "invertscreen", nvs_config_get_bool(NVS_CONFIG_INVERT_SCREEN)); - cJSON_AddNumberToObject(root, "displayTimeout", nvs_config_get_i32(NVS_CONFIG_DISPLAY_TIMEOUT)); - - cJSON_AddNumberToObject(root, "autofanspeed", nvs_config_get_bool(NVS_CONFIG_AUTO_FAN_SPEED)); - - cJSON_AddFloatToObject(root, "fanspeed", GLOBAL_STATE->POWER_MANAGEMENT_MODULE.fan_perc); - cJSON_AddNumberToObject(root, "manualFanSpeed", nvs_config_get_u16(NVS_CONFIG_MANUAL_FAN_SPEED)); - cJSON_AddNumberToObject(root, "minFanSpeed", nvs_config_get_u16(NVS_CONFIG_MIN_FAN_SPEED)); - cJSON_AddNumberToObject(root, "temptarget", nvs_config_get_u16(NVS_CONFIG_TEMP_TARGET)); - cJSON_AddNumberToObject(root, "fanrpm", GLOBAL_STATE->POWER_MANAGEMENT_MODULE.fan_rpm); - cJSON_AddNumberToObject(root, "fan2rpm", GLOBAL_STATE->POWER_MANAGEMENT_MODULE.fan2_rpm); - - cJSON_AddNumberToObject(root, "statsFrequency", nvs_config_get_u16(NVS_CONFIG_STATISTICS_FREQUENCY)); - - cJSON_AddNumberToObject(root, "blockFound", GLOBAL_STATE->SYSTEM_MODULE.block_found); - - if (GLOBAL_STATE->SYSTEM_MODULE.power_fault > 0) { - cJSON_AddStringToObject(root, "power_fault", VCORE_get_fault_string(GLOBAL_STATE)); - } - - if (GLOBAL_STATE->block_height > 0) { - cJSON_AddNumberToObject(root, "blockHeight", GLOBAL_STATE->block_height); - cJSON_AddStringToObject(root, "scriptsig", GLOBAL_STATE->scriptsig); - cJSON_AddNumberToObject(root, "networkDifficulty", GLOBAL_STATE->network_nonce_diff); - } - - cJSON *hashrate_monitor = cJSON_CreateObject(); - cJSON_AddItemToObject(root, "hashrateMonitor", hashrate_monitor); - - cJSON *asics_array = cJSON_CreateArray(); - cJSON_AddItemToObject(hashrate_monitor, "asics", asics_array); - - if (GLOBAL_STATE->HASHRATE_MONITOR_MODULE.is_initialized) { - for (int asic_nr = 0; asic_nr < GLOBAL_STATE->DEVICE_CONFIG.family.asic_count; asic_nr++) { - cJSON *asic = cJSON_CreateObject(); - cJSON_AddItemToArray(asics_array, asic); - cJSON_AddFloatToObject(asic, "total", GLOBAL_STATE->HASHRATE_MONITOR_MODULE.total_measurement[asic_nr].hashrate); - - int hash_domains = GLOBAL_STATE->DEVICE_CONFIG.family.asic.hash_domains; - cJSON* hash_domain_array = cJSON_CreateArray(); - for (int domain_nr = 0; domain_nr < hash_domains; domain_nr++) { - cJSON *hashrate = cJSON_CreateFloat(GLOBAL_STATE->HASHRATE_MONITOR_MODULE.domain_measurements[asic_nr][domain_nr].hashrate); - cJSON_AddItemToArray(hash_domain_array, hashrate); - } - cJSON_AddItemToObject(asic, "domains", hash_domain_array); - - cJSON_AddNumberToObject(asic, "errorCount", GLOBAL_STATE->HASHRATE_MONITOR_MODULE.error_measurement[asic_nr].value); - } + cJSON * root = SYSTEM_create_info_json(GLOBAL_STATE); + if (root == NULL) { + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to create system info"); + return ESP_OK; } - free(ssid); - free(hostname); - free(stratumURL); - free(fallbackStratumURL); - free(stratumCert); - free(fallbackStratumCert); - free(stratumUser); - free(fallbackStratumUser); - free(display); - esp_err_t res = HTTP_send_json(req, root, &system_info_prebuffer_len); cJSON_Delete(root); diff --git a/main/main.c b/main/main.c index cb4536949..81661cf44 100644 --- a/main/main.c +++ b/main/main.c @@ -6,6 +6,7 @@ #include "create_jobs_task.h" #include "hashrate_monitor_task.h" #include "statistics_task.h" +#include "webhook_task.h" #include "system.h" #include "http_server.h" #include "serial.h" @@ -118,4 +119,7 @@ void app_main(void) if (xTaskCreateWithCaps(statistics_task, "statistics", 8192, (void *) &GLOBAL_STATE, 3, NULL, MALLOC_CAP_SPIRAM) != pdPASS) { ESP_LOGE(TAG, "Error creating statistics task"); } + if (xTaskCreate(webhook_task, "webhook", 8192, (void *) &GLOBAL_STATE, 2, NULL) != pdPASS) { + ESP_LOGE(TAG, "Error creating webhook task"); + } } diff --git a/main/nvs_config.c b/main/nvs_config.c index 5d5fdeea3..ea8162ef2 100644 --- a/main/nvs_config.c +++ b/main/nvs_config.c @@ -107,6 +107,10 @@ static Settings settings[NVS_CONFIG_COUNT] = { [NVS_CONFIG_TPS546] = {.nvs_key_name = "TPS546", .type = TYPE_BOOL}, [NVS_CONFIG_TMP1075] = {.nvs_key_name = "TMP1075", .type = TYPE_BOOL}, [NVS_CONFIG_POWER_CONSUMPTION_TARGET] = {.nvs_key_name = "power_cons_tgt", .type = TYPE_U16}, + + [NVS_CONFIG_WEBHOOK_ENABLED] = {.nvs_key_name = "webhook_enabled", .type = TYPE_BOOL, .default_value = {.b = false}, .rest_name = "webhookEnabled", .min = 0, .max = 1}, + [NVS_CONFIG_WEBHOOK_URL] = {.nvs_key_name = "webhook_url", .type = TYPE_STR, .default_value = {.str = ""}, .rest_name = "webhookUrl", .min = 0, .max = NVS_STR_LIMIT}, + [NVS_CONFIG_WEBHOOK_INTERVAL] = {.nvs_key_name = "webhook_interval", .type = TYPE_U16, .default_value = {.u16 = 60}, .rest_name = "webhookInterval", .min = 10, .max = 3600}, }; Settings *nvs_config_get_settings(NvsConfigKey key) diff --git a/main/nvs_config.h b/main/nvs_config.h index 14b600a2d..f1fee161d 100644 --- a/main/nvs_config.h +++ b/main/nvs_config.h @@ -70,6 +70,10 @@ typedef enum { NVS_CONFIG_TPS546, NVS_CONFIG_TMP1075, NVS_CONFIG_POWER_CONSUMPTION_TARGET, + + NVS_CONFIG_WEBHOOK_ENABLED, + NVS_CONFIG_WEBHOOK_URL, + NVS_CONFIG_WEBHOOK_INTERVAL, NVS_CONFIG_COUNT } NvsConfigKey; diff --git a/main/system.c b/main/system.c index 1293ea581..61e9a49cb 100644 --- a/main/system.c +++ b/main/system.c @@ -30,9 +30,49 @@ #include "vcore.h" #include "thermal.h" #include "utils.h" +#include "cJSON.h" +#include "esp_heap_caps.h" +#include "esp_ota_ops.h" +#include "esp_system.h" static const char * TAG = "system"; +// Helper functions for float JSON +static const double FACTOR = 10000000.0; + +static cJSON* cJSON_AddFloatToObject(cJSON * const object, const char * const name, const float number) { + double d_value = round((double)number * FACTOR) / FACTOR; + return cJSON_AddNumberToObject(object, name, d_value); +} + +static cJSON* cJSON_CreateFloat(float number) { + double d_value = round((double)number * FACTOR) / FACTOR; + return cJSON_CreateNumber(d_value); +} + +// Helper function to convert reset reason to string +static const char* esp_reset_reason_to_string(esp_reset_reason_t reason) { + switch (reason) { + case ESP_RST_UNKNOWN: return "Reset reason can not be determined"; + case ESP_RST_POWERON: return "Reset due to power-on event"; + case ESP_RST_EXT: return "Reset by external pin (not applicable for ESP32)"; + case ESP_RST_SW: return "Software reset via esp_restart"; + case ESP_RST_PANIC: return "Software reset due to exception/panic"; + case ESP_RST_INT_WDT: return "Reset (software or hardware) due to interrupt watchdog"; + case ESP_RST_TASK_WDT: return "Reset due to task watchdog"; + case ESP_RST_WDT: return "Reset due to other watchdogs"; + case ESP_RST_DEEPSLEEP: return "Reset after exiting deep sleep mode"; + case ESP_RST_BROWNOUT: return "Brownout reset (software or hardware)"; + case ESP_RST_SDIO: return "Reset over SDIO"; + case ESP_RST_USB: return "Reset by USB peripheral"; + case ESP_RST_JTAG: return "Reset by JTAG"; + case ESP_RST_EFUSE: return "Reset due to efuse error"; + case ESP_RST_PWR_GLITCH: return "Reset due to power glitch detected"; + case ESP_RST_CPU_LOCKUP: return "Reset due to CPU lock up (double exception)"; + default: return "Unknown reset"; + } +} + //local function prototypes static esp_err_t ensure_overheat_mode_config(); @@ -270,3 +310,174 @@ static esp_err_t ensure_overheat_mode_config() { return ESP_OK; } + +cJSON* SYSTEM_create_info_json(GlobalState *GLOBAL_STATE) +{ + char * ssid = nvs_config_get_string(NVS_CONFIG_WIFI_SSID); + char * hostname = nvs_config_get_string(NVS_CONFIG_HOSTNAME); + char * ipv4 = GLOBAL_STATE->SYSTEM_MODULE.ip_addr_str; + char * ipv6 = GLOBAL_STATE->SYSTEM_MODULE.ipv6_addr_str; + char * stratumURL = nvs_config_get_string(NVS_CONFIG_STRATUM_URL); + char * fallbackStratumURL = nvs_config_get_string(NVS_CONFIG_FALLBACK_STRATUM_URL); + char * stratumUser = nvs_config_get_string(NVS_CONFIG_STRATUM_USER); + char * fallbackStratumUser = nvs_config_get_string(NVS_CONFIG_FALLBACK_STRATUM_USER); + char * stratumCert = nvs_config_get_string(NVS_CONFIG_STRATUM_CERT); + char * fallbackStratumCert = nvs_config_get_string(NVS_CONFIG_FALLBACK_STRATUM_CERT); + char * display = nvs_config_get_string(NVS_CONFIG_DISPLAY); + float frequency = nvs_config_get_float(NVS_CONFIG_ASIC_FREQUENCY); + + uint8_t mac[6]; + esp_wifi_get_mac(WIFI_IF_STA, mac); + char formattedMac[18]; + snprintf(formattedMac, sizeof(formattedMac), "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + + int8_t wifi_rssi = -90; + get_wifi_current_rssi(&wifi_rssi); + + cJSON * root = cJSON_CreateObject(); + cJSON_AddFloatToObject(root, "power", GLOBAL_STATE->POWER_MANAGEMENT_MODULE.power); + cJSON_AddFloatToObject(root, "voltage", GLOBAL_STATE->POWER_MANAGEMENT_MODULE.voltage); + cJSON_AddFloatToObject(root, "current", GLOBAL_STATE->POWER_MANAGEMENT_MODULE.current); + cJSON_AddFloatToObject(root, "temp", GLOBAL_STATE->POWER_MANAGEMENT_MODULE.chip_temp_avg); + cJSON_AddFloatToObject(root, "temp2", GLOBAL_STATE->POWER_MANAGEMENT_MODULE.chip_temp2_avg); + cJSON_AddFloatToObject(root, "vrTemp", GLOBAL_STATE->POWER_MANAGEMENT_MODULE.vr_temp); + cJSON_AddNumberToObject(root, "maxPower", GLOBAL_STATE->DEVICE_CONFIG.family.max_power); + cJSON_AddNumberToObject(root, "nominalVoltage", GLOBAL_STATE->DEVICE_CONFIG.family.nominal_voltage); + cJSON_AddFloatToObject(root, "hashRate", GLOBAL_STATE->SYSTEM_MODULE.current_hashrate); + cJSON_AddFloatToObject(root, "hashRate_1m", GLOBAL_STATE->SYSTEM_MODULE.hashrate_1m); + cJSON_AddFloatToObject(root, "hashRate_10m", GLOBAL_STATE->SYSTEM_MODULE.hashrate_10m); + cJSON_AddFloatToObject(root, "hashRate_1h", GLOBAL_STATE->SYSTEM_MODULE.hashrate_1h); + cJSON_AddFloatToObject(root, "expectedHashrate", GLOBAL_STATE->POWER_MANAGEMENT_MODULE.expected_hashrate); + cJSON_AddFloatToObject(root, "errorPercentage", GLOBAL_STATE->SYSTEM_MODULE.error_percentage); + cJSON_AddNumberToObject(root, "bestDiff", GLOBAL_STATE->SYSTEM_MODULE.best_nonce_diff); + cJSON_AddNumberToObject(root, "bestSessionDiff", GLOBAL_STATE->SYSTEM_MODULE.best_session_nonce_diff); + cJSON_AddNumberToObject(root, "poolDifficulty", GLOBAL_STATE->pool_difficulty); + + cJSON_AddNumberToObject(root, "isUsingFallbackStratum", GLOBAL_STATE->SYSTEM_MODULE.is_using_fallback); + cJSON_AddStringToObject(root, "poolConnectionInfo", GLOBAL_STATE->SYSTEM_MODULE.pool_connection_info); + + cJSON_AddNumberToObject(root, "isPSRAMAvailable", GLOBAL_STATE->psram_is_available); + + cJSON_AddNumberToObject(root, "freeHeap", esp_get_free_heap_size()); + cJSON_AddNumberToObject(root, "freeHeapInternal", heap_caps_get_free_size(MALLOC_CAP_INTERNAL)); + cJSON_AddNumberToObject(root, "freeHeapSpiram", heap_caps_get_free_size(MALLOC_CAP_SPIRAM)); + + cJSON_AddNumberToObject(root, "coreVoltage", nvs_config_get_u16(NVS_CONFIG_ASIC_VOLTAGE)); + cJSON_AddNumberToObject(root, "coreVoltageActual", GLOBAL_STATE->POWER_MANAGEMENT_MODULE.core_voltage); + cJSON_AddNumberToObject(root, "frequency", frequency); + cJSON_AddStringToObject(root, "ssid", ssid); + cJSON_AddStringToObject(root, "macAddr", formattedMac); + cJSON_AddStringToObject(root, "hostname", hostname); + cJSON_AddStringToObject(root, "ipv4", ipv4); + cJSON_AddStringToObject(root, "ipv6", ipv6); + cJSON_AddStringToObject(root, "wifiStatus", GLOBAL_STATE->SYSTEM_MODULE.wifi_status); + cJSON_AddNumberToObject(root, "wifiRSSI", wifi_rssi); + cJSON_AddNumberToObject(root, "apEnabled", GLOBAL_STATE->SYSTEM_MODULE.ap_enabled); + cJSON_AddNumberToObject(root, "sharesAccepted", GLOBAL_STATE->SYSTEM_MODULE.shares_accepted); + cJSON_AddNumberToObject(root, "sharesRejected", GLOBAL_STATE->SYSTEM_MODULE.shares_rejected); + + cJSON *error_array = cJSON_CreateArray(); + cJSON_AddItemToObject(root, "sharesRejectedReasons", error_array); + + for (int i = 0; i < GLOBAL_STATE->SYSTEM_MODULE.rejected_reason_stats_count; i++) { + cJSON *error_obj = cJSON_CreateObject(); + cJSON_AddStringToObject(error_obj, "message", GLOBAL_STATE->SYSTEM_MODULE.rejected_reason_stats[i].message); + cJSON_AddNumberToObject(error_obj, "count", GLOBAL_STATE->SYSTEM_MODULE.rejected_reason_stats[i].count); + cJSON_AddItemToArray(error_array, error_obj); + } + + cJSON_AddNumberToObject(root, "uptimeSeconds", (esp_timer_get_time() - GLOBAL_STATE->SYSTEM_MODULE.start_time) / 1000000); + cJSON_AddNumberToObject(root, "smallCoreCount", GLOBAL_STATE->DEVICE_CONFIG.family.asic.small_core_count); + cJSON_AddStringToObject(root, "ASICModel", GLOBAL_STATE->DEVICE_CONFIG.family.asic.name); + cJSON_AddStringToObject(root, "stratumURL", stratumURL); + cJSON_AddNumberToObject(root, "stratumPort", nvs_config_get_u16(NVS_CONFIG_STRATUM_PORT)); + cJSON_AddStringToObject(root, "stratumUser", stratumUser); + cJSON_AddNumberToObject(root, "stratumSuggestedDifficulty", nvs_config_get_u16(NVS_CONFIG_STRATUM_DIFFICULTY)); + cJSON_AddNumberToObject(root, "stratumExtranonceSubscribe", nvs_config_get_bool(NVS_CONFIG_STRATUM_EXTRANONCE_SUBSCRIBE)); + cJSON_AddNumberToObject(root, "stratumTLS", nvs_config_get_u16(NVS_CONFIG_STRATUM_TLS)); + cJSON_AddStringToObject(root, "stratumCert", stratumCert); + cJSON_AddStringToObject(root, "fallbackStratumURL", fallbackStratumURL); + cJSON_AddNumberToObject(root, "fallbackStratumPort", nvs_config_get_u16(NVS_CONFIG_FALLBACK_STRATUM_PORT)); + cJSON_AddStringToObject(root, "fallbackStratumUser", fallbackStratumUser); + cJSON_AddNumberToObject(root, "fallbackStratumSuggestedDifficulty", nvs_config_get_u16(NVS_CONFIG_FALLBACK_STRATUM_DIFFICULTY)); + cJSON_AddNumberToObject(root, "fallbackStratumExtranonceSubscribe", nvs_config_get_bool(NVS_CONFIG_FALLBACK_STRATUM_EXTRANONCE_SUBSCRIBE)); + cJSON_AddNumberToObject(root, "fallbackStratumTLS", nvs_config_get_u16(NVS_CONFIG_FALLBACK_STRATUM_TLS)); + cJSON_AddStringToObject(root, "fallbackStratumCert", fallbackStratumCert); + cJSON_AddNumberToObject(root, "responseTime", GLOBAL_STATE->SYSTEM_MODULE.response_time); + + cJSON_AddStringToObject(root, "version", GLOBAL_STATE->SYSTEM_MODULE.version); + cJSON_AddStringToObject(root, "axeOSVersion", GLOBAL_STATE->SYSTEM_MODULE.axeOSVersion); + + cJSON_AddStringToObject(root, "idfVersion", esp_get_idf_version()); + cJSON_AddStringToObject(root, "boardVersion", GLOBAL_STATE->DEVICE_CONFIG.board_version); + cJSON_AddStringToObject(root, "resetReason", esp_reset_reason_to_string(esp_reset_reason())); + cJSON_AddStringToObject(root, "runningPartition", esp_ota_get_running_partition()->label); + + cJSON_AddNumberToObject(root, "overheat_mode", nvs_config_get_bool(NVS_CONFIG_OVERHEAT_MODE)); + cJSON_AddNumberToObject(root, "overclockEnabled", nvs_config_get_bool(NVS_CONFIG_OVERCLOCK_ENABLED)); + cJSON_AddStringToObject(root, "display", display); + cJSON_AddNumberToObject(root, "rotation", nvs_config_get_u16(NVS_CONFIG_ROTATION)); + cJSON_AddNumberToObject(root, "invertscreen", nvs_config_get_bool(NVS_CONFIG_INVERT_SCREEN)); + cJSON_AddNumberToObject(root, "displayTimeout", nvs_config_get_i32(NVS_CONFIG_DISPLAY_TIMEOUT)); + + cJSON_AddNumberToObject(root, "autofanspeed", nvs_config_get_bool(NVS_CONFIG_AUTO_FAN_SPEED)); + cJSON_AddFloatToObject(root, "fanspeed", GLOBAL_STATE->POWER_MANAGEMENT_MODULE.fan_perc); + cJSON_AddNumberToObject(root, "manualFanSpeed", nvs_config_get_u16(NVS_CONFIG_MANUAL_FAN_SPEED)); + cJSON_AddNumberToObject(root, "minFanSpeed", nvs_config_get_u16(NVS_CONFIG_MIN_FAN_SPEED)); + cJSON_AddNumberToObject(root, "temptarget", nvs_config_get_u16(NVS_CONFIG_TEMP_TARGET)); + cJSON_AddNumberToObject(root, "fanrpm", GLOBAL_STATE->POWER_MANAGEMENT_MODULE.fan_rpm); + cJSON_AddNumberToObject(root, "fan2rpm", GLOBAL_STATE->POWER_MANAGEMENT_MODULE.fan2_rpm); + + cJSON_AddNumberToObject(root, "statsFrequency", nvs_config_get_u16(NVS_CONFIG_STATISTICS_FREQUENCY)); + cJSON_AddNumberToObject(root, "blockFound", GLOBAL_STATE->SYSTEM_MODULE.block_found); + + // Webhook configuration + cJSON_AddNumberToObject(root, "webhookEnabled", nvs_config_get_bool(NVS_CONFIG_WEBHOOK_ENABLED)); + cJSON_AddStringToObject(root, "webhookUrl", nvs_config_get_string(NVS_CONFIG_WEBHOOK_URL)); + cJSON_AddNumberToObject(root, "webhookInterval", nvs_config_get_u16(NVS_CONFIG_WEBHOOK_INTERVAL)); + + if (GLOBAL_STATE->SYSTEM_MODULE.power_fault > 0) { + cJSON_AddStringToObject(root, "power_fault", VCORE_get_fault_string(GLOBAL_STATE)); + } + + if (GLOBAL_STATE->block_height > 0) { + cJSON_AddNumberToObject(root, "blockHeight", GLOBAL_STATE->block_height); + cJSON_AddStringToObject(root, "scriptsig", GLOBAL_STATE->scriptsig); + cJSON_AddNumberToObject(root, "networkDifficulty", GLOBAL_STATE->network_nonce_diff); + } + + cJSON *hashrate_monitor = cJSON_CreateObject(); + cJSON_AddItemToObject(root, "hashrateMonitor", hashrate_monitor); + + cJSON *asics_array = cJSON_CreateArray(); + cJSON_AddItemToObject(hashrate_monitor, "asics", asics_array); + + if (GLOBAL_STATE->HASHRATE_MONITOR_MODULE.is_initialized) { + for (int asic_nr = 0; asic_nr < GLOBAL_STATE->DEVICE_CONFIG.family.asic_count; asic_nr++) { + cJSON *asic = cJSON_CreateObject(); + cJSON_AddItemToArray(asics_array, asic); + cJSON_AddFloatToObject(asic, "total", GLOBAL_STATE->HASHRATE_MONITOR_MODULE.total_measurement[asic_nr].hashrate); + + int hash_domains = GLOBAL_STATE->DEVICE_CONFIG.family.asic.hash_domains; + cJSON* hash_domain_array = cJSON_CreateArray(); + for (int domain_nr = 0; domain_nr < hash_domains; domain_nr++) { + cJSON *hashrate = cJSON_CreateFloat(GLOBAL_STATE->HASHRATE_MONITOR_MODULE.domain_measurements[asic_nr][domain_nr].hashrate); + cJSON_AddItemToArray(hash_domain_array, hashrate); + } + cJSON_AddItemToObject(asic, "domains", hash_domain_array); + cJSON_AddNumberToObject(asic, "errorCount", GLOBAL_STATE->HASHRATE_MONITOR_MODULE.error_measurement[asic_nr].value); + } + } + + free(ssid); + free(hostname); + free(stratumURL); + free(fallbackStratumURL); + free(stratumCert); + free(fallbackStratumCert); + free(stratumUser); + free(fallbackStratumUser); + free(display); + + return root; +} diff --git a/main/system.h b/main/system.h index 57040cecf..575e3b765 100644 --- a/main/system.h +++ b/main/system.h @@ -13,4 +13,7 @@ void SYSTEM_notify_rejected_share(GlobalState * GLOBAL_STATE, char * error_msg); void SYSTEM_notify_found_nonce(GlobalState * GLOBAL_STATE, double diff, uint8_t job_id); void SYSTEM_notify_new_ntime(GlobalState * GLOBAL_STATE, uint32_t ntime); +#include "cJSON.h" +cJSON* SYSTEM_create_info_json(GlobalState * GLOBAL_STATE); + #endif /* SYSTEM_H_ */ diff --git a/main/tasks/webhook_task.c b/main/tasks/webhook_task.c new file mode 100644 index 000000000..0ab18e7d7 --- /dev/null +++ b/main/tasks/webhook_task.c @@ -0,0 +1,153 @@ +#include "webhook_task.h" +#include "global_state.h" +#include "nvs_config.h" +#include "connect.h" +#include "esp_log.h" +#include "esp_http_client.h" +#include "esp_wifi.h" +#include "cJSON.h" +#include "esp_timer.h" +#include "esp_system.h" +#include "system.h" +#include +#include + +static const char *TAG = "webhook_task"; + +// HTTP event handler +static esp_err_t http_event_handler(esp_http_client_event_t *evt) +{ + switch(evt->event_id) { + case HTTP_EVENT_ERROR: + ESP_LOGD(TAG, "HTTP_EVENT_ERROR"); + break; + case HTTP_EVENT_ON_CONNECTED: + ESP_LOGD(TAG, "HTTP_EVENT_ON_CONNECTED"); + break; + case HTTP_EVENT_HEADER_SENT: + ESP_LOGD(TAG, "HTTP_EVENT_HEADER_SENT"); + break; + case HTTP_EVENT_ON_HEADER: + ESP_LOGD(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value); + break; + case HTTP_EVENT_ON_DATA: + ESP_LOGD(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len); + break; + case HTTP_EVENT_ON_FINISH: + ESP_LOGD(TAG, "HTTP_EVENT_ON_FINISH"); + break; + case HTTP_EVENT_DISCONNECTED: + ESP_LOGI(TAG, "HTTP_EVENT_DISCONNECTED"); + break; + default: + break; + } + return ESP_OK; +} + +// Send webhook POST request +static esp_err_t send_webhook(GlobalState *GLOBAL_STATE, const char *url, const char *json_data) +{ + esp_http_client_config_t config = { + .url = url, + .event_handler = http_event_handler, + .timeout_ms = 10000, + }; + + esp_http_client_handle_t client = esp_http_client_init(&config); + if (client == NULL) { + ESP_LOGE(TAG, "Failed to initialize HTTP client"); + return ESP_FAIL; + } + + esp_http_client_set_method(client, HTTP_METHOD_POST); + esp_http_client_set_header(client, "Content-Type", "application/json"); + esp_http_client_set_post_field(client, json_data, strlen(json_data)); + + esp_err_t err = esp_http_client_perform(client); + if (err == ESP_OK) { + int status_code = esp_http_client_get_status_code(client); + int content_length = esp_http_client_get_content_length(client); + ESP_LOGI(TAG, "Webhook sent successfully, status=%d, content_length=%d", status_code, content_length); + } else { + ESP_LOGE(TAG, "Webhook send failed: %s", esp_err_to_name(err)); + } + + esp_http_client_cleanup(client); + return err; +} + +// Check if WiFi is connected +static bool is_wifi_connected() +{ + wifi_ap_record_t ap_info; + return (esp_wifi_sta_get_ap_info(&ap_info) == ESP_OK); +} + +void webhook_task(void *pvParameters) +{ + GlobalState *GLOBAL_STATE = (GlobalState *)pvParameters; + + ESP_LOGI(TAG, "Webhook task started"); + + while (1) { + // Check if webhook is enabled + bool webhook_enabled = nvs_config_get_bool(NVS_CONFIG_WEBHOOK_ENABLED); + + if (!webhook_enabled) { + vTaskDelay(10000 / portTICK_PERIOD_MS); // Check every 10 seconds if disabled + continue; + } + + // Get webhook URL and interval + char *webhook_url = nvs_config_get_string(NVS_CONFIG_WEBHOOK_URL); + uint16_t interval = nvs_config_get_u16(NVS_CONFIG_WEBHOOK_INTERVAL); + + // Validate URL + if (webhook_url == NULL || strlen(webhook_url) == 0) { + ESP_LOGW(TAG, "Webhook enabled but URL is empty"); + free(webhook_url); + vTaskDelay(60000 / portTICK_PERIOD_MS); // Wait 1 minute before retry + continue; + } + + // Check WiFi connection + if (!is_wifi_connected()) { + ESP_LOGD(TAG, "WiFi not connected, skipping webhook"); + free(webhook_url); + vTaskDelay(10000 / portTICK_PERIOD_MS); // Wait 10 seconds + continue; + } + + // Create system info JSON + cJSON *json = SYSTEM_create_info_json(GLOBAL_STATE); + if (json == NULL) { + ESP_LOGE(TAG, "Failed to create system info JSON"); + free(webhook_url); + vTaskDelay(interval * 1000 / portTICK_PERIOD_MS); + continue; + } + + // Convert JSON to string + char *json_string = cJSON_Print(json); + if (json_string == NULL) { + ESP_LOGE(TAG, "Failed to convert JSON to string"); + cJSON_Delete(json); + free(webhook_url); + vTaskDelay(interval * 1000 / portTICK_PERIOD_MS); + continue; + } + + // Send webhook + ESP_LOGI(TAG, "Sending webhook to %s", webhook_url); + send_webhook(GLOBAL_STATE, webhook_url, json_string); + + // Cleanup + free(json_string); + cJSON_Delete(json); + free(webhook_url); + + // Wait for next interval + vTaskDelay(interval * 1000 / portTICK_PERIOD_MS); + } +} diff --git a/main/tasks/webhook_task.h b/main/tasks/webhook_task.h new file mode 100644 index 000000000..65742af28 --- /dev/null +++ b/main/tasks/webhook_task.h @@ -0,0 +1,8 @@ +#ifndef WEBHOOK_TASK_H +#define WEBHOOK_TASK_H + +#include "global_state.h" + +void webhook_task(void *pvParameters); + +#endif // WEBHOOK_TASK_H