Add web interface control for greeting delay
- Move greeting_delay from system config to user runtime config - Add GET/POST API endpoints at /api/greeting_delay - Add delay slider to web interface (0-10 seconds range) - Implement debounced slider updates (300ms delay) - Update visual gradient on slider movement - Display current delay value in seconds - Allow greeting delay adjustment without editing config files 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -105,16 +105,19 @@ class RotaryPhone:
|
|||||||
"active_greeting": SYS_CONFIG['system']['active_greeting'],
|
"active_greeting": SYS_CONFIG['system']['active_greeting'],
|
||||||
"extra_button_sound": SYS_CONFIG['system'].get('extra_button_sound', 'button_sound.wav'),
|
"extra_button_sound": SYS_CONFIG['system'].get('extra_button_sound', 'button_sound.wav'),
|
||||||
"greetings": [],
|
"greetings": [],
|
||||||
"volume": SYS_CONFIG['system']['volume']
|
"volume": SYS_CONFIG['system']['volume'],
|
||||||
|
"greeting_delay": SYS_CONFIG['system'].get('greeting_delay_seconds', 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
if os.path.exists(USER_CONFIG_FILE):
|
if os.path.exists(USER_CONFIG_FILE):
|
||||||
try:
|
try:
|
||||||
with open(USER_CONFIG_FILE, 'r') as f:
|
with open(USER_CONFIG_FILE, 'r') as f:
|
||||||
config = json.load(f)
|
config = json.load(f)
|
||||||
# Ensure volume key exists
|
# Ensure required keys exist
|
||||||
if "volume" not in config:
|
if "volume" not in config:
|
||||||
config["volume"] = SYS_CONFIG['system']['volume']
|
config["volume"] = SYS_CONFIG['system']['volume']
|
||||||
|
if "greeting_delay" not in config:
|
||||||
|
config["greeting_delay"] = SYS_CONFIG['system'].get('greeting_delay_seconds', 0)
|
||||||
return config
|
return config
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
@@ -156,6 +159,17 @@ class RotaryPhone:
|
|||||||
def get_volume(self):
|
def get_volume(self):
|
||||||
"""Get current volume setting"""
|
"""Get current volume setting"""
|
||||||
return self.config.get("volume", 70)
|
return self.config.get("volume", 70)
|
||||||
|
|
||||||
|
def set_greeting_delay(self, delay):
|
||||||
|
"""Set greeting delay in seconds (0-10)"""
|
||||||
|
delay = max(0, min(10, int(delay))) # Clamp between 0-10
|
||||||
|
self.config["greeting_delay"] = delay
|
||||||
|
self.save_config()
|
||||||
|
return delay
|
||||||
|
|
||||||
|
def get_greeting_delay(self):
|
||||||
|
"""Get current greeting delay"""
|
||||||
|
return self.config.get("greeting_delay", 0)
|
||||||
|
|
||||||
def generate_default_dialtone(self):
|
def generate_default_dialtone(self):
|
||||||
"""Generate a classic dial tone (350Hz + 440Hz) and save as default"""
|
"""Generate a classic dial tone (350Hz + 440Hz) and save as default"""
|
||||||
@@ -349,7 +363,7 @@ class RotaryPhone:
|
|||||||
print("\n=== Handset picked up ===")
|
print("\n=== Handset picked up ===")
|
||||||
|
|
||||||
# Apply greeting delay if configured
|
# Apply greeting delay if configured
|
||||||
greeting_delay = SYS_CONFIG['system'].get('greeting_delay_seconds', 0)
|
greeting_delay = self.get_greeting_delay()
|
||||||
if greeting_delay > 0:
|
if greeting_delay > 0:
|
||||||
print(f"Waiting {greeting_delay} seconds before greeting...")
|
print(f"Waiting {greeting_delay} seconds before greeting...")
|
||||||
time.sleep(greeting_delay)
|
time.sleep(greeting_delay)
|
||||||
@@ -390,6 +404,7 @@ def index():
|
|||||||
active_greeting = phone.config.get("active_greeting", "dialtone.wav")
|
active_greeting = phone.config.get("active_greeting", "dialtone.wav")
|
||||||
extra_button_sound = phone.config.get("extra_button_sound", "button_sound.wav")
|
extra_button_sound = phone.config.get("extra_button_sound", "button_sound.wav")
|
||||||
volume = phone.get_volume()
|
volume = phone.get_volume()
|
||||||
|
greeting_delay = phone.get_greeting_delay()
|
||||||
|
|
||||||
return render_template('index.html',
|
return render_template('index.html',
|
||||||
recordings=recordings,
|
recordings=recordings,
|
||||||
@@ -398,7 +413,8 @@ def index():
|
|||||||
extra_button_sound=extra_button_sound,
|
extra_button_sound=extra_button_sound,
|
||||||
extra_button_enabled=EXTRA_BUTTON_ENABLED,
|
extra_button_enabled=EXTRA_BUTTON_ENABLED,
|
||||||
status=status,
|
status=status,
|
||||||
volume=volume)
|
volume=volume,
|
||||||
|
greeting_delay=greeting_delay)
|
||||||
|
|
||||||
@app.route('/api/status')
|
@app.route('/api/status')
|
||||||
def api_status():
|
def api_status():
|
||||||
@@ -428,6 +444,19 @@ def api_set_volume():
|
|||||||
new_volume = phone.set_volume(volume)
|
new_volume = phone.set_volume(volume)
|
||||||
return jsonify({"success": True, "volume": new_volume})
|
return jsonify({"success": True, "volume": new_volume})
|
||||||
|
|
||||||
|
@app.route('/api/greeting_delay', methods=['GET'])
|
||||||
|
def api_get_greeting_delay():
|
||||||
|
"""Get current greeting delay setting"""
|
||||||
|
return jsonify({"delay": phone.get_greeting_delay()})
|
||||||
|
|
||||||
|
@app.route('/api/greeting_delay', methods=['POST'])
|
||||||
|
def api_set_greeting_delay():
|
||||||
|
"""Set greeting delay in seconds"""
|
||||||
|
data = request.get_json()
|
||||||
|
delay = data.get('delay', 0)
|
||||||
|
new_delay = phone.set_greeting_delay(delay)
|
||||||
|
return jsonify({"success": True, "delay": new_delay})
|
||||||
|
|
||||||
@app.route('/upload_greeting', methods=['POST'])
|
@app.route('/upload_greeting', methods=['POST'])
|
||||||
def upload_greeting():
|
def upload_greeting():
|
||||||
"""Upload a new greeting message"""
|
"""Upload a new greeting message"""
|
||||||
@@ -1065,10 +1094,13 @@ if __name__ == "__main__":
|
|||||||
<button class="btn btn-secondary" onclick="refreshStatus()">🔄 Refresh</button>
|
<button class="btn btn-secondary" onclick="refreshStatus()">🔄 Refresh</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Volume Control Card -->
|
<!-- Volume & Delay Control Card -->
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<h2>🔊 Volume Control</h2>
|
<h2>🔊 Volume & Delay Control</h2>
|
||||||
<div style="padding: 20px;">
|
|
||||||
|
<!-- Volume Control -->
|
||||||
|
<div style="padding: 20px; border-bottom: 1px solid #e5e7eb;">
|
||||||
|
<h3 style="margin: 0 0 15px 0; font-size: 1.1em; color: #333;">Volume</h3>
|
||||||
<div style="display: flex; align-items: center; gap: 20px;">
|
<div style="display: flex; align-items: center; gap: 20px;">
|
||||||
<span style="font-size: 1.5em;">🔇</span>
|
<span style="font-size: 1.5em;">🔇</span>
|
||||||
<input type="range" id="volume-slider" min="0" max="100" value="{{ volume }}"
|
<input type="range" id="volume-slider" min="0" max="100" value="{{ volume }}"
|
||||||
@@ -1076,8 +1108,23 @@ if __name__ == "__main__":
|
|||||||
<span style="font-size: 1.5em;">🔊</span>
|
<span style="font-size: 1.5em;">🔊</span>
|
||||||
<span id="volume-display" style="font-weight: bold; min-width: 50px; text-align: center; font-size: 1.2em;">{{ volume }}%</span>
|
<span id="volume-display" style="font-weight: bold; min-width: 50px; text-align: center; font-size: 1.2em;">{{ volume }}%</span>
|
||||||
</div>
|
</div>
|
||||||
<p style="margin-top: 15px; color: #6b7280; font-size: 0.9em; text-align: center;">
|
<p style="margin-top: 10px; color: #6b7280; font-size: 0.85em; text-align: center;">
|
||||||
Adjust the playback volume for greeting messages
|
Playback volume for greeting messages
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Greeting Delay Control -->
|
||||||
|
<div style="padding: 20px;">
|
||||||
|
<h3 style="margin: 0 0 15px 0; font-size: 1.1em; color: #333;">Greeting Delay</h3>
|
||||||
|
<div style="display: flex; align-items: center; gap: 20px;">
|
||||||
|
<span style="font-size: 1.5em;">⏱️</span>
|
||||||
|
<input type="range" id="delay-slider" min="0" max="10" value="{{ greeting_delay }}"
|
||||||
|
style="flex: 1; height: 8px; border-radius: 5px; outline: none; background: linear-gradient(to right, #667eea 0%, #667eea {{ greeting_delay * 10 }}%, #ddd {{ greeting_delay * 10 }}%, #ddd 100%);">
|
||||||
|
<span style="font-size: 1.5em;">⏰</span>
|
||||||
|
<span id="delay-display" style="font-weight: bold; min-width: 50px; text-align: center; font-size: 1.2em;">{{ greeting_delay }}s</span>
|
||||||
|
</div>
|
||||||
|
<p style="margin-top: 10px; color: #6b7280; font-size: 0.85em; text-align: center;">
|
||||||
|
Delay before greeting plays after pickup (0-10 seconds)
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1244,6 +1291,7 @@ if __name__ == "__main__":
|
|||||||
<script>
|
<script>
|
||||||
let selectedFiles = null;
|
let selectedFiles = null;
|
||||||
let volumeUpdateTimer = null;
|
let volumeUpdateTimer = null;
|
||||||
|
let delayUpdateTimer = null;
|
||||||
|
|
||||||
// Volume control
|
// Volume control
|
||||||
const volumeSlider = document.getElementById('volume-slider');
|
const volumeSlider = document.getElementById('volume-slider');
|
||||||
@@ -1280,6 +1328,42 @@ if __name__ == "__main__":
|
|||||||
.catch(error => console.error('Error updating volume:', error));
|
.catch(error => console.error('Error updating volume:', error));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Greeting Delay control
|
||||||
|
const delaySlider = document.getElementById('delay-slider');
|
||||||
|
const delayDisplay = document.getElementById('delay-display');
|
||||||
|
|
||||||
|
delaySlider.addEventListener('input', function() {
|
||||||
|
const value = this.value;
|
||||||
|
delayDisplay.textContent = value + 's';
|
||||||
|
|
||||||
|
// Update slider background gradient (0-10 range = 0-100%)
|
||||||
|
const percent = (value / 10) * 100;
|
||||||
|
this.style.background = `linear-gradient(to right, #667eea 0%, #667eea ${percent}%, #ddd ${percent}%, #ddd 100%)`;
|
||||||
|
|
||||||
|
// Debounce API call
|
||||||
|
clearTimeout(delayUpdateTimer);
|
||||||
|
delayUpdateTimer = setTimeout(() => {
|
||||||
|
updateGreetingDelay(value);
|
||||||
|
}, 300);
|
||||||
|
});
|
||||||
|
|
||||||
|
function updateGreetingDelay(delay) {
|
||||||
|
fetch('/api/greeting_delay', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ delay: parseInt(delay) })
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
console.log('Greeting delay updated to ' + data.delay + 's');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => console.error('Error updating delay:', error));
|
||||||
|
}
|
||||||
|
|
||||||
function handleFileSelect(input) {
|
function handleFileSelect(input) {
|
||||||
if (input.files && input.files.length > 0) {
|
if (input.files && input.files.length > 0) {
|
||||||
selectedFiles = input.files;
|
selectedFiles = input.files;
|
||||||
|
|||||||
Reference in New Issue
Block a user