mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-04-10 21:03:01 +00:00
2207 lines
120 KiB
HTML
2207 lines
120 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>LED Matrix Config</title>
|
|
<style>
|
|
body {
|
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
margin: 20px;
|
|
background-color: #f4f4f4;
|
|
color: #333;
|
|
}
|
|
.container {
|
|
max-width: 1200px;
|
|
margin: auto;
|
|
background: #fff;
|
|
padding: 20px;
|
|
border-radius: 8px;
|
|
box-shadow: 0 0 15px rgba(0,0,0,0.1);
|
|
}
|
|
h1, h2, h3 {
|
|
text-align: center;
|
|
color: #333;
|
|
margin-bottom: 20px;
|
|
}
|
|
.tabs {
|
|
display: flex;
|
|
border-bottom: 1px solid #ccc;
|
|
margin-bottom: 20px;
|
|
flex-wrap: wrap;
|
|
}
|
|
.tab-link {
|
|
padding: 12px 20px;
|
|
cursor: pointer;
|
|
border: none;
|
|
background-color: transparent;
|
|
font-size: 14px;
|
|
border-bottom: 3px solid transparent;
|
|
transition: all 0.3s;
|
|
white-space: nowrap;
|
|
}
|
|
.tab-link.active {
|
|
border-bottom: 3px solid #4CAF50;
|
|
font-weight: bold;
|
|
background-color: #f0f8f0;
|
|
}
|
|
.tab-content {
|
|
display: none;
|
|
padding: 20px 0;
|
|
}
|
|
.form-section {
|
|
background: #f9f9f9;
|
|
padding: 20px;
|
|
margin-bottom: 20px;
|
|
border-radius: 8px;
|
|
border-left: 4px solid #4CAF50;
|
|
}
|
|
.form-group {
|
|
margin-bottom: 15px;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
}
|
|
.form-group label {
|
|
display: inline-block;
|
|
margin-bottom: 5px;
|
|
font-weight: bold;
|
|
min-width: 200px;
|
|
}
|
|
.form-group input[type="text"],
|
|
.form-group input[type="number"],
|
|
.form-group input[type="time"],
|
|
.form-group select {
|
|
padding: 8px 12px;
|
|
border-radius: 4px;
|
|
border: 1px solid #ccc;
|
|
font-size: 14px;
|
|
min-width: 150px;
|
|
}
|
|
.form-group input[type="checkbox"] {
|
|
width: 20px;
|
|
height: 20px;
|
|
margin: 0;
|
|
}
|
|
.form-group textarea {
|
|
width: 100%;
|
|
min-height: 80px;
|
|
padding: 8px 12px;
|
|
border-radius: 4px;
|
|
border: 1px solid #ccc;
|
|
font-family: monospace;
|
|
font-size: 12px;
|
|
}
|
|
.description {
|
|
font-size: 12px;
|
|
color: #666;
|
|
margin-top: 5px;
|
|
font-style: italic;
|
|
}
|
|
.toggle-container {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
}
|
|
.toggle-switch {
|
|
position: relative;
|
|
display: inline-block;
|
|
width: 60px;
|
|
height: 34px;
|
|
}
|
|
.toggle-switch input {
|
|
opacity: 0;
|
|
width: 0;
|
|
height: 0;
|
|
}
|
|
.slider {
|
|
position: absolute;
|
|
cursor: pointer;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background-color: #ccc;
|
|
transition: .4s;
|
|
border-radius: 34px;
|
|
}
|
|
.slider:before {
|
|
position: absolute;
|
|
content: "";
|
|
height: 26px;
|
|
width: 26px;
|
|
left: 4px;
|
|
bottom: 4px;
|
|
background-color: white;
|
|
transition: .4s;
|
|
border-radius: 50%;
|
|
}
|
|
input:checked + .slider {
|
|
background-color: #4CAF50;
|
|
}
|
|
input:checked + .slider:before {
|
|
transform: translateX(26px);
|
|
}
|
|
button {
|
|
background-color: #4CAF50;
|
|
color: white;
|
|
padding: 12px 25px;
|
|
border: none;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
margin-top: 10px;
|
|
font-size: 16px;
|
|
transition: background-color 0.3s;
|
|
}
|
|
button:hover {
|
|
background-color: #45a049;
|
|
}
|
|
.flash-messages {
|
|
list-style: none;
|
|
padding: 0;
|
|
margin-bottom: 15px;
|
|
}
|
|
.flash-messages li {
|
|
padding: 10px;
|
|
margin-bottom: 10px;
|
|
border-radius: 4px;
|
|
}
|
|
.flash-messages .success {
|
|
background-color: #d4edda;
|
|
color: #155724;
|
|
}
|
|
.flash-messages .error {
|
|
background-color: #f8d7da;
|
|
color: #721c24;
|
|
}
|
|
.filepath {
|
|
font-family: monospace;
|
|
background-color: #eee;
|
|
padding: 2px 5px;
|
|
border-radius: 3px;
|
|
font-size: 0.9em;
|
|
}
|
|
.action-buttons {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
gap: 10px;
|
|
margin-bottom: 20px;
|
|
}
|
|
.action-button {
|
|
padding: 15px;
|
|
font-size: 14px;
|
|
}
|
|
.config-section {
|
|
margin-bottom: 30px;
|
|
}
|
|
.config-section h3 {
|
|
color: #4CAF50;
|
|
border-bottom: 2px solid #4CAF50;
|
|
padding-bottom: 5px;
|
|
margin-bottom: 15px;
|
|
}
|
|
.array-input {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 10px;
|
|
align-items: center;
|
|
}
|
|
.array-input input {
|
|
flex: 1;
|
|
min-width: 120px;
|
|
}
|
|
.add-item-btn {
|
|
background-color: #2196F3;
|
|
padding: 8px 15px;
|
|
font-size: 12px;
|
|
}
|
|
.remove-item-btn {
|
|
background-color: #f44336;
|
|
padding: 8px 15px;
|
|
font-size: 12px;
|
|
}
|
|
.array-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
margin-bottom: 5px;
|
|
}
|
|
.json-editor {
|
|
display: none;
|
|
}
|
|
.toggle-json-btn {
|
|
background-color: #ff9800;
|
|
margin-bottom: 10px;
|
|
}
|
|
.toggle-json-btn:hover {
|
|
background-color: #e68900;
|
|
}
|
|
.form-row {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
|
gap: 20px;
|
|
}
|
|
.form-col {
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<h1>LED Matrix Configuration</h1>
|
|
|
|
{% with messages = get_flashed_messages(with_categories=true) %}
|
|
{% if messages %}
|
|
<ul class="flash-messages">
|
|
{% for category, message in messages %}
|
|
<li class="{{ category }}">{{ message }}</li>
|
|
{% endfor %}
|
|
</ul>
|
|
{% endif %}
|
|
{% endwith %}
|
|
|
|
<div class="tabs">
|
|
<button class="tab-link active" onclick="openTab(event, 'schedule')">Schedule</button>
|
|
<button class="tab-link" onclick="openTab(event, 'display')">Display Settings</button>
|
|
<button class="tab-link" onclick="openTab(event, 'sports')">Sports</button>
|
|
<button class="tab-link" onclick="openTab(event, 'weather')">Weather</button>
|
|
<button class="tab-link" onclick="openTab(event, 'stocks')">Stocks & Crypto</button>
|
|
<button class="tab-link" onclick="openTab(event, 'features')">Additional Features</button>
|
|
<button class="tab-link" onclick="openTab(event, 'music')">Music</button>
|
|
<button class="tab-link" onclick="openTab(event, 'calendar')">Calendar</button>
|
|
<button class="tab-link" onclick="openTab(event, 'secrets')">API Keys</button>
|
|
<button class="tab-link" onclick="openTab(event, 'actions')">Actions</button>
|
|
</div>
|
|
|
|
<!-- Schedule Tab -->
|
|
<div id="schedule" class="tab-content" style="display: block;">
|
|
<div class="form-section">
|
|
<h2>Display Schedule</h2>
|
|
<p>Set the time for the display to be active. A restart is needed for changes to take effect.</p>
|
|
<form action="{{ url_for('save_schedule_route') }}" method="POST">
|
|
<div class="form-group">
|
|
<label for="schedule_enabled">Enable Schedule:</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="schedule_enabled" name="schedule_enabled" {% if schedule_config.enabled %}checked{% endif %}>
|
|
<span class="slider"></span>
|
|
</label>
|
|
<span>Turn display on/off automatically</span>
|
|
</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="start_time">Display On Time:</label>
|
|
<input type="time" id="start_time" name="start_time" value="{{ schedule_config.start_time }}">
|
|
<div class="description">Time when the display should turn on</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="end_time">Display Off Time:</label>
|
|
<input type="time" id="end_time" name="end_time" value="{{ schedule_config.end_time }}">
|
|
<div class="description">Time when the display should turn off</div>
|
|
</div>
|
|
<button type="submit">Save Schedule</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Display Settings Tab -->
|
|
<div id="display" class="tab-content">
|
|
<div class="form-section">
|
|
<h2>Display Hardware Settings</h2>
|
|
<form action="{{ url_for('save_config_route') }}" method="POST" id="display-form">
|
|
<input type="hidden" name="config_type" value="main">
|
|
<input type="hidden" name="config_data" id="display-config-data">
|
|
|
|
<div class="form-row">
|
|
<div class="form-col">
|
|
<div class="form-group">
|
|
<label for="rows">Rows:</label>
|
|
<input type="number" id="rows" name="rows" value="{{ main_config.display.hardware.rows }}" min="1" max="64">
|
|
<div class="description">Number of LED rows</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="cols">Columns:</label>
|
|
<input type="number" id="cols" name="cols" value="{{ main_config.display.hardware.cols }}" min="1" max="128">
|
|
<div class="description">Number of LED columns</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="chain_length">Chain Length:</label>
|
|
<input type="number" id="chain_length" name="chain_length" value="{{ main_config.display.hardware.chain_length }}" min="1" max="8">
|
|
<div class="description">Number of LED panels chained together</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="parallel">Parallel:</label>
|
|
<input type="number" id="parallel" name="parallel" value="{{ main_config.display.hardware.parallel }}" min="1" max="4">
|
|
<div class="description">Number of parallel chains</div>
|
|
</div>
|
|
</div>
|
|
<div class="form-col">
|
|
<div class="form-group">
|
|
<label for="brightness">Brightness:</label>
|
|
<input type="number" id="brightness" name="brightness" value="{{ main_config.display.hardware.brightness }}" min="1" max="100">
|
|
<div class="description">LED brightness (1-100)</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="hardware_mapping">Hardware Mapping:</label>
|
|
<select id="hardware_mapping" name="hardware_mapping">
|
|
<option value="adafruit-hat-pwm" {% if main_config.display.hardware.hardware_mapping == "adafruit-hat-pwm" %}selected{% endif %}>Adafruit HAT PWM</option>
|
|
<option value="adafruit-hat" {% if main_config.display.hardware.hardware_mapping == "adafruit-hat" %}selected{% endif %}>Adafruit HAT</option>
|
|
<option value="regular" {% if main_config.display.hardware.hardware_mapping == "regular" %}selected{% endif %}>Regular</option>
|
|
<option value="regular-pi1" {% if main_config.display.hardware.hardware_mapping == "regular-pi1" %}selected{% endif %}>Regular Pi1</option>
|
|
</select>
|
|
<div class="description">Hardware mapping type</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="gpio_slowdown">GPIO Slowdown:</label>
|
|
<input type="number" id="gpio_slowdown" name="gpio_slowdown" value="{{ main_config.display.runtime.gpio_slowdown }}" min="0" max="5">
|
|
<div class="description">GPIO slowdown factor (0-5)</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<button type="submit">Save Display Settings</button>
|
|
</form>
|
|
</div>
|
|
|
|
<div class="form-section">
|
|
<h2>Display Durations</h2>
|
|
<p>Set how long each content type displays on the LED matrix.</p>
|
|
<form action="{{ url_for('save_config_route') }}" method="POST" id="durations-form">
|
|
<input type="hidden" name="config_type" value="main">
|
|
<input type="hidden" name="config_data" id="durations-config-data">
|
|
|
|
<div class="form-row">
|
|
<div class="form-col">
|
|
<div class="form-group">
|
|
<label for="clock_duration">Clock Duration (seconds):</label>
|
|
<input type="number" id="clock_duration" name="clock_duration" value="{{ main_config.display.display_durations.clock }}" min="5" max="120">
|
|
<div class="description">How long to show clock</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="weather_duration">Weather Duration (seconds):</label>
|
|
<input type="number" id="weather_duration" name="weather_duration" value="{{ main_config.display.display_durations.weather }}" min="5" max="120">
|
|
<div class="description">How long to show weather</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="stocks_duration">Stocks Duration (seconds):</label>
|
|
<input type="number" id="stocks_duration" name="stocks_duration" value="{{ main_config.display.display_durations.stocks }}" min="5" max="120">
|
|
<div class="description">How long to show stocks</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="music_duration">Music Duration (seconds):</label>
|
|
<input type="number" id="music_duration" name="music_duration" value="{{ main_config.display.display_durations.music }}" min="5" max="120">
|
|
<div class="description">How long to show music info</div>
|
|
</div>
|
|
</div>
|
|
<div class="form-col">
|
|
<div class="form-group">
|
|
<label for="calendar_duration">Calendar Duration (seconds):</label>
|
|
<input type="number" id="calendar_duration" name="calendar_duration" value="{{ main_config.display.display_durations.calendar }}" min="5" max="120">
|
|
<div class="description">How long to show calendar events</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="youtube_duration">YouTube Duration (seconds):</label>
|
|
<input type="number" id="youtube_duration" name="youtube_duration" value="{{ main_config.display.display_durations.youtube }}" min="5" max="120">
|
|
<div class="description">How long to show YouTube info</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="text_display_duration">Text Display Duration (seconds):</label>
|
|
<input type="number" id="text_display_duration" name="text_display_duration" value="{{ main_config.display.display_durations.text_display }}" min="5" max="120">
|
|
<div class="description">How long to show custom text</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="of_the_day_duration">Of The Day Duration (seconds):</label>
|
|
<input type="number" id="of_the_day_duration" name="of_the_day_duration" value="{{ main_config.display.display_durations.of_the_day }}" min="5" max="120">
|
|
<div class="description">How long to show word of the day</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<button type="submit">Save Display Durations</button>
|
|
</form>
|
|
</div>
|
|
|
|
<div class="form-section">
|
|
<h2>General Settings</h2>
|
|
<form action="{{ url_for('save_config_route') }}" method="POST" id="general-form">
|
|
<input type="hidden" name="config_type" value="main">
|
|
<input type="hidden" name="config_data" id="general-config-data">
|
|
|
|
<div class="form-group">
|
|
<label for="web_display_autostart">Auto-start Display:</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="web_display_autostart" name="web_display_autostart" {% if main_config.web_display_autostart %}checked{% endif %}>
|
|
<span class="slider"></span>
|
|
</label>
|
|
<span>Automatically start display on boot</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="timezone">Timezone:</label>
|
|
<select id="timezone" name="timezone">
|
|
<option value="America/Chicago" {% if main_config.timezone == "America/Chicago" %}selected{% endif %}>Central Time</option>
|
|
<option value="America/New_York" {% if main_config.timezone == "America/New_York" %}selected{% endif %}>Eastern Time</option>
|
|
<option value="America/Denver" {% if main_config.timezone == "America/Denver" %}selected{% endif %}>Mountain Time</option>
|
|
<option value="America/Los_Angeles" {% if main_config.timezone == "America/Los_Angeles" %}selected{% endif %}>Pacific Time</option>
|
|
<option value="UTC" {% if main_config.timezone == "UTC" %}selected{% endif %}>UTC</option>
|
|
</select>
|
|
<div class="description">System timezone</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="location_country">Country:</label>
|
|
<input type="text" id="location_country" name="location_country" value="{{ main_config.location.country }}" placeholder="US">
|
|
<div class="description">Country code for location</div>
|
|
</div>
|
|
|
|
<button type="submit">Save General Settings</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Sports Tab -->
|
|
<div id="sports" class="tab-content">
|
|
<div class="form-section">
|
|
<h2>Sports Configuration</h2>
|
|
<p>Configure which sports leagues to display and their settings.</p>
|
|
|
|
<div class="config-section">
|
|
<h3>MLB (Baseball)</h3>
|
|
<div class="form-group">
|
|
<label for="mlb_enabled">Enable MLB:</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="mlb_enabled" name="mlb_enabled" {% if main_config.mlb.enabled %}checked{% endif %}>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="mlb_favorites">Favorite Teams:</label>
|
|
<div class="array-input">
|
|
<input type="text" id="mlb_favorites" name="mlb_favorites" value="{{ main_config.mlb.favorite_teams|join(', ') }}" placeholder="TB, TEX">
|
|
<button type="button" class="add-item-btn" onclick="addArrayItem('mlb_favorites')">Add</button>
|
|
</div>
|
|
<div class="description">Comma-separated team abbreviations (e.g., TB, TEX)</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="mlb_live_priority">Live Priority:</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="mlb_live_priority" name="mlb_live_priority" {% if main_config.mlb.live_priority %}checked{% endif %}>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
<div class="description">Prioritize live/in-progress games over finished/upcoming games</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="mlb_live_game_duration">Live Game Duration (minutes):</label>
|
|
<input type="number" id="mlb_live_game_duration" name="mlb_live_game_duration" value="{{ main_config.mlb.live_game_duration }}" min="5" max="300">
|
|
<div class="description">How long to display each live game</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="mlb_show_odds">Show Odds:</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="mlb_show_odds" name="mlb_show_odds" {% if main_config.mlb.show_odds %}checked{% endif %}>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
<div class="description">Display betting odds for games</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="mlb_show_favorite_teams_only">Show Favorite Teams Only:</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="mlb_show_favorite_teams_only" name="mlb_show_favorite_teams_only" {% if main_config.mlb.show_favorite_teams_only %}checked{% endif %}>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
<div class="description">Only display games involving your favorite teams</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="config-section">
|
|
<h3>NFL (Football)</h3>
|
|
<div class="form-group">
|
|
<label for="nfl_enabled">Enable NFL:</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="nfl_enabled" name="nfl_enabled" {% if main_config.nfl_scoreboard.enabled %}checked{% endif %}>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="nfl_favorites">Favorite Teams:</label>
|
|
<div class="array-input">
|
|
<input type="text" id="nfl_favorites" name="nfl_favorites" value="{{ main_config.nfl_scoreboard.favorite_teams|join(', ') }}" placeholder="TB, DAL">
|
|
<button type="button" class="add-item-btn" onclick="addArrayItem('nfl_favorites')">Add</button>
|
|
</div>
|
|
<div class="description">Comma-separated team abbreviations</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="nfl_live_priority">Live Priority:</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="nfl_live_priority" name="nfl_live_priority" {% if main_config.nfl_scoreboard.live_priority %}checked{% endif %}>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
<div class="description">Prioritize live/in-progress games over finished/upcoming games</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="nfl_live_game_duration">Live Game Duration (minutes):</label>
|
|
<input type="number" id="nfl_live_game_duration" name="nfl_live_game_duration" value="{{ main_config.nfl_scoreboard.live_game_duration }}" min="5" max="300">
|
|
<div class="description">How long to display each live game</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="nfl_show_odds">Show Odds:</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="nfl_show_odds" name="nfl_show_odds" {% if main_config.nfl_scoreboard.show_odds %}checked{% endif %}>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
<div class="description">Display betting odds for games</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="nfl_show_favorite_teams_only">Show Favorite Teams Only:</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="nfl_show_favorite_teams_only" name="nfl_show_favorite_teams_only" {% if main_config.nfl_scoreboard.show_favorite_teams_only %}checked{% endif %}>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
<div class="description">Only display games involving your favorite teams</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="config-section">
|
|
<h3>NBA (Basketball)</h3>
|
|
<div class="form-group">
|
|
<label for="nba_enabled">Enable NBA:</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="nba_enabled" name="nba_enabled" {% if main_config.nba_scoreboard.enabled %}checked{% endif %}>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="nba_favorites">Favorite Teams:</label>
|
|
<div class="array-input">
|
|
<input type="text" id="nba_favorites" name="nba_favorites" value="{{ main_config.nba_scoreboard.favorite_teams|join(', ') }}" placeholder="DAL">
|
|
<button type="button" class="add-item-btn" onclick="addArrayItem('nba_favorites')">Add</button>
|
|
</div>
|
|
<div class="description">Comma-separated team abbreviations</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="nba_live_priority">Live Priority:</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="nba_live_priority" name="nba_live_priority" {% if main_config.nba_scoreboard.live_priority %}checked{% endif %}>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
<div class="description">Prioritize live/in-progress games over finished/upcoming games</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="nba_live_game_duration">Live Game Duration (minutes):</label>
|
|
<input type="number" id="nba_live_game_duration" name="nba_live_game_duration" value="{{ main_config.nba_scoreboard.live_game_duration }}" min="5" max="300">
|
|
<div class="description">How long to display each live game</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="nba_show_odds">Show Odds:</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="nba_show_odds" name="nba_show_odds" {% if main_config.nba_scoreboard.show_odds %}checked{% endif %}>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
<div class="description">Display betting odds for games</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="nba_recent_game_hours">Recent Game Hours:</label>
|
|
<input type="number" id="nba_recent_game_hours" name="nba_recent_game_hours" value="{{ main_config.nba_scoreboard.recent_game_hours }}" min="1" max="168">
|
|
<div class="description">How many hours back to show recent games (default: 72)</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="nba_show_favorite_teams_only">Show Favorite Teams Only:</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="nba_show_favorite_teams_only" name="nba_show_favorite_teams_only" {% if main_config.nba_scoreboard.show_favorite_teams_only %}checked{% endif %}>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
<div class="description">Only display games involving your favorite teams</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="config-section">
|
|
<h3>NHL (Hockey)</h3>
|
|
<div class="form-group">
|
|
<label for="nhl_enabled">Enable NHL:</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="nhl_enabled" name="nhl_enabled" {% if main_config.nhl_scoreboard.enabled %}checked{% endif %}>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="nhl_favorites">Favorite Teams:</label>
|
|
<div class="array-input">
|
|
<input type="text" id="nhl_favorites" name="nhl_favorites" value="{{ main_config.nhl_scoreboard.favorite_teams|join(', ') }}" placeholder="TB">
|
|
<button type="button" class="add-item-btn" onclick="addArrayItem('nhl_favorites')">Add</button>
|
|
</div>
|
|
<div class="description">Comma-separated team abbreviations</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="nhl_live_priority">Live Priority:</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="nhl_live_priority" name="nhl_live_priority" {% if main_config.nhl_scoreboard.live_priority %}checked{% endif %}>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
<div class="description">Prioritize live/in-progress games over finished/upcoming games</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="nhl_live_game_duration">Live Game Duration (minutes):</label>
|
|
<input type="number" id="nhl_live_game_duration" name="nhl_live_game_duration" value="{{ main_config.nhl_scoreboard.live_game_duration }}" min="5" max="300">
|
|
<div class="description">How long to display each live game</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="nhl_show_odds">Show Odds:</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="nhl_show_odds" name="nhl_show_odds" {% if main_config.nhl_scoreboard.show_odds %}checked{% endif %}>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
<div class="description">Display betting odds for games</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="nhl_show_favorite_teams_only">Show Favorite Teams Only:</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="nhl_show_favorite_teams_only" name="nhl_show_favorite_teams_only" {% if main_config.nhl_scoreboard.show_favorite_teams_only %}checked{% endif %}>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
<div class="description">Only display games involving your favorite teams</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="config-section">
|
|
<h3>NCAA Football</h3>
|
|
<div class="form-group">
|
|
<label for="ncaa_fb_enabled">Enable NCAA Football:</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="ncaa_fb_enabled" name="ncaa_fb_enabled" {% if main_config.ncaa_fb_scoreboard.enabled %}checked{% endif %}>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="ncaa_fb_favorites">Favorite Teams:</label>
|
|
<div class="array-input">
|
|
<input type="text" id="ncaa_fb_favorites" name="ncaa_fb_favorites" value="{{ main_config.ncaa_fb_scoreboard.favorite_teams|join(', ') }}" placeholder="UGA, AUB">
|
|
<button type="button" class="add-item-btn" onclick="addArrayItem('ncaa_fb_favorites')">Add</button>
|
|
</div>
|
|
<div class="description">Comma-separated team abbreviations</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="ncaa_fb_live_priority">Live Priority:</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="ncaa_fb_live_priority" name="ncaa_fb_live_priority" {% if main_config.ncaa_fb_scoreboard.live_priority %}checked{% endif %}>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
<div class="description">Prioritize live/in-progress games over finished/upcoming games</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="ncaa_fb_live_game_duration">Live Game Duration (minutes):</label>
|
|
<input type="number" id="ncaa_fb_live_game_duration" name="ncaa_fb_live_game_duration" value="{{ main_config.ncaa_fb_scoreboard.live_game_duration }}" min="5" max="300">
|
|
<div class="description">How long to display each live game</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="ncaa_fb_show_odds">Show Odds:</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="ncaa_fb_show_odds" name="ncaa_fb_show_odds" {% if main_config.ncaa_fb_scoreboard.show_odds %}checked{% endif %}>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
<div class="description">Display betting odds for games</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="ncaa_fb_show_favorite_teams_only">Show Favorite Teams Only:</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="ncaa_fb_show_favorite_teams_only" name="ncaa_fb_show_favorite_teams_only" {% if main_config.ncaa_fb_scoreboard.show_favorite_teams_only %}checked{% endif %}>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
<div class="description">Only display games involving your favorite teams</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="config-section">
|
|
<h3>NCAA Baseball</h3>
|
|
<div class="form-group">
|
|
<label for="ncaa_baseball_enabled">Enable NCAA Baseball:</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="ncaa_baseball_enabled" name="ncaa_baseball_enabled" {% if main_config.ncaa_baseball_scoreboard.enabled %}checked{% endif %}>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="ncaa_baseball_favorites">Favorite Teams:</label>
|
|
<div class="array-input">
|
|
<input type="text" id="ncaa_baseball_favorites" name="ncaa_baseball_favorites" value="{{ main_config.ncaa_baseball_scoreboard.favorite_teams|join(', ') }}" placeholder="UGA, AUB">
|
|
<button type="button" class="add-item-btn" onclick="addArrayItem('ncaa_baseball_favorites')">Add</button>
|
|
</div>
|
|
<div class="description">Comma-separated team abbreviations</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="ncaa_baseball_live_priority">Live Priority:</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="ncaa_baseball_live_priority" name="ncaa_baseball_live_priority" {% if main_config.ncaa_baseball_scoreboard.live_priority %}checked{% endif %}>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
<div class="description">Prioritize live/in-progress games over finished/upcoming games</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="ncaa_baseball_live_game_duration">Live Game Duration (minutes):</label>
|
|
<input type="number" id="ncaa_baseball_live_game_duration" name="ncaa_baseball_live_game_duration" value="{{ main_config.ncaa_baseball_scoreboard.live_game_duration }}" min="5" max="300">
|
|
<div class="description">How long to display each live game</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="ncaa_baseball_show_odds">Show Odds:</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="ncaa_baseball_show_odds" name="ncaa_baseball_show_odds" {% if main_config.ncaa_baseball_scoreboard.show_odds %}checked{% endif %}>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
<div class="description">Display betting odds for games</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="ncaa_baseball_recent_game_hours">Recent Game Hours:</label>
|
|
<input type="number" id="ncaa_baseball_recent_game_hours" name="ncaa_baseball_recent_game_hours" value="{{ main_config.ncaa_baseball_scoreboard.recent_game_hours }}" min="1" max="168">
|
|
<div class="description">How many hours back to show recent games (default: 72)</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="ncaa_baseball_show_favorite_teams_only">Show Favorite Teams Only:</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="ncaa_baseball_show_favorite_teams_only" name="ncaa_baseball_show_favorite_teams_only" {% if main_config.ncaa_baseball_scoreboard.show_favorite_teams_only %}checked{% endif %}>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
<div class="description">Only display games involving your favorite teams</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="config-section">
|
|
<h3>NCAA Basketball</h3>
|
|
<div class="form-group">
|
|
<label for="ncaam_basketball_enabled">Enable NCAA Basketball:</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="ncaam_basketball_enabled" name="ncaam_basketball_enabled" {% if main_config.ncaam_basketball_scoreboard.enabled %}checked{% endif %}>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="ncaam_basketball_favorites">Favorite Teams:</label>
|
|
<div class="array-input">
|
|
<input type="text" id="ncaam_basketball_favorites" name="ncaam_basketball_favorites" value="{{ main_config.ncaam_basketball_scoreboard.favorite_teams|join(', ') }}" placeholder="UGA, AUB">
|
|
<button type="button" class="add-item-btn" onclick="addArrayItem('ncaam_basketball_favorites')">Add</button>
|
|
</div>
|
|
<div class="description">Comma-separated team abbreviations</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="ncaam_basketball_live_priority">Live Priority:</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="ncaam_basketball_live_priority" name="ncaam_basketball_live_priority" {% if main_config.ncaam_basketball_scoreboard.live_priority %}checked{% endif %}>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
<div class="description">Prioritize live/in-progress games over finished/upcoming games</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="ncaam_basketball_live_game_duration">Live Game Duration (minutes):</label>
|
|
<input type="number" id="ncaam_basketball_live_game_duration" name="ncaam_basketball_live_game_duration" value="{{ main_config.ncaam_basketball_scoreboard.live_game_duration }}" min="5" max="300">
|
|
<div class="description">How long to display each live game</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="ncaam_basketball_show_odds">Show Odds:</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="ncaam_basketball_show_odds" name="ncaam_basketball_show_odds" {% if main_config.ncaam_basketball_scoreboard.show_odds %}checked{% endif %}>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
<div class="description">Display betting odds for games</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="ncaam_basketball_recent_game_hours">Recent Game Hours:</label>
|
|
<input type="number" id="ncaam_basketball_recent_game_hours" name="ncaam_basketball_recent_game_hours" value="{{ main_config.ncaam_basketball_scoreboard.recent_game_hours }}" min="1" max="168">
|
|
<div class="description">How many hours back to show recent games (default: 72)</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="ncaam_basketball_show_favorite_teams_only">Show Favorite Teams Only:</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="ncaam_basketball_show_favorite_teams_only" name="ncaam_basketball_show_favorite_teams_only" {% if main_config.ncaam_basketball_scoreboard.show_favorite_teams_only %}checked{% endif %}>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
<div class="description">Only display games involving your favorite teams</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="config-section">
|
|
<h3>MiLB (Minor League Baseball)</h3>
|
|
<div class="form-group">
|
|
<label for="milb_enabled">Enable MiLB:</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="milb_enabled" name="milb_enabled" {% if main_config.milb.enabled %}checked{% endif %}>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="milb_favorites">Favorite Teams:</label>
|
|
<div class="array-input">
|
|
<input type="text" id="milb_favorites" name="milb_favorites" value="{{ main_config.milb.favorite_teams|join(', ') }}" placeholder="TAM">
|
|
<button type="button" class="add-item-btn" onclick="addArrayItem('milb_favorites')">Add</button>
|
|
</div>
|
|
<div class="description">Comma-separated team abbreviations</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="milb_live_priority">Live Priority:</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="milb_live_priority" name="milb_live_priority" {% if main_config.milb.live_priority %}checked{% endif %}>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
<div class="description">Prioritize live/in-progress games over finished/upcoming games</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="milb_live_game_duration">Live Game Duration (minutes):</label>
|
|
<input type="number" id="milb_live_game_duration" name="milb_live_game_duration" value="{{ main_config.milb.live_game_duration }}" min="5" max="300">
|
|
<div class="description">How long to display each live game</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="config-section">
|
|
<h3>Soccer</h3>
|
|
<div class="form-group">
|
|
<label for="soccer_enabled">Enable Soccer:</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="soccer_enabled" name="soccer_enabled" {% if main_config.soccer_scoreboard.enabled %}checked{% endif %}>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="soccer_favorites">Favorite Teams:</label>
|
|
<div class="array-input">
|
|
<input type="text" id="soccer_favorites" name="soccer_favorites" value="{{ main_config.soccer_scoreboard.favorite_teams|join(', ') }}" placeholder="LIV">
|
|
<button type="button" class="add-item-btn" onclick="addArrayItem('soccer_favorites')">Add</button>
|
|
</div>
|
|
<div class="description">Comma-separated team abbreviations</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="soccer_leagues">Soccer Leagues:</label>
|
|
<div class="array-input">
|
|
<input type="text" id="soccer_leagues" name="soccer_leagues" value="{{ main_config.soccer_scoreboard.leagues|join(', ') }}" placeholder="eng.1, esp.1, ger.1">
|
|
<button type="button" class="add-item-btn" onclick="addArrayItem('soccer_leagues')">Add</button>
|
|
</div>
|
|
<div class="description">Comma-separated league codes (e.g., eng.1 for Premier League)</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="soccer_live_priority">Live Priority:</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="soccer_live_priority" name="soccer_live_priority" {% if main_config.soccer_scoreboard.live_priority %}checked{% endif %}>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
<div class="description">Prioritize live/in-progress games over finished/upcoming games</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="soccer_live_game_duration">Live Game Duration (minutes):</label>
|
|
<input type="number" id="soccer_live_game_duration" name="soccer_live_game_duration" value="{{ main_config.soccer_scoreboard.live_game_duration }}" min="5" max="300">
|
|
<div class="description">How long to display each live game</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="soccer_show_odds">Show Odds:</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="soccer_show_odds" name="soccer_show_odds" {% if main_config.soccer_scoreboard.show_odds %}checked{% endif %}>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
<div class="description">Display betting odds for games</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="soccer_recent_game_hours">Recent Game Hours:</label>
|
|
<input type="number" id="soccer_recent_game_hours" name="soccer_recent_game_hours" value="{{ main_config.soccer_scoreboard.recent_game_hours }}" min="1" max="336">
|
|
<div class="description">How many hours back to show recent games (default: 168)</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="soccer_show_favorite_teams_only">Show Favorite Teams Only:</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="soccer_show_favorite_teams_only" name="soccer_show_favorite_teams_only" {% if main_config.soccer_scoreboard.show_favorite_teams_only %}checked{% endif %}>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
<div class="description">Only display games involving your favorite teams</div>
|
|
</div>
|
|
</div>
|
|
|
|
<button type="button" onclick="saveSportsConfig()">Save Sports Settings</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Weather Tab -->
|
|
<div id="weather" class="tab-content">
|
|
<div class="form-section">
|
|
<h2>Weather Configuration</h2>
|
|
<form action="{{ url_for('save_config_route') }}" method="POST" id="weather-form">
|
|
<input type="hidden" name="config_type" value="main">
|
|
<input type="hidden" name="config_data" id="weather-config-data">
|
|
|
|
<div class="form-group">
|
|
<label for="weather_enabled">Enable Weather:</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="weather_enabled" name="weather_enabled" {% if main_config.weather.enabled %}checked{% endif %}>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="weather_city">City:</label>
|
|
<input type="text" id="weather_city" name="weather_city" value="{{ main_config.location.city }}">
|
|
<div class="description">City name for weather data</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="weather_state">State:</label>
|
|
<input type="text" id="weather_state" name="weather_state" value="{{ main_config.location.state }}">
|
|
<div class="description">State/province name</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="weather_units">Units:</label>
|
|
<select id="weather_units" name="weather_units">
|
|
<option value="imperial" {% if main_config.weather.units == "imperial" %}selected{% endif %}>Fahrenheit</option>
|
|
<option value="metric" {% if main_config.weather.units == "metric" %}selected{% endif %}>Celsius</option>
|
|
</select>
|
|
<div class="description">Temperature units</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="weather_update_interval">Update Interval (seconds):</label>
|
|
<input type="number" id="weather_update_interval" name="weather_update_interval" value="{{ main_config.weather.update_interval }}" min="300" max="3600">
|
|
<div class="description">How often to update weather data (300-3600 seconds)</div>
|
|
</div>
|
|
|
|
<button type="submit">Save Weather Settings</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Stocks Tab -->
|
|
<div id="stocks" class="tab-content">
|
|
<div class="form-section">
|
|
<h2>Stocks & Crypto Configuration</h2>
|
|
|
|
<div class="config-section">
|
|
<h3>Stocks</h3>
|
|
<form action="{{ url_for('save_config_route') }}" method="POST" id="stocks-form">
|
|
<input type="hidden" name="config_type" value="main">
|
|
<input type="hidden" name="config_data" id="stocks-config-data">
|
|
|
|
<div class="form-group">
|
|
<label for="stocks_enabled">Enable Stocks:</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="stocks_enabled" name="stocks_enabled" {% if main_config.stocks.enabled %}checked{% endif %}>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="stocks_symbols">Stock Symbols:</label>
|
|
<div class="array-input">
|
|
<input type="text" id="stocks_symbols" name="stocks_symbols" value="{{ main_config.stocks.symbols|join(', ') }}" placeholder="AAPL, GOOGL, MSFT">
|
|
<button type="button" class="add-item-btn" onclick="addArrayItem('stocks_symbols')">Add</button>
|
|
</div>
|
|
<div class="description">Comma-separated stock symbols</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="stocks_update_interval">Update Interval (seconds):</label>
|
|
<input type="number" id="stocks_update_interval" name="stocks_update_interval" value="{{ main_config.stocks.update_interval }}" min="60" max="3600">
|
|
<div class="description">How often to update stock data</div>
|
|
</div>
|
|
|
|
<button type="submit">Save Stocks Settings</button>
|
|
</form>
|
|
</div>
|
|
|
|
<div class="config-section">
|
|
<h3>Cryptocurrency</h3>
|
|
<form action="{{ url_for('save_config_route') }}" method="POST" id="crypto-form">
|
|
<input type="hidden" name="config_type" value="main">
|
|
<input type="hidden" name="config_data" id="crypto-config-data">
|
|
|
|
<div class="form-group">
|
|
<label for="crypto_enabled">Enable Crypto:</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="crypto_enabled" name="crypto_enabled" {% if main_config.crypto.enabled %}checked{% endif %}>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="crypto_symbols">Crypto Symbols:</label>
|
|
<div class="array-input">
|
|
<input type="text" id="crypto_symbols" name="crypto_symbols" value="{{ main_config.crypto.symbols|join(', ') }}" placeholder="BTC-USD, ETH-USD">
|
|
<button type="button" class="add-item-btn" onclick="addArrayItem('crypto_symbols')">Add</button>
|
|
</div>
|
|
<div class="description">Comma-separated crypto symbols (e.g., BTC-USD, ETH-USD)</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="crypto_update_interval">Update Interval (seconds):</label>
|
|
<input type="number" id="crypto_update_interval" name="crypto_update_interval" value="{{ main_config.crypto.update_interval }}" min="60" max="3600">
|
|
<div class="description">How often to update crypto data</div>
|
|
</div>
|
|
|
|
<button type="submit">Save Crypto Settings</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Additional Features Tab -->
|
|
<div id="features" class="tab-content">
|
|
<div class="form-section">
|
|
<h2>Additional Features</h2>
|
|
<p>Configure additional features like clock, stock news, odds ticker, YouTube, text display, and of the day.</p>
|
|
|
|
<div class="config-section">
|
|
<h3>Clock</h3>
|
|
<form action="{{ url_for('save_config_route') }}" method="POST" id="clock-form">
|
|
<input type="hidden" name="config_type" value="main">
|
|
<input type="hidden" name="config_data" id="clock-config-data">
|
|
|
|
<div class="form-group">
|
|
<label for="clock_enabled">Enable Clock:</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="clock_enabled" name="clock_enabled" {% if main_config.clock.enabled %}checked{% endif %}>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="clock_format">Clock Format:</label>
|
|
<select id="clock_format" name="clock_format">
|
|
<option value="%I:%M %p" {% if main_config.clock.format == "%I:%M %p" %}selected{% endif %}>12-hour (AM/PM)</option>
|
|
<option value="%H:%M" {% if main_config.clock.format == "%H:%M" %}selected{% endif %}>24-hour</option>
|
|
</select>
|
|
<div class="description">Time format for the clock display</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="clock_update_interval">Update Interval (seconds):</label>
|
|
<input type="number" id="clock_update_interval" name="clock_update_interval" value="{{ main_config.clock.update_interval }}" min="1" max="60">
|
|
<div class="description">How often to update the clock display</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="clock_date_format">Date Format:</label>
|
|
<select id="clock_date_format" name="clock_date_format">
|
|
<option value="MM/DD/YYYY" {% if main_config.clock.date_format == "MM/DD/YYYY" %}selected{% endif %}>MM/DD/YYYY</option>
|
|
<option value="DD/MM/YYYY" {% if main_config.clock.date_format == "DD/MM/YYYY" %}selected{% endif %}>DD/MM/YYYY</option>
|
|
<option value="YYYY/MM/DD" {% if main_config.clock.date_format == "YYYY/MM/DD" %}selected{% endif %}>YYYY/MM/DD</option>
|
|
</select>
|
|
<div class="description">Date format for the clock display</div>
|
|
</div>
|
|
|
|
<button type="submit">Save Clock Settings</button>
|
|
</form>
|
|
</div>
|
|
|
|
<div class="config-section">
|
|
<h3>Stock News</h3>
|
|
<form action="{{ url_for('save_config_route') }}" method="POST" id="stock-news-form">
|
|
<input type="hidden" name="config_type" value="main">
|
|
<input type="hidden" name="config_data" id="stock-news-config-data">
|
|
|
|
<div class="form-group">
|
|
<label for="stock_news_enabled">Enable Stock News:</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="stock_news_enabled" name="stock_news_enabled" {% if main_config.stock_news.enabled %}checked{% endif %}>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="stock_news_update_interval">Update Interval (seconds):</label>
|
|
<input type="number" id="stock_news_update_interval" name="stock_news_update_interval" value="{{ main_config.stock_news.update_interval }}" min="60" max="3600">
|
|
<div class="description">How often to update stock news</div>
|
|
</div>
|
|
|
|
<button type="submit">Save Stock News Settings</button>
|
|
</form>
|
|
</div>
|
|
|
|
<div class="config-section">
|
|
<h3>Odds Ticker</h3>
|
|
<form action="{{ url_for('save_config_route') }}" method="POST" id="odds-ticker-form">
|
|
<input type="hidden" name="config_type" value="main">
|
|
<input type="hidden" name="config_data" id="odds-ticker-config-data">
|
|
|
|
<div class="form-group">
|
|
<label for="odds_ticker_enabled">Enable Odds Ticker:</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="odds_ticker_enabled" name="odds_ticker_enabled" {% if main_config.odds_ticker.enabled %}checked{% endif %}>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="odds_ticker_update_interval">Update Interval (seconds):</label>
|
|
<input type="number" id="odds_ticker_update_interval" name="odds_ticker_update_interval" value="{{ main_config.odds_ticker.update_interval }}" min="60" max="3600">
|
|
<div class="description">How often to update odds</div>
|
|
</div>
|
|
|
|
<button type="submit">Save Odds Ticker Settings</button>
|
|
</form>
|
|
</div>
|
|
|
|
<div class="config-section">
|
|
<h3>YouTube</h3>
|
|
<form action="{{ url_for('save_config_route') }}" method="POST" id="youtube-form">
|
|
<input type="hidden" name="config_type" value="main">
|
|
<input type="hidden" name="config_data" id="youtube-config-data">
|
|
|
|
<div class="form-group">
|
|
<label for="youtube_enabled">Enable YouTube:</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="youtube_enabled" name="youtube_enabled" {% if main_config.youtube.enabled %}checked{% endif %}>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="youtube_channel_id">YouTube Channel ID:</label>
|
|
<input type="text" id="youtube_channel_id" name="youtube_channel_id" value="{{ main_config.youtube.channel_id if main_config.youtube.channel_id else '' }}" placeholder="Enter your YouTube channel ID">
|
|
<div class="description">Your YouTube channel ID (found in channel settings)</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="youtube_update_interval">Update Interval (seconds):</label>
|
|
<input type="number" id="youtube_update_interval" name="youtube_update_interval" value="{{ main_config.youtube.update_interval }}" min="60" max="3600">
|
|
<div class="description">How often to update YouTube info</div>
|
|
</div>
|
|
|
|
<button type="submit">Save YouTube Settings</button>
|
|
</form>
|
|
</div>
|
|
|
|
<div class="config-section">
|
|
<h3>Text Display</h3>
|
|
<form action="{{ url_for('save_config_route') }}" method="POST" id="text-display-form">
|
|
<input type="hidden" name="config_type" value="main">
|
|
<input type="hidden" name="config_data" id="text-display-config-data">
|
|
|
|
<div class="form-group">
|
|
<label for="text_display_enabled">Enable Custom Text Display:</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="text_display_enabled" name="text_display_enabled" {% if main_config.text_display.enabled %}checked{% endif %}>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="text_display_text">Custom Text:</label>
|
|
<textarea id="text_display_text" name="text_display_text" rows="5" cols="50">{{ main_config.text_display.text }}</textarea>
|
|
<div class="description">Custom text to display on the LED matrix</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="text_display_duration">Text Display Duration (seconds):</label>
|
|
<input type="number" id="text_display_duration" name="text_display_duration" value="{{ main_config.display.display_durations.text_display }}" min="5" max="120">
|
|
<div class="description">How long to show custom text</div>
|
|
</div>
|
|
|
|
<button type="submit">Save Text Display Settings</button>
|
|
</form>
|
|
</div>
|
|
|
|
<div class="config-section">
|
|
<h3>Of The Day</h3>
|
|
<form action="{{ url_for('save_config_route') }}" method="POST" id="of_the_day-form">
|
|
<input type="hidden" name="config_type" value="main">
|
|
<input type="hidden" name="config_data" id="of_the_day-config-data">
|
|
|
|
<div class="form-group">
|
|
<label for="of_the_day_enabled">Enable Of The Day:</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="of_the_day_enabled" name="of_the_day_enabled" {% if main_config.of_the_day.enabled %}checked{% endif %}>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="of_the_day_update_interval">Update Interval (seconds):</label>
|
|
<input type="number" id="of_the_day_update_interval" name="of_the_day_update_interval" value="{{ main_config.of_the_day.update_interval }}" min="300" max="3600">
|
|
<div class="description">How often to update word of the day</div>
|
|
</div>
|
|
|
|
<button type="submit">Save Of The Day Settings</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Music Tab -->
|
|
<div id="music" class="tab-content">
|
|
<div class="form-section">
|
|
<h2>Music Configuration</h2>
|
|
<form action="{{ url_for('save_config_route') }}" method="POST" id="music-form">
|
|
<input type="hidden" name="config_type" value="main">
|
|
<input type="hidden" name="config_data" id="music-config-data">
|
|
|
|
<div class="form-group">
|
|
<label for="music_enabled">Enable Music Display:</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="music_enabled" name="music_enabled" {% if main_config.music.enabled %}checked{% endif %}>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="music_preferred_source">Preferred Source:</label>
|
|
<select id="music_preferred_source" name="music_preferred_source">
|
|
<option value="ytm" {% if main_config.music.preferred_source == "ytm" %}selected{% endif %}>YouTube Music</option>
|
|
<option value="spotify" {% if main_config.music.preferred_source == "spotify" %}selected{% endif %}>Spotify</option>
|
|
</select>
|
|
<div class="description">Primary music source to display</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="ytm_companion_url">YouTube Music Companion URL:</label>
|
|
<input type="text" id="ytm_companion_url" name="ytm_companion_url" value="{{ main_config.music.YTM_COMPANION_URL }}">
|
|
<div class="description">URL for YouTube Music companion app</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="music_polling_interval">Polling Interval (seconds):</label>
|
|
<input type="number" id="music_polling_interval" name="music_polling_interval" value="{{ main_config.music.POLLING_INTERVAL_SECONDS }}" min="1" max="60">
|
|
<div class="description">How often to check for music updates</div>
|
|
</div>
|
|
|
|
<button type="submit">Save Music Settings</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Calendar Tab -->
|
|
<div id="calendar" class="tab-content">
|
|
<div class="form-section">
|
|
<h2>Calendar Configuration</h2>
|
|
<form action="{{ url_for('save_config_route') }}" method="POST" id="calendar-form">
|
|
<input type="hidden" name="config_type" value="main">
|
|
<input type="hidden" name="config_data" id="calendar-config-data">
|
|
|
|
<div class="form-group">
|
|
<label for="calendar_enabled">Enable Calendar:</label>
|
|
<div class="toggle-container">
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="calendar_enabled" name="calendar_enabled" {% if main_config.calendar.enabled %}checked{% endif %}>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="calendar_max_events">Max Events to Show:</label>
|
|
<input type="number" id="calendar_max_events" name="calendar_max_events" value="{{ main_config.calendar.max_events }}" min="1" max="10">
|
|
<div class="description">Maximum number of events to display</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="calendar_update_interval">Update Interval (seconds):</label>
|
|
<input type="number" id="calendar_update_interval" name="calendar_update_interval" value="{{ main_config.calendar.update_interval }}" min="300" max="3600">
|
|
<div class="description">How often to update calendar data</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="calendar_calendars">Calendars:</label>
|
|
<div class="array-input">
|
|
<input type="text" id="calendar_calendars" name="calendar_calendars" value="{{ main_config.calendar.calendars|join(', ') }}" placeholder="birthdays, work">
|
|
<button type="button" class="add-item-btn" onclick="addArrayItem('calendar_calendars')">Add</button>
|
|
</div>
|
|
<div class="description">Comma-separated calendar names</div>
|
|
</div>
|
|
|
|
<button type="submit">Save Calendar Settings</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Secrets Tab -->
|
|
<div id="secrets" class="tab-content">
|
|
<div class="form-section">
|
|
<h2>API Keys Configuration</h2>
|
|
<p>Enter your API keys for various services. These are stored securely and not shared.</p>
|
|
|
|
<button type="button" class="toggle-json-btn" onclick="toggleJsonEditor('secrets')">Toggle JSON Editor</button>
|
|
|
|
<div id="secrets-form" class="form-editor">
|
|
<form action="{{ url_for('save_config_route') }}" method="POST" id="secrets-form-content">
|
|
<input type="hidden" name="config_type" value="secrets">
|
|
<input type="hidden" name="config_data" id="secrets-config-data">
|
|
|
|
<div class="config-section">
|
|
<h3>Weather API</h3>
|
|
<div class="form-group">
|
|
<label for="weather_api_key">OpenWeatherMap API Key:</label>
|
|
<input type="password" id="weather_api_key" name="weather_api_key" value="{{ secrets_config.weather.api_key if secrets_config.weather.api_key != 'YOUR_OPENWEATHERMAP_API_KEY' else '' }}" placeholder="Enter your OpenWeatherMap API key">
|
|
<div class="description">Get your free API key from <a href="https://openweathermap.org/api" target="_blank">OpenWeatherMap</a></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="config-section">
|
|
<h3>YouTube API</h3>
|
|
<div class="form-group">
|
|
<label for="youtube_api_key">YouTube API Key:</label>
|
|
<input type="password" id="youtube_api_key" name="youtube_api_key" value="{{ secrets_config.youtube.api_key if secrets_config.youtube.api_key != 'YOUR_YOUTUBE_API_KEY' else '' }}" placeholder="Enter your YouTube API key">
|
|
<div class="description">Get your API key from <a href="https://console.developers.google.com/" target="_blank">Google Cloud Console</a></div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="youtube_channel_id">YouTube Channel ID:</label>
|
|
<input type="text" id="youtube_channel_id" name="youtube_channel_id" value="{{ secrets_config.youtube.channel_id if secrets_config.youtube.channel_id != 'YOUR_YOUTUBE_CHANNEL_ID' else '' }}" placeholder="Enter your YouTube channel ID">
|
|
<div class="description">Your YouTube channel ID (found in channel settings)</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="config-section">
|
|
<h3>Spotify API</h3>
|
|
<div class="form-group">
|
|
<label for="spotify_client_id">Spotify Client ID:</label>
|
|
<input type="password" id="spotify_client_id" name="spotify_client_id" value="{{ secrets_config.music.SPOTIFY_CLIENT_ID if secrets_config.music.SPOTIFY_CLIENT_ID != 'YOUR_SPOTIFY_CLIENT_ID_HERE' else '' }}" placeholder="Enter your Spotify Client ID">
|
|
<div class="description">Get from <a href="https://developer.spotify.com/dashboard" target="_blank">Spotify Developer Dashboard</a></div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="spotify_client_secret">Spotify Client Secret:</label>
|
|
<input type="password" id="spotify_client_secret" name="spotify_client_secret" value="{{ secrets_config.music.SPOTIFY_CLIENT_SECRET if secrets_config.music.SPOTIFY_CLIENT_SECRET != 'YOUR_SPOTIFY_CLIENT_SECRET_HERE' else '' }}" placeholder="Enter your Spotify Client Secret">
|
|
<div class="description">Your Spotify Client Secret</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="spotify_redirect_uri">Spotify Redirect URI:</label>
|
|
<input type="text" id="spotify_redirect_uri" name="spotify_redirect_uri" value="{{ secrets_config.music.SPOTIFY_REDIRECT_URI }}" placeholder="http://127.0.0.1:8888/callback">
|
|
<div class="description">Redirect URI for Spotify authentication</div>
|
|
</div>
|
|
</div>
|
|
|
|
<button type="submit">Save API Keys</button>
|
|
</form>
|
|
</div>
|
|
|
|
<div id="secrets-json" class="json-editor">
|
|
<form action="{{ url_for('save_config_route') }}" method="POST">
|
|
<input type="hidden" name="config_type" value="secrets">
|
|
<h2>Secrets Configuration (<span class="filepath">{{ secrets_config_path }}</span>)</h2>
|
|
<textarea name="config_data">{{ secrets_config_json }}</textarea>
|
|
<button type="submit">Save Secrets</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Actions Tab -->
|
|
<div id="actions" class="tab-content">
|
|
<div class="form-section">
|
|
<h2>System Actions</h2>
|
|
<p>Control the display service and system operations.</p>
|
|
|
|
<div class="config-section">
|
|
<h3>Display Control</h3>
|
|
<div class="action-buttons">
|
|
<button type="button" class="action-button" onclick="runAction('start_display')">Start Display</button>
|
|
<button type="button" class="action-button" onclick="runAction('stop_display')">Stop Display</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="config-section">
|
|
<h3>Auto-Start Settings</h3>
|
|
<div class="action-buttons">
|
|
<button type="button" class="action-button" onclick="runAction('enable_autostart')">Enable Auto-Start</button>
|
|
<button type="button" class="action-button" onclick="runAction('disable_autostart')">Disable Auto-Start</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="config-section">
|
|
<h3>System Operations</h3>
|
|
<div class="action-buttons">
|
|
<button type="button" class="action-button" onclick="runAction('reboot_system')">Reboot System</button>
|
|
<button type="button" class="action-button" onclick="runAction('git_pull')">Download Latest Update</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="config-section">
|
|
<h3>Action Output</h3>
|
|
<div id="action_output_container" style="margin-top: 20px;">
|
|
<pre id="action_output">No action run yet.</pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
function openTab(evt, tabName) {
|
|
var i, tabcontent, tablinks;
|
|
tabcontent = document.getElementsByClassName("tab-content");
|
|
for (i = 0; i < tabcontent.length; i++) {
|
|
tabcontent[i].style.display = "none";
|
|
}
|
|
tablinks = document.getElementsByClassName("tab-link");
|
|
for (i = 0; i < tablinks.length; i++) {
|
|
tablinks[i].className = tablinks[i].className.replace(" active", "");
|
|
}
|
|
document.getElementById(tabName).style.display = "block";
|
|
evt.currentTarget.className += " active";
|
|
}
|
|
|
|
function toggleJsonEditor(section) {
|
|
const formEditor = document.getElementById(section + '-form');
|
|
const jsonEditor = document.getElementById(section + '-json');
|
|
|
|
if (formEditor.style.display === 'none') {
|
|
formEditor.style.display = 'block';
|
|
jsonEditor.style.display = 'none';
|
|
} else {
|
|
formEditor.style.display = 'none';
|
|
jsonEditor.style.display = 'block';
|
|
}
|
|
}
|
|
|
|
function addArrayItem(inputId) {
|
|
const input = document.getElementById(inputId);
|
|
const currentValue = input.value.trim();
|
|
if (currentValue && !currentValue.endsWith(',')) {
|
|
input.value = currentValue + ', ';
|
|
}
|
|
input.focus();
|
|
}
|
|
|
|
function saveSportsConfig() {
|
|
// Collect all sports configuration and save
|
|
const config = {
|
|
mlb: {
|
|
enabled: document.getElementById('mlb_enabled').checked,
|
|
favorite_teams: document.getElementById('mlb_favorites').value.split(',').map(s => s.trim()).filter(s => s),
|
|
live_priority: document.getElementById('mlb_live_priority').checked,
|
|
live_game_duration: parseInt(document.getElementById('mlb_live_game_duration').value),
|
|
show_odds: document.getElementById('mlb_show_odds').checked,
|
|
show_favorite_teams_only: document.getElementById('mlb_show_favorite_teams_only').checked
|
|
},
|
|
nfl_scoreboard: {
|
|
enabled: document.getElementById('nfl_enabled').checked,
|
|
favorite_teams: document.getElementById('nfl_favorites').value.split(',').map(s => s.trim()).filter(s => s),
|
|
live_priority: document.getElementById('nfl_live_priority').checked,
|
|
live_game_duration: parseInt(document.getElementById('nfl_live_game_duration').value),
|
|
show_odds: document.getElementById('nfl_show_odds').checked,
|
|
show_favorite_teams_only: document.getElementById('nfl_show_favorite_teams_only').checked
|
|
},
|
|
nba_scoreboard: {
|
|
enabled: document.getElementById('nba_enabled').checked,
|
|
favorite_teams: document.getElementById('nba_favorites').value.split(',').map(s => s.trim()).filter(s => s),
|
|
live_priority: document.getElementById('nba_live_priority').checked,
|
|
live_game_duration: parseInt(document.getElementById('nba_live_game_duration').value),
|
|
show_odds: document.getElementById('nba_show_odds').checked,
|
|
recent_game_hours: parseInt(document.getElementById('nba_recent_game_hours').value),
|
|
show_favorite_teams_only: document.getElementById('nba_show_favorite_teams_only').checked
|
|
},
|
|
nhl_scoreboard: {
|
|
enabled: document.getElementById('nhl_enabled').checked,
|
|
favorite_teams: document.getElementById('nhl_favorites').value.split(',').map(s => s.trim()).filter(s => s),
|
|
live_priority: document.getElementById('nhl_live_priority').checked,
|
|
live_game_duration: parseInt(document.getElementById('nhl_live_game_duration').value),
|
|
show_odds: document.getElementById('nhl_show_odds').checked,
|
|
show_favorite_teams_only: document.getElementById('nhl_show_favorite_teams_only').checked
|
|
},
|
|
ncaa_fb_scoreboard: {
|
|
enabled: document.getElementById('ncaa_fb_enabled').checked,
|
|
favorite_teams: document.getElementById('ncaa_fb_favorites').value.split(',').map(s => s.trim()).filter(s => s),
|
|
live_priority: document.getElementById('ncaa_fb_live_priority').checked,
|
|
live_game_duration: parseInt(document.getElementById('ncaa_fb_live_game_duration').value),
|
|
show_odds: document.getElementById('ncaa_fb_show_odds').checked,
|
|
show_favorite_teams_only: document.getElementById('ncaa_fb_show_favorite_teams_only').checked
|
|
},
|
|
ncaa_baseball_scoreboard: {
|
|
enabled: document.getElementById('ncaa_baseball_enabled').checked,
|
|
favorite_teams: document.getElementById('ncaa_baseball_favorites').value.split(',').map(s => s.trim()).filter(s => s),
|
|
live_priority: document.getElementById('ncaa_baseball_live_priority').checked,
|
|
live_game_duration: parseInt(document.getElementById('ncaa_baseball_live_game_duration').value),
|
|
show_odds: document.getElementById('ncaa_baseball_show_odds').checked,
|
|
recent_game_hours: parseInt(document.getElementById('ncaa_baseball_recent_game_hours').value),
|
|
show_favorite_teams_only: document.getElementById('ncaa_baseball_show_favorite_teams_only').checked
|
|
},
|
|
ncaam_basketball_scoreboard: {
|
|
enabled: document.getElementById('ncaam_basketball_enabled').checked,
|
|
favorite_teams: document.getElementById('ncaam_basketball_favorites').value.split(',').map(s => s.trim()).filter(s => s),
|
|
live_priority: document.getElementById('ncaam_basketball_live_priority').checked,
|
|
live_game_duration: parseInt(document.getElementById('ncaam_basketball_live_game_duration').value),
|
|
show_odds: document.getElementById('ncaam_basketball_show_odds').checked,
|
|
recent_game_hours: parseInt(document.getElementById('ncaam_basketball_recent_game_hours').value),
|
|
show_favorite_teams_only: document.getElementById('ncaam_basketball_show_favorite_teams_only').checked
|
|
},
|
|
milb: {
|
|
enabled: document.getElementById('milb_enabled').checked,
|
|
favorite_teams: document.getElementById('milb_favorites').value.split(',').map(s => s.trim()).filter(s => s),
|
|
live_priority: document.getElementById('milb_live_priority').checked,
|
|
live_game_duration: parseInt(document.getElementById('milb_live_game_duration').value)
|
|
},
|
|
soccer_scoreboard: {
|
|
enabled: document.getElementById('soccer_enabled').checked,
|
|
favorite_teams: document.getElementById('soccer_favorites').value.split(',').map(s => s.trim()).filter(s => s),
|
|
leagues: document.getElementById('soccer_leagues').value.split(',').map(s => s.trim()).filter(s => s),
|
|
live_priority: document.getElementById('soccer_live_priority').checked,
|
|
live_game_duration: parseInt(document.getElementById('soccer_live_game_duration').value),
|
|
show_odds: document.getElementById('soccer_show_odds').checked,
|
|
recent_game_hours: parseInt(document.getElementById('soccer_recent_game_hours').value),
|
|
show_favorite_teams_only: document.getElementById('soccer_show_favorite_teams_only').checked
|
|
}
|
|
};
|
|
|
|
// Send to server
|
|
fetch("{{ url_for('save_config_route') }}", {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
body: new URLSearchParams({
|
|
'config_type': 'main',
|
|
'config_data': JSON.stringify(config)
|
|
})
|
|
})
|
|
.then(response => {
|
|
if (response.redirected) {
|
|
window.location.href = response.url;
|
|
} else {
|
|
return response.text();
|
|
}
|
|
})
|
|
.then(data => {
|
|
if (data) {
|
|
alert('Sports configuration saved successfully!');
|
|
}
|
|
})
|
|
.catch(error => {
|
|
alert('Error: ' + error);
|
|
});
|
|
}
|
|
|
|
// Add form submission handlers for better UX
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Handle display form submission
|
|
const displayForm = document.getElementById('display-form');
|
|
if (displayForm) {
|
|
displayForm.addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
const formData = new FormData(displayForm);
|
|
const config = {
|
|
display: {
|
|
hardware: {
|
|
rows: parseInt(formData.get('rows')),
|
|
cols: parseInt(formData.get('cols')),
|
|
chain_length: parseInt(formData.get('chain_length')),
|
|
parallel: parseInt(formData.get('parallel')),
|
|
brightness: parseInt(formData.get('brightness')),
|
|
hardware_mapping: formData.get('hardware_mapping')
|
|
},
|
|
runtime: {
|
|
gpio_slowdown: parseInt(formData.get('gpio_slowdown'))
|
|
}
|
|
}
|
|
};
|
|
|
|
fetch("{{ url_for('save_config_route') }}", {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
body: new URLSearchParams({
|
|
'config_type': 'main',
|
|
'config_data': JSON.stringify(config)
|
|
})
|
|
})
|
|
.then(response => {
|
|
if (response.redirected) {
|
|
window.location.href = response.url;
|
|
}
|
|
})
|
|
.catch(error => {
|
|
alert('Error saving display settings: ' + error);
|
|
});
|
|
});
|
|
}
|
|
|
|
// Handle weather form submission
|
|
const weatherForm = document.getElementById('weather-form');
|
|
if (weatherForm) {
|
|
weatherForm.addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
const formData = new FormData(weatherForm);
|
|
const config = {
|
|
weather: {
|
|
enabled: formData.get('weather_enabled') === 'on',
|
|
units: formData.get('weather_units'),
|
|
update_interval: parseInt(formData.get('weather_update_interval'))
|
|
},
|
|
location: {
|
|
city: formData.get('weather_city'),
|
|
state: formData.get('weather_state')
|
|
}
|
|
};
|
|
|
|
fetch("{{ url_for('save_config_route') }}", {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
body: new URLSearchParams({
|
|
'config_type': 'main',
|
|
'config_data': JSON.stringify(config)
|
|
})
|
|
})
|
|
.then(response => {
|
|
if (response.redirected) {
|
|
window.location.href = response.url;
|
|
}
|
|
})
|
|
.catch(error => {
|
|
alert('Error saving weather settings: ' + error);
|
|
});
|
|
});
|
|
}
|
|
|
|
// Handle stocks form submission
|
|
const stocksForm = document.getElementById('stocks-form');
|
|
if (stocksForm) {
|
|
stocksForm.addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
const formData = new FormData(stocksForm);
|
|
const symbols = formData.get('stocks_symbols').split(',').map(s => s.trim()).filter(s => s);
|
|
const config = {
|
|
stocks: {
|
|
enabled: formData.get('stocks_enabled') === 'on',
|
|
symbols: symbols,
|
|
update_interval: parseInt(formData.get('stocks_update_interval'))
|
|
}
|
|
};
|
|
|
|
fetch("{{ url_for('save_config_route') }}", {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
body: new URLSearchParams({
|
|
'config_type': 'main',
|
|
'config_data': JSON.stringify(config)
|
|
})
|
|
})
|
|
.then(response => {
|
|
if (response.redirected) {
|
|
window.location.href = response.url;
|
|
}
|
|
})
|
|
.catch(error => {
|
|
alert('Error saving stocks settings: ' + error);
|
|
});
|
|
});
|
|
}
|
|
|
|
// Handle crypto form submission
|
|
const cryptoForm = document.getElementById('crypto-form');
|
|
if (cryptoForm) {
|
|
cryptoForm.addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
const formData = new FormData(cryptoForm);
|
|
const symbols = formData.get('crypto_symbols').split(',').map(s => s.trim()).filter(s => s);
|
|
const config = {
|
|
crypto: {
|
|
enabled: formData.get('crypto_enabled') === 'on',
|
|
symbols: symbols,
|
|
update_interval: parseInt(formData.get('crypto_update_interval'))
|
|
}
|
|
};
|
|
|
|
fetch("{{ url_for('save_config_route') }}", {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
body: new URLSearchParams({
|
|
'config_type': 'main',
|
|
'config_data': JSON.stringify(config)
|
|
})
|
|
})
|
|
.then(response => {
|
|
if (response.redirected) {
|
|
window.location.href = response.url;
|
|
}
|
|
})
|
|
.catch(error => {
|
|
alert('Error saving crypto settings: ' + error);
|
|
});
|
|
});
|
|
}
|
|
|
|
// Handle music form submission
|
|
const musicForm = document.getElementById('music-form');
|
|
if (musicForm) {
|
|
musicForm.addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
const formData = new FormData(musicForm);
|
|
const config = {
|
|
music: {
|
|
enabled: formData.get('music_enabled') === 'on',
|
|
preferred_source: formData.get('music_preferred_source'),
|
|
YTM_COMPANION_URL: formData.get('ytm_companion_url'),
|
|
POLLING_INTERVAL_SECONDS: parseInt(formData.get('music_polling_interval'))
|
|
}
|
|
};
|
|
|
|
fetch("{{ url_for('save_config_route') }}", {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
body: new URLSearchParams({
|
|
'config_type': 'main',
|
|
'config_data': JSON.stringify(config)
|
|
})
|
|
})
|
|
.then(response => {
|
|
if (response.redirected) {
|
|
window.location.href = response.url;
|
|
}
|
|
})
|
|
.catch(error => {
|
|
alert('Error saving music settings: ' + error);
|
|
});
|
|
});
|
|
}
|
|
|
|
// Handle calendar form submission
|
|
const calendarForm = document.getElementById('calendar-form');
|
|
if (calendarForm) {
|
|
calendarForm.addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
const formData = new FormData(calendarForm);
|
|
const calendars = formData.get('calendar_calendars').split(',').map(c => c.trim()).filter(c => c);
|
|
const config = {
|
|
calendar: {
|
|
enabled: formData.get('calendar_enabled') === 'on',
|
|
max_events: parseInt(formData.get('calendar_max_events')),
|
|
update_interval: parseInt(formData.get('calendar_update_interval')),
|
|
calendars: calendars
|
|
}
|
|
};
|
|
|
|
fetch("{{ url_for('save_config_route') }}", {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
body: new URLSearchParams({
|
|
'config_type': 'main',
|
|
'config_data': JSON.stringify(config)
|
|
})
|
|
})
|
|
.then(response => {
|
|
if (response.redirected) {
|
|
window.location.href = response.url;
|
|
}
|
|
})
|
|
.catch(error => {
|
|
alert('Error saving calendar settings: ' + error);
|
|
});
|
|
});
|
|
}
|
|
|
|
// Handle secrets form submission
|
|
const secretsForm = document.getElementById('secrets-form-content');
|
|
if (secretsForm) {
|
|
secretsForm.addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
const formData = new FormData(secretsForm);
|
|
const config = {
|
|
weather: {
|
|
api_key: formData.get('weather_api_key')
|
|
},
|
|
youtube: {
|
|
api_key: formData.get('youtube_api_key'),
|
|
channel_id: formData.get('youtube_channel_id')
|
|
},
|
|
music: {
|
|
SPOTIFY_CLIENT_ID: formData.get('spotify_client_id'),
|
|
SPOTIFY_CLIENT_SECRET: formData.get('spotify_client_secret'),
|
|
SPOTIFY_REDIRECT_URI: formData.get('spotify_redirect_uri')
|
|
}
|
|
};
|
|
|
|
fetch("{{ url_for('save_config_route') }}", {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
body: new URLSearchParams({
|
|
'config_type': 'secrets',
|
|
'config_data': JSON.stringify(config)
|
|
})
|
|
})
|
|
.then(response => {
|
|
if (response.redirected) {
|
|
window.location.href = response.url;
|
|
}
|
|
})
|
|
.catch(error => {
|
|
alert('Error saving API keys: ' + error);
|
|
});
|
|
});
|
|
}
|
|
|
|
// Handle durations form submission
|
|
const durationsForm = document.getElementById('durations-form');
|
|
if (durationsForm) {
|
|
durationsForm.addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
const formData = new FormData(durationsForm);
|
|
const config = {
|
|
display: {
|
|
display_durations: {
|
|
clock: parseInt(formData.get('clock_duration')),
|
|
weather: parseInt(formData.get('weather_duration')),
|
|
stocks: parseInt(formData.get('stocks_duration')),
|
|
music: parseInt(formData.get('music_duration')),
|
|
calendar: parseInt(formData.get('calendar_duration')),
|
|
youtube: parseInt(formData.get('youtube_duration')),
|
|
text_display: parseInt(formData.get('text_display_duration')),
|
|
of_the_day: parseInt(formData.get('of_the_day_duration'))
|
|
}
|
|
}
|
|
};
|
|
|
|
fetch("{{ url_for('save_config_route') }}", {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
body: new URLSearchParams({
|
|
'config_type': 'main',
|
|
'config_data': JSON.stringify(config)
|
|
})
|
|
})
|
|
.then(response => {
|
|
if (response.redirected) {
|
|
window.location.href = response.url;
|
|
}
|
|
})
|
|
.catch(error => {
|
|
alert('Error saving display durations: ' + error);
|
|
});
|
|
});
|
|
}
|
|
|
|
// Handle general form submission
|
|
const generalForm = document.getElementById('general-form');
|
|
if (generalForm) {
|
|
generalForm.addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
const formData = new FormData(generalForm);
|
|
const config = {
|
|
web_display_autostart: formData.get('web_display_autostart') === 'on',
|
|
timezone: formData.get('timezone'),
|
|
location: {
|
|
country: formData.get('location_country')
|
|
}
|
|
};
|
|
|
|
fetch("{{ url_for('save_config_route') }}", {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
body: new URLSearchParams({
|
|
'config_type': 'main',
|
|
'config_data': JSON.stringify(config)
|
|
})
|
|
})
|
|
.then(response => {
|
|
if (response.redirected) {
|
|
window.location.href = response.url;
|
|
}
|
|
})
|
|
.catch(error => {
|
|
alert('Error saving general settings: ' + error);
|
|
});
|
|
});
|
|
}
|
|
|
|
// Handle clock form submission
|
|
const clockForm = document.getElementById('clock-form');
|
|
if (clockForm) {
|
|
clockForm.addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
const formData = new FormData(clockForm);
|
|
const config = {
|
|
clock: {
|
|
enabled: formData.get('clock_enabled') === 'on',
|
|
format: formData.get('clock_format'),
|
|
date_format: formData.get('clock_date_format'),
|
|
update_interval: parseInt(formData.get('clock_update_interval'))
|
|
}
|
|
};
|
|
|
|
fetch("{{ url_for('save_config_route') }}", {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
body: new URLSearchParams({
|
|
'config_type': 'main',
|
|
'config_data': JSON.stringify(config)
|
|
})
|
|
})
|
|
.then(response => {
|
|
if (response.redirected) {
|
|
window.location.href = response.url;
|
|
}
|
|
})
|
|
.catch(error => {
|
|
alert('Error saving clock settings: ' + error);
|
|
});
|
|
});
|
|
}
|
|
|
|
// Handle stock news form submission
|
|
const stockNewsForm = document.getElementById('stock-news-form');
|
|
if (stockNewsForm) {
|
|
stockNewsForm.addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
const formData = new FormData(stockNewsForm);
|
|
const config = {
|
|
stock_news: {
|
|
enabled: formData.get('stock_news_enabled') === 'on',
|
|
update_interval: parseInt(formData.get('stock_news_update_interval'))
|
|
}
|
|
};
|
|
|
|
fetch("{{ url_for('save_config_route') }}", {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
body: new URLSearchParams({
|
|
'config_type': 'main',
|
|
'config_data': JSON.stringify(config)
|
|
})
|
|
})
|
|
.then(response => {
|
|
if (response.redirected) {
|
|
window.location.href = response.url;
|
|
}
|
|
})
|
|
.catch(error => {
|
|
alert('Error saving stock news settings: ' + error);
|
|
});
|
|
});
|
|
}
|
|
|
|
// Handle odds ticker form submission
|
|
const oddsTickerForm = document.getElementById('odds-ticker-form');
|
|
if (oddsTickerForm) {
|
|
oddsTickerForm.addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
const formData = new FormData(oddsTickerForm);
|
|
const config = {
|
|
odds_ticker: {
|
|
enabled: formData.get('odds_ticker_enabled') === 'on',
|
|
update_interval: parseInt(formData.get('odds_ticker_update_interval'))
|
|
}
|
|
};
|
|
|
|
fetch("{{ url_for('save_config_route') }}", {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
body: new URLSearchParams({
|
|
'config_type': 'main',
|
|
'config_data': JSON.stringify(config)
|
|
})
|
|
})
|
|
.then(response => {
|
|
if (response.redirected) {
|
|
window.location.href = response.url;
|
|
}
|
|
})
|
|
.catch(error => {
|
|
alert('Error saving odds ticker settings: ' + error);
|
|
});
|
|
});
|
|
}
|
|
|
|
// Handle YouTube form submission
|
|
const youtubeForm = document.getElementById('youtube-form');
|
|
if (youtubeForm) {
|
|
youtubeForm.addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
const formData = new FormData(youtubeForm);
|
|
const config = {
|
|
youtube: {
|
|
enabled: formData.get('youtube_enabled') === 'on',
|
|
channel_id: formData.get('youtube_channel_id'),
|
|
update_interval: parseInt(formData.get('youtube_update_interval'))
|
|
}
|
|
};
|
|
|
|
fetch("{{ url_for('save_config_route') }}", {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
body: new URLSearchParams({
|
|
'config_type': 'main',
|
|
'config_data': JSON.stringify(config)
|
|
})
|
|
})
|
|
.then(response => {
|
|
if (response.redirected) {
|
|
window.location.href = response.url;
|
|
}
|
|
})
|
|
.catch(error => {
|
|
alert('Error saving YouTube settings: ' + error);
|
|
});
|
|
});
|
|
}
|
|
|
|
// Handle text display form submission
|
|
const textDisplayForm = document.getElementById('text-display-form');
|
|
if (textDisplayForm) {
|
|
textDisplayForm.addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
const formData = new FormData(textDisplayForm);
|
|
const config = {
|
|
text_display: {
|
|
enabled: formData.get('text_display_enabled') === 'on',
|
|
text: formData.get('text_display_text')
|
|
},
|
|
display: {
|
|
display_durations: {
|
|
text_display: parseInt(formData.get('text_display_duration'))
|
|
}
|
|
}
|
|
};
|
|
|
|
fetch("{{ url_for('save_config_route') }}", {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
body: new URLSearchParams({
|
|
'config_type': 'main',
|
|
'config_data': JSON.stringify(config)
|
|
})
|
|
})
|
|
.then(response => {
|
|
if (response.redirected) {
|
|
window.location.href = response.url;
|
|
}
|
|
})
|
|
.catch(error => {
|
|
alert('Error saving text display settings: ' + error);
|
|
});
|
|
});
|
|
}
|
|
|
|
// Handle of the day form submission
|
|
const ofTheDayForm = document.getElementById('of_the_day-form');
|
|
if (ofTheDayForm) {
|
|
ofTheDayForm.addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
const formData = new FormData(ofTheDayForm);
|
|
const config = {
|
|
of_the_day: {
|
|
enabled: formData.get('of_the_day_enabled') === 'on',
|
|
update_interval: parseInt(formData.get('of_the_day_update_interval'))
|
|
}
|
|
};
|
|
|
|
fetch("{{ url_for('save_config_route') }}", {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
body: new URLSearchParams({
|
|
'config_type': 'main',
|
|
'config_data': JSON.stringify(config)
|
|
})
|
|
})
|
|
.then(response => {
|
|
if (response.redirected) {
|
|
window.location.href = response.url;
|
|
}
|
|
})
|
|
.catch(error => {
|
|
alert('Error saving of the day settings: ' + error);
|
|
});
|
|
});
|
|
}
|
|
});
|
|
|
|
function runAction(actionName) {
|
|
const outputElement = document.getElementById('action_output');
|
|
outputElement.textContent = `Running ${actionName.replace(/_/g, ' ')}...`;
|
|
|
|
fetch("{{ url_for('run_action_route') }}", {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ action: actionName })
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
let outputText = `Status: ${data.status}\nMessage: ${data.message}\n`;
|
|
if (data.stdout) outputText += `\n--- STDOUT ---\n${data.stdout}`;
|
|
if (data.stderr) outputText += `\n--- STDERR ---\n${data.stderr}`;
|
|
outputElement.textContent = outputText;
|
|
})
|
|
.catch(error => {
|
|
outputElement.textContent = `Error: ${error}`;
|
|
});
|
|
}
|
|
|
|
// Set default active tab
|
|
document.addEventListener("DOMContentLoaded", function() {
|
|
document.querySelector('.tab-link').click();
|
|
});
|
|
</script>
|
|
</body>
|
|
</html> |