add segment settings

Signed-off-by: Peter Siegmund <developer@mars3142.org>
This commit is contained in:
2026-01-01 17:31:41 +01:00
parent 52f6c2acab
commit b0e93d613c
5 changed files with 461 additions and 4 deletions

View File

@@ -1198,4 +1198,136 @@ body {
.scene-action-row select {
width: 100%;
}
}
/* WLED Configuration */
.segment-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.segment-header h3 {
margin: 0;
font-size: 1rem;
color: var(--text);
}
.btn-small {
padding: 6px 12px;
font-size: 0.85rem;
}
.wled-segments-list {
display: flex;
flex-direction: column;
gap: 12px;
margin-bottom: 20px;
}
.wled-segment-item {
display: grid;
grid-template-columns: 1fr auto auto auto;
gap: 12px;
align-items: flex-end;
padding: 16px;
background: var(--bg-color);
border-radius: 10px;
border: 1px solid var(--border);
}
.segment-name-field {
display: flex;
flex-direction: column;
gap: 4px;
}
.segment-number {
font-weight: 600;
font-size: 0.75rem;
color: var(--primary);
}
.segment-name-input {
padding: 8px 12px;
border: 1px solid var(--border);
border-radius: 6px;
background: var(--card-bg);
color: var(--text);
font-size: 0.9rem;
height: 38px;
box-sizing: border-box;
}
.segment-field {
display: flex;
flex-direction: column;
gap: 4px;
}
.segment-field label {
font-size: 0.75rem;
color: var(--text-secondary);
white-space: nowrap;
}
.segment-field input {
padding: 8px 10px;
border: 1px solid var(--border);
border-radius: 6px;
background: var(--card-bg);
color: var(--text);
font-size: 0.9rem;
text-align: center;
width: 70px;
height: 38px;
box-sizing: border-box;
}
.segment-remove-btn {
padding: 8px;
background: transparent;
border: 1px solid var(--border);
border-radius: 6px;
cursor: pointer;
color: var(--text-secondary);
transition: all 0.2s;
height: 38px;
width: 38px;
box-sizing: border-box;
align-self: flex-end;
}
.segment-remove-btn:hover {
background: #ff4444;
border-color: #ff4444;
color: white;
}
/* Responsive for WLED */
@media (max-width: 600px) {
.segment-header {
flex-direction: column;
align-items: flex-start;
gap: 10px;
}
.wled-segment-item {
grid-template-columns: 1fr 1fr;
grid-template-rows: auto auto auto;
}
.segment-number {
grid-column: 1 / -1;
}
.segment-name-input {
grid-column: 1 / -1;
}
.segment-remove-btn {
grid-column: 1 / -1;
justify-self: end;
}
}

View File

@@ -135,7 +135,8 @@
<div id="tab-config" class="tab-content">
<div class="sub-tabs">
<button class="sub-tab active" onclick="switchSubTab('wifi')" data-i18n="subtab.wifi">📶 WLAN</button>
<button class="sub-tab" onclick="switchSubTab('schema')" data-i18n="subtab.schema">💡 Schema</button>
<button class="sub-tab" onclick="switchSubTab('schema')" data-i18n="subtab.light">💡
Lichtsteuerung</button>
<button class="sub-tab" onclick="switchSubTab('devices')" data-i18n="subtab.devices">🔗 Geräte</button>
<button class="sub-tab" onclick="switchSubTab('scenes')" data-i18n="subtab.scenes">🎬 Szenen</button>
</div>
@@ -185,8 +186,39 @@
</div>
</div>
<!-- Schema Sub-Tab -->
<!-- Schema Sub-Tab (Lichtsteuerung) -->
<div id="subtab-schema" class="sub-tab-content">
<!-- WLED Konfiguration -->
<div class="card">
<h2 data-i18n="wled.config.title">WLED Konfiguration</h2>
<p class="card-description" data-i18n="wled.config.desc">Konfiguriere die WLED-Segmente und LEDs pro
Segment</p>
<div class="segment-header">
<h3 data-i18n="wled.segments.title">Segmente</h3>
<button class="btn btn-secondary btn-small" onclick="addWledSegment()"
data-i18n="wled.segment.add"> Segment hinzufügen</button>
</div>
<div id="wled-segments-list" class="wled-segments-list">
<!-- Wird dynamisch gefüllt -->
<div class="empty-state" id="no-wled-segments">
<span class="empty-icon">💡</span>
<p data-i18n="wled.segments.empty">Keine Segmente konfiguriert</p>
<p class="empty-hint" data-i18n="wled.segments.empty.hint">Klicke auf "Segment hinzufügen"
um ein Segment zu erstellen</p>
</div>
</div>
<div class="btn-group">
<button class="btn btn-primary" onclick="saveWledConfig()" data-i18n="btn.save">💾
Speichern</button>
</div>
<div id="wled-status" class="status"></div>
</div>
<!-- Schema Editor -->
<div class="card">
<h2 data-i18n="schema.editor.title">Licht-Schema Editor</h2>
@@ -423,6 +455,7 @@
<script src="js/scenes.js"></script>
<script src="js/devices.js"></script>
<script src="js/schema.js"></script>
<script src="js/wled.js"></script>
<script src="js/app.js"></script>
</body>

