Enhance block finding metrics and UI display

Updated CSS for improved styling, added functions to calculate block finding probability and time, and modified UI to display these metrics based on the 24-hour hashrate. New HTML elements added to the dashboard for better user visibility of block odds.
This commit is contained in:
DJObleezy 2025-04-21 10:04:54 -07:00
parent 3bb74c37e7
commit 7267244e94
3 changed files with 199 additions and 2 deletions

View File

@ -300,7 +300,7 @@ h1 {
.yellow { .yellow {
color: #ffd700 !important; color: #ffd700 !important;
text-shadow: 0 0 2px #ffd700, 0 0 2px #ffd700; text-shadow: 0 0 2px #ffd700, 0 0 2px #ffd700 !important;
} }
.white { .white {

View File

@ -1,4 +1,4 @@
"use strict"; "use strict";
/** /**
* ArrowIndicator - A clean implementation for managing metric value change indicators * ArrowIndicator - A clean implementation for managing metric value change indicators
@ -404,6 +404,113 @@ function formatHashrateForDisplay(value, unit) {
} }
} }
// Function to calculate block finding probability based on hashrate and network hashrate
function calculateBlockProbability(yourHashrate, yourHashrateUnit, networkHashrate) {
// First normalize both hashrates to the same unit (TH/s)
const normalizedYourHashrate = normalizeHashrate(yourHashrate, yourHashrateUnit);
// Network hashrate is in EH/s, convert to TH/s (1 EH/s = 1,000,000 TH/s)
const networkHashrateInTH = networkHashrate * 1000000;
// Calculate probability as your_hashrate / network_hashrate
const probability = normalizedYourHashrate / networkHashrateInTH;
// Format the probability for display
return formatProbability(probability);
}
// Format probability for display
function formatProbability(probability) {
// Format as 1 in X chance (more intuitive for small probabilities)
if (probability > 0) {
const oneInX = Math.round(1 / probability);
return `1 : ${numberWithCommas(oneInX)}`;
} else {
return "N/A";
}
}
// Calculate theoretical time to find a block based on hashrate
function calculateBlockTime(yourHashrate, yourHashrateUnit, networkHashrate) {
// First normalize both hashrates to the same unit (TH/s)
const normalizedYourHashrate = normalizeHashrate(yourHashrate, yourHashrateUnit);
// Make sure network hashrate is a valid number
if (typeof networkHashrate !== 'number' || isNaN(networkHashrate) || networkHashrate <= 0) {
console.error("Invalid network hashrate:", networkHashrate);
return "N/A";
}
// Network hashrate is in EH/s, convert to TH/s (1 EH/s = 1,000,000 TH/s)
const networkHashrateInTH = networkHashrate * 1000000;
// Calculate the probability of finding a block per hash attempt
const probability = normalizedYourHashrate / networkHashrateInTH;
// Bitcoin produces a block every 10 minutes (600 seconds) on average
const secondsToFindBlock = 600 / probability;
// Log the calculation for debugging
console.log(`Block time calculation using network hashrate: ${networkHashrate} EH/s`);
console.log(`Your hashrate: ${yourHashrate} ${yourHashrateUnit} (normalized: ${normalizedYourHashrate} TH/s)`);
console.log(`Probability: ${normalizedYourHashrate} / (${networkHashrate} * 1,000,000) = ${probability}`);
console.log(`Time to find block: 600 seconds / ${probability} = ${secondsToFindBlock} seconds`);
console.log(`Estimated time: ${secondsToFindBlock / 86400} days (${secondsToFindBlock / 86400 / 365.25} years)`);
return formatTimeRemaining(secondsToFindBlock);
}
// Format time in seconds to a readable format (similar to est_time_to_payout)
function formatTimeRemaining(seconds) {
if (!seconds || seconds <= 0 || !isFinite(seconds)) {
return "N/A";
}
// Extremely large values (over 100 years) are not useful
if (seconds > 3153600000) { // 100 years in seconds
return "Never (statistically)";
}
const minutes = seconds / 60;
const hours = minutes / 60;
const days = hours / 24;
const months = days / 30.44; // Average month length
const years = days / 365.25; // Account for leap years
if (years >= 1) {
// For very long timeframes, show years and months
const remainingMonths = Math.floor((years - Math.floor(years)) * 12);
if (remainingMonths > 0) {
return `${Math.floor(years)} year${Math.floor(years) !== 1 ? 's' : ''}, ${remainingMonths} month${remainingMonths !== 1 ? 's' : ''}`;
}
return `${Math.floor(years)} year${Math.floor(years) !== 1 ? 's' : ''}`;
} else if (months >= 1) {
// For months, show months and days
const remainingDays = Math.floor((months - Math.floor(months)) * 30.44);
if (remainingDays > 0) {
return `${Math.floor(months)} month${Math.floor(months) !== 1 ? 's' : ''}, ${remainingDays} day${remainingDays !== 1 ? 's' : ''}`;
}
return `${Math.floor(months)} month${Math.floor(months) !== 1 ? 's' : ''}`;
} else if (days >= 1) {
// For days, show days and hours
const remainingHours = Math.floor((days - Math.floor(days)) * 24);
if (remainingHours > 0) {
return `${Math.floor(days)} day${Math.floor(days) !== 1 ? 's' : ''}, ${remainingHours} hour${remainingHours !== 1 ? 's' : ''}`;
}
return `${Math.floor(days)} day${Math.floor(days) !== 1 ? 's' : ''}`;
} else if (hours >= 1) {
// For hours, show hours and minutes
const remainingMinutes = Math.floor((hours - Math.floor(hours)) * 60);
if (remainingMinutes > 0) {
return `${Math.floor(hours)} hour${Math.floor(hours) !== 1 ? 's' : ''}, ${remainingMinutes} minute${remainingMinutes !== 1 ? 's' : ''}`;
}
return `${Math.floor(hours)} hour${Math.floor(hours) !== 1 ? 's' : ''}`;
} else {
// For minutes, just show minutes
return `${Math.ceil(minutes)} minute${Math.ceil(minutes) !== 1 ? 's' : ''}`;
}
}
// SSE Connection with Error Handling and Reconnection Logic // SSE Connection with Error Handling and Reconnection Logic
function setupEventSource() { function setupEventSource() {
console.log("Setting up EventSource connection..."); console.log("Setting up EventSource connection...");
@ -1183,6 +1290,50 @@ function updateUI() {
} }
updateElementText("hashrate_24hr", formatted24hrHashrate); updateElementText("hashrate_24hr", formatted24hrHashrate);
// Calculate and display theoretical block finding time
if (data.hashrate_24hr != null && data.network_hashrate != null) {
const blockTime = calculateBlockTime(
data.hashrate_24hr,
data.hashrate_24hr_unit || 'th/s',
data.network_hashrate
);
// Update the time element if it exists, or create it
const timeElement = document.getElementById("block_time");
if (timeElement) {
timeElement.textContent = blockTime;
} else {
// Find the hashrate_24hr element's parent paragraph
const hashratePara = document.getElementById("hashrate_24hr").parentNode;
// Create the time element inline with spacing
const timeSpan = document.createElement("span");
timeSpan.id = "block_time";
timeSpan.className = "metric-value yellow"; // Using yellow for time values
timeSpan.style.marginLeft = "10px";
timeSpan.style.fontSize = "0.75em"; // Slightly larger font size
timeSpan.style.fontWeight = "Normal"; // Normal font
timeSpan.textContent = blockTime;
// Create a small label for it
const timeLabel = document.createElement("span");
timeLabel.className = "metric-label";
timeLabel.style.fontSize = "0.65em";
timeLabel.style.opacity = "0.8";
timeLabel.style.marginLeft = "5px";
timeLabel.style.color = "white"; // White color for label"
timeLabel.style.fontWeight = "Normal"; // Normal font
timeLabel.textContent = "[Time to ₿]";
// Add both elements after the arrow indicator
const arrowIndicator = document.getElementById("indicator_hashrate_24hr");
if (arrowIndicator && hashratePara) {
arrowIndicator.parentNode.insertBefore(timeSpan, arrowIndicator.nextSibling);
arrowIndicator.parentNode.insertBefore(timeLabel, timeSpan.nextSibling);
}
}
}
// 3hr Hashrate // 3hr Hashrate
let formatted3hrHashrate = "N/A"; let formatted3hrHashrate = "N/A";
if (data.hashrate_3hr != null) { if (data.hashrate_3hr != null) {
@ -1193,6 +1344,50 @@ function updateUI() {
} }
updateElementText("hashrate_3hr", formatted3hrHashrate); updateElementText("hashrate_3hr", formatted3hrHashrate);
// Calculate and display block finding probability for 24hr hashrate instead of 3hr hashrate
if (data.hashrate_24hr != null && data.network_hashrate != null) {
const blockProbability = calculateBlockProbability(
data.hashrate_24hr,
data.hashrate_24hr_unit || 'th/s',
data.network_hashrate
);
// Update the block odds element
const probabilityElement = document.getElementById("block_odds_3hr");
if (probabilityElement) {
probabilityElement.textContent = blockProbability;
} else {
// Find the hashrate_3hr element's parent paragraph
const hashratePara = document.getElementById("hashrate_3hr").parentNode;
// Create the probability element inline with spacing
const probSpan = document.createElement("span");
probSpan.id = "block_odds_3hr";
probSpan.className = "metric-value yellow";
probSpan.style.marginLeft = "10px";
probSpan.style.fontSize = "0.75em"; // Slightly larger font size
probSpan.style.fontWeight = "Normal"; // Normal font
probSpan.textContent = blockProbability;
// Create a small label for it
const probLabel = document.createElement("span");
probLabel.className = "metric-label";
probLabel.style.fontSize = "0.65em";
probLabel.style.opacity = "0.8";
probLabel.style.marginLeft = "5px";
probLabel.style.color = "white";
probLabel.style.fontWeight = "Normal"; // Normal font
probLabel.textContent = "[₿ Odds]";
// Add both elements after the arrow indicator
const arrowIndicator = document.getElementById("indicator_hashrate_3hr");
if (arrowIndicator && hashratePara) {
arrowIndicator.parentNode.insertBefore(probSpan, arrowIndicator.nextSibling);
arrowIndicator.parentNode.insertBefore(probLabel, probSpan.nextSibling);
}
}
}
// 10min Hashrate // 10min Hashrate
let formatted10minHashrate = "N/A"; let formatted10minHashrate = "N/A";
if (data.hashrate_10min != null) { if (data.hashrate_10min != null) {

View File

@ -159,6 +159,8 @@
{% endif %} {% endif %}
</span> </span>
<span id="indicator_hashrate_3hr"></span> <span id="indicator_hashrate_3hr"></span>
<span id="block_odds_3hr" class="metric-value yellow" style="margin-left: 10px; font-size: 0.75em; font-weight: normal;"></span>
<span class="metric-label" style="font-size: 0.65em; opacity: 0.8; margin-left: 5px; color: white;">[₿ Odds]</span>
</p> </p>
<p> <p>
<strong>10min Avg Hashrate:</strong> <strong>10min Avg Hashrate:</strong>