mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-04-11 21:33:00 +00:00
improvements
This commit is contained in:
@@ -1125,9 +1125,9 @@
|
|||||||
<h3>Sports Configuration</h3>
|
<h3>Sports Configuration</h3>
|
||||||
<p>Configure which sports leagues to display and their settings.</p>
|
<p>Configure which sports leagues to display and their settings.</p>
|
||||||
<!-- Sports configuration will be populated by JavaScript -->
|
<!-- Sports configuration will be populated by JavaScript -->
|
||||||
<div id="sports-config">
|
<div id="sports-config">
|
||||||
Loading sports configuration...
|
Loading sports configuration...
|
||||||
</div>
|
</div>
|
||||||
<div style="margin-top:10px; display:flex; gap:10px;">
|
<div style="margin-top:10px; display:flex; gap:10px;">
|
||||||
<button type="button" class="btn btn-primary" onclick="refreshSportsConfig()">Refresh</button>
|
<button type="button" class="btn btn-primary" onclick="refreshSportsConfig()">Refresh</button>
|
||||||
<button type="button" class="btn btn-success" onclick="saveSportsConfig()">Save Sports Settings</button>
|
<button type="button" class="btn btn-success" onclick="saveSportsConfig()">Save Sports Settings</button>
|
||||||
@@ -1407,6 +1407,11 @@
|
|||||||
Loading features configuration...
|
Loading features configuration...
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<h4>API Calls (24h window)</h4>
|
||||||
|
<div id="api-metrics" class="stat-card" style="text-align:left;">
|
||||||
|
<div>Loading API metrics...</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Music Tab -->
|
<!-- Music Tab -->
|
||||||
@@ -1772,6 +1777,7 @@
|
|||||||
initializeEditor();
|
initializeEditor();
|
||||||
updateSystemStats();
|
updateSystemStats();
|
||||||
loadNewsManagerData();
|
loadNewsManagerData();
|
||||||
|
updateApiMetrics();
|
||||||
|
|
||||||
// UI controls for grid & scale
|
// UI controls for grid & scale
|
||||||
const scaleRange = document.getElementById('scaleRange');
|
const scaleRange = document.getElementById('scaleRange');
|
||||||
@@ -1836,6 +1842,7 @@
|
|||||||
|
|
||||||
// Update stats every 30 seconds
|
// Update stats every 30 seconds
|
||||||
setInterval(updateSystemStats, 30000);
|
setInterval(updateSystemStats, 30000);
|
||||||
|
setInterval(updateApiMetrics, 60000);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Socket.IO connection
|
// Socket.IO connection
|
||||||
@@ -1882,6 +1889,27 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function updateApiMetrics(){
|
||||||
|
try {
|
||||||
|
const res = await fetch('/api/metrics');
|
||||||
|
const data = await res.json();
|
||||||
|
if (data.status !== 'success') return;
|
||||||
|
const el = document.getElementById('api-metrics');
|
||||||
|
const w = Math.round((data.window_seconds || 86400) / 3600);
|
||||||
|
const f = data.forecast || {};
|
||||||
|
const u = data.used || {};
|
||||||
|
el.innerHTML = `
|
||||||
|
<div><strong>Window:</strong> ${w} hours</div>
|
||||||
|
<div><strong>Weather:</strong> ${u.weather || 0} used / ${f.weather || 0} forecast</div>
|
||||||
|
<div><strong>Stocks:</strong> ${u.stocks || 0} used / ${f.stocks || 0} forecast</div>
|
||||||
|
<div><strong>Sports:</strong> ${u.sports || 0} used / ${f.sports || 0} forecast</div>
|
||||||
|
<div><strong>News:</strong> ${u.news || 0} used / ${f.news || 0} forecast</div>
|
||||||
|
`;
|
||||||
|
} catch (e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Fallback polling when websocket is disconnected
|
// Fallback polling when websocket is disconnected
|
||||||
let __previewPollTimer = null;
|
let __previewPollTimer = null;
|
||||||
function startPreviewPolling(){
|
function startPreviewPolling(){
|
||||||
@@ -2986,6 +3014,11 @@
|
|||||||
const html = leagues.map(l => {
|
const html = leagues.map(l => {
|
||||||
const sec = cfg[l.key] || {};
|
const sec = cfg[l.key] || {};
|
||||||
const fav = (sec.favorite_teams || []).join(', ');
|
const fav = (sec.favorite_teams || []).join(', ');
|
||||||
|
const recentToShow = sec.recent_games_to_show ?? 1;
|
||||||
|
const upcomingToShow = sec.upcoming_games_to_show ?? 1;
|
||||||
|
const liveUpd = sec.live_update_interval ?? 30;
|
||||||
|
const recentUpd = sec.recent_update_interval ?? 3600;
|
||||||
|
const upcomingUpd = sec.upcoming_update_interval ?? 3600;
|
||||||
return `
|
return `
|
||||||
<div style="border:1px solid #ddd; border-radius:6px; padding:12px; margin:10px 0;">
|
<div style="border:1px solid #ddd; border-radius:6px; padding:12px; margin:10px 0;">
|
||||||
<label style="display:flex; align-items:center; gap:8px;">
|
<label style="display:flex; align-items:center; gap:8px;">
|
||||||
@@ -3011,6 +3044,30 @@
|
|||||||
<div class="description">Comma-separated abbreviations</div>
|
<div class="description">Comma-separated abbreviations</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-row" style="margin-top:10px;">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Live Update Interval (sec)</label>
|
||||||
|
<input type="number" min="10" class="form-control sp-live-update" data-league="${l.key}" value="${liveUpd}">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Recent Update Interval (sec)</label>
|
||||||
|
<input type="number" min="60" class="form-control sp-recent-update" data-league="${l.key}" value="${recentUpd}">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Upcoming Update Interval (sec)</label>
|
||||||
|
<input type="number" min="60" class="form-control sp-upcoming-update" data-league="${l.key}" value="${upcomingUpd}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Recent Games to Show</label>
|
||||||
|
<input type="number" min="0" class="form-control sp-recent-count" data-league="${l.key}" value="${recentToShow}">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Upcoming Games to Show</label>
|
||||||
|
<input type="number" min="0" class="form-control sp-upcoming-count" data-league="${l.key}" value="${upcomingToShow}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}).join('');
|
}).join('');
|
||||||
@@ -3032,12 +3089,22 @@
|
|||||||
const favoritesOnly = document.querySelector(`.sp-favorites-only[data-league="${key}"]`)?.checked || false;
|
const favoritesOnly = document.querySelector(`.sp-favorites-only[data-league="${key}"]`)?.checked || false;
|
||||||
const favs = document.querySelector(`.sp-favorites[data-league="${key}"]`)?.value || '';
|
const favs = document.querySelector(`.sp-favorites[data-league="${key}"]`)?.value || '';
|
||||||
const favorite_teams = favs.split(',').map(s => s.trim()).filter(Boolean);
|
const favorite_teams = favs.split(',').map(s => s.trim()).filter(Boolean);
|
||||||
|
const liveUpd = parseInt(document.querySelector(`.sp-live-update[data-league="${key}"]`)?.value || '30');
|
||||||
|
const recentUpd = parseInt(document.querySelector(`.sp-recent-update[data-league="${key}"]`)?.value || '3600');
|
||||||
|
const upcomingUpd = parseInt(document.querySelector(`.sp-upcoming-update[data-league="${key}"]`)?.value || '3600');
|
||||||
|
const recentCount = parseInt(document.querySelector(`.sp-recent-count[data-league="${key}"]`)?.value || '1');
|
||||||
|
const upcomingCount = parseInt(document.querySelector(`.sp-upcoming-count[data-league="${key}"]`)?.value || '1');
|
||||||
fragment[key] = {
|
fragment[key] = {
|
||||||
enabled,
|
enabled,
|
||||||
live_priority: livePriority,
|
live_priority: livePriority,
|
||||||
show_odds: showOdds,
|
show_odds: showOdds,
|
||||||
show_favorite_teams_only: favoritesOnly,
|
show_favorite_teams_only: favoritesOnly,
|
||||||
favorite_teams
|
favorite_teams,
|
||||||
|
live_update_interval: liveUpd,
|
||||||
|
recent_update_interval: recentUpd,
|
||||||
|
upcoming_update_interval: upcomingUpd,
|
||||||
|
recent_games_to_show: recentCount,
|
||||||
|
upcoming_games_to_show: upcomingCount
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
await saveConfigJson(fragment);
|
await saveConfigJson(fragment);
|
||||||
|
|||||||
@@ -469,6 +469,82 @@ def get_system_status_api():
|
|||||||
"""Get system status as JSON."""
|
"""Get system status as JSON."""
|
||||||
return jsonify(get_system_status())
|
return jsonify(get_system_status())
|
||||||
|
|
||||||
|
# --- API Call Metrics (simple in-memory counters) ---
|
||||||
|
api_counters = {
|
||||||
|
'weather': {'used': 0},
|
||||||
|
'stocks': {'used': 0},
|
||||||
|
'sports': {'used': 0},
|
||||||
|
'news': {'used': 0},
|
||||||
|
}
|
||||||
|
api_window_start = time.time()
|
||||||
|
api_window_seconds = 24 * 3600
|
||||||
|
|
||||||
|
def increment_api_counter(kind: str, count: int = 1):
|
||||||
|
global api_window_start
|
||||||
|
now = time.time()
|
||||||
|
if now - api_window_start > api_window_seconds:
|
||||||
|
# Reset window
|
||||||
|
api_window_start = now
|
||||||
|
for v in api_counters.values():
|
||||||
|
v['used'] = 0
|
||||||
|
if kind in api_counters:
|
||||||
|
api_counters[kind]['used'] = api_counters[kind].get('used', 0) + count
|
||||||
|
|
||||||
|
@app.route('/api/metrics')
|
||||||
|
def get_metrics():
|
||||||
|
"""Expose lightweight API usage counters and simple forecasts based on config."""
|
||||||
|
try:
|
||||||
|
config = config_manager.load_config()
|
||||||
|
forecast = {}
|
||||||
|
# Weather forecasted calls per 24h
|
||||||
|
try:
|
||||||
|
w_int = int(config.get('weather', {}).get('update_interval', 1800))
|
||||||
|
forecast['weather'] = max(1, int(api_window_seconds / max(1, w_int)))
|
||||||
|
except Exception:
|
||||||
|
forecast['weather'] = 0
|
||||||
|
# Stocks
|
||||||
|
try:
|
||||||
|
s_int = int(config.get('stocks', {}).get('update_interval', 600))
|
||||||
|
forecast['stocks'] = max(1, int(api_window_seconds / max(1, s_int)))
|
||||||
|
except Exception:
|
||||||
|
forecast['stocks'] = 0
|
||||||
|
# Sports (aggregate of enabled leagues using their recent update intervals)
|
||||||
|
sports_leagues = [
|
||||||
|
('nhl_scoreboard','recent_update_interval'),
|
||||||
|
('nba_scoreboard','recent_update_interval'),
|
||||||
|
('mlb','recent_update_interval'),
|
||||||
|
('milb','recent_update_interval'),
|
||||||
|
('soccer_scoreboard','recent_update_interval'),
|
||||||
|
('nfl_scoreboard','recent_update_interval'),
|
||||||
|
('ncaa_fb_scoreboard','recent_update_interval'),
|
||||||
|
('ncaa_baseball_scoreboard','recent_update_interval'),
|
||||||
|
('ncaam_basketball_scoreboard','recent_update_interval'),
|
||||||
|
]
|
||||||
|
sports_calls = 0
|
||||||
|
for key, interval_key in sports_leagues:
|
||||||
|
sec = config.get(key, {})
|
||||||
|
if sec.get('enabled', False):
|
||||||
|
ival = int(sec.get(interval_key, 3600))
|
||||||
|
sports_calls += max(1, int(api_window_seconds / max(1, ival)))
|
||||||
|
forecast['sports'] = sports_calls
|
||||||
|
|
||||||
|
# News manager
|
||||||
|
try:
|
||||||
|
n_int = int(config.get('news_manager', {}).get('update_interval', 300))
|
||||||
|
forecast['news'] = max(1, int(api_window_seconds / max(1, n_int)))
|
||||||
|
except Exception:
|
||||||
|
forecast['news'] = 0
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
'status': 'success',
|
||||||
|
'window_seconds': api_window_seconds,
|
||||||
|
'since': api_window_start,
|
||||||
|
'forecast': forecast,
|
||||||
|
'used': {k: v.get('used', 0) for k, v in api_counters.items()}
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({'status': 'error', 'message': str(e)}), 500
|
||||||
|
|
||||||
# Add all the routes from the original web interface for compatibility
|
# Add all the routes from the original web interface for compatibility
|
||||||
@app.route('/save_schedule', methods=['POST'])
|
@app.route('/save_schedule', methods=['POST'])
|
||||||
def save_schedule_route():
|
def save_schedule_route():
|
||||||
|
|||||||
Reference in New Issue
Block a user