View File

@@ -12,10 +12,28 @@ const translations = {
// Sub Tabs
'subtab.wifi': '📶 WLAN',
'subtab.schema': '💡 Schema',
'subtab.light': '💡 Lichtsteuerung',
'subtab.devices': '🔗 Geräte',
'subtab.scenes': '🎬 Szenen',
// WLED Configuration
'wled.config.title': 'WLED Konfiguration',
'wled.config.desc': 'Konfiguriere die WLED-Segmente und LEDs pro Segment',
'wled.host': 'WLED Host',
'wled.host.placeholder': 'z.B. 192.168.1.100 oder wled.local',
'wled.segments.title': 'Segmente',
'wled.segments.empty': 'Keine Segmente konfiguriert',
'wled.segments.empty.hint': 'Klicke auf "Segment hinzufügen" um ein Segment zu erstellen',
'wled.segment.add': ' Segment hinzufügen',
'wled.segment.name': 'Segment {num}',
'wled.segment.leds': 'Anzahl LEDs',
'wled.segment.start': 'Start-LED',
'wled.segment.remove': 'Entfernen',
'wled.saved': 'WLED-Konfiguration gespeichert!',
'wled.error.host': 'Bitte WLED Host eingeben',
'wled.error.save': 'Fehler beim Speichern der WLED-Konfiguration',
'wled.loaded': 'WLED-Konfiguration geladen',
// Light Control
'control.light.title': 'Lichtsteuerung',
'control.light.onoff': 'Ein/Aus',
@@ -168,10 +186,28 @@ const translations = {
// Sub Tabs
'subtab.wifi': '📶 WiFi',
'subtab.schema': '💡 Schema',
'subtab.light': '💡 Light Control',
'subtab.devices': '🔗 Devices',
'subtab.scenes': '🎬 Scenes',
// WLED Configuration
'wled.config.title': 'WLED Configuration',
'wled.config.desc': 'Configure WLED segments and LEDs per segment',
'wled.host': 'WLED Host',
'wled.host.placeholder': 'e.g. 192.168.1.100 or wled.local',
'wled.segments.title': 'Segments',
'wled.segments.empty': 'No segments configured',
'wled.segments.empty.hint': 'Click "Add Segment" to create a segment',
'wled.segment.add': ' Add Segment',
'wled.segment.name': 'Segment {num}',
'wled.segment.leds': 'Number of LEDs',
'wled.segment.start': 'Start LED',
'wled.segment.remove': 'Remove',
'wled.saved': 'WLED configuration saved!',
'wled.error.host': 'Please enter WLED host',
'wled.error.save': 'Error saving WLED configuration',
'wled.loaded': 'WLED configuration loaded',
// Light Control
'control.light.title': 'Light Control',
'control.light.onoff': 'On/Off',

View File

@@ -0,0 +1,172 @@
// WLED Configuration Module
// Manages WLED segments and LED configuration
let wledConfig = {
segments: []
};
/**
* Initialize WLED module
*/
function initWled() {
loadWledConfig();
}
/**
* Load WLED configuration from server
*/
async function loadWledConfig() {
try {
const response = await fetch('/api/wled/config');
if (response.ok) {
wledConfig = await response.json();
renderWledSegments();
showStatus('wled-status', t('wled.loaded'), 'success');
}
} catch (error) {
console.log('Using default WLED config');
wledConfig = { segments: [] };
renderWledSegments();
}
}
/**
* Render WLED segments list
*/
function renderWledSegments() {
const list = document.getElementById('wled-segments-list');
const emptyState = document.getElementById('no-wled-segments');
if (!list) return;
// Clear existing segments (keep empty state)
const existingItems = list.querySelectorAll('.wled-segment-item');
existingItems.forEach(item => item.remove());
if (wledConfig.segments.length === 0) {
if (emptyState) emptyState.style.display = 'block';
return;
}
if (emptyState) emptyState.style.display = 'none';
wledConfig.segments.forEach((segment, index) => {
const item = createSegmentElement(segment, index);
list.insertBefore(item, emptyState);
});
}
/**
* Create segment DOM element
* @param {object} segment - Segment data
* @param {number} index - Segment index
* @returns {HTMLElement} Segment element
*/
function createSegmentElement(segment, index) {
const item = document.createElement('div');
item.className = 'wled-segment-item';
item.dataset.index = index;
item.innerHTML = `
<div class="segment-name-field">
<label class="segment-number">${t('wled.segment.name', { num: index + 1 })}</label>
<input type="text" class="segment-name-input" value="${escapeHtml(segment.name || '')}"
placeholder="${t('wled.segment.name', { num: index + 1 })}"
onchange="updateSegment(${index}, 'name', this.value)">
</div>
<div class="segment-field">
<label data-i18n="wled.segment.start">${t('wled.segment.start')}</label>
<input type="number" min="0" value="${segment.start || 0}"
onchange="updateSegment(${index}, 'start', parseInt(this.value))">
</div>
<div class="segment-field">
<label data-i18n="wled.segment.leds">${t('wled.segment.leds')}</label>
<input type="number" min="1" value="${segment.leds || 1}"
onchange="updateSegment(${index}, 'leds', parseInt(this.value))">
</div>
<button class="segment-remove-btn" onclick="removeWledSegment(${index})" title="${t('wled.segment.remove')}">
🗑️
</button>
`;
return item;
}
/**
* Add a new WLED segment
*/
function addWledSegment() {
// Calculate next start position
let nextStart = 0;
if (wledConfig.segments.length > 0) {
const lastSegment = wledConfig.segments[wledConfig.segments.length - 1];
nextStart = (lastSegment.start || 0) + (lastSegment.leds || 0);
}
wledConfig.segments.push({
name: '',
start: nextStart,
leds: 10
});
renderWledSegments();
}
/**
* Update a segment property
* @param {number} index - Segment index
* @param {string} property - Property name
* @param {*} value - New value
*/
function updateSegment(index, property, value) {
if (index >= 0 && index < wledConfig.segments.length) {
wledConfig.segments[index][property] = value;
}
}
/**
* Remove a WLED segment
* @param {number} index - Segment index to remove
*/
function removeWledSegment(index) {
if (index >= 0 && index < wledConfig.segments.length) {
wledConfig.segments.splice(index, 1);
renderWledSegments();
}
}
/**
* Save WLED configuration to server
*/
async function saveWledConfig() {
try {
const response = await fetch('/api/wled/config', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(wledConfig)
});
if (response.ok) {
showStatus('wled-status', t('wled.saved'), 'success');
} else {
throw new Error('Save failed');
}
} catch (error) {
console.error('Error saving WLED config:', error);
showStatus('wled-status', t('wled.error.save'), 'error');
}
}
/**
* Helper function to escape HTML
* @param {string} text - Text to escape
* @returns {string} Escaped text
*/
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// Initialize when DOM is ready
document.addEventListener('DOMContentLoaded', initWled);