diff --git a/firmware/data/bottom.js b/firmware/data/bottom.js
new file mode 100644
index 0000000..e997670
--- /dev/null
+++ b/firmware/data/bottom.js
@@ -0,0 +1,299 @@
+function addTabListener() {
+try {
+var tabMain = document.getElementById("tab-lights");
+var tabConfig = document.getElementById("tab-config");
+var tabTDE = document.getElementById("tab-tde");
+var amain = document.getElementById("tab-a-lights");
+var acfg = document.getElementById("tab-a-config");
+var atde = document.getElementById("tab-a-tde");
+amain.addEventListener("click", function() {
+tabMain.classList.add("visible");
+tabConfig.classList.remove("visible");
+tabTDE.classList.remove("visible");
+amain.classList.add("pure-button-primary");
+acfg.classList.remove("pure-button-primary");
+atde.classList.remove("pure-button-primary");
+});
+acfg.addEventListener("click", function() {
+tabMain.classList.remove("visible");
+tabConfig.classList.add("visible");
+tabTDE.classList.remove("visible");
+amain.classList.remove("pure-button-primary");
+acfg.classList.add("pure-button-primary");
+atde.classList.remove("pure-button-primary");
+});
+atde.addEventListener("click", function() {
+tabMain.classList.remove("visible");
+tabConfig.classList.remove("visible");
+tabTDE.classList.add("visible");
+amain.classList.remove("pure-button-primary");
+acfg.classList.remove("pure-button-primary");
+atde.classList.add("pure-button-primary");
+createTable();
+});
+} catch (error) {
+console.log("Fehler in load listener of the tab action listener management: " + error.message);
+}
+}
+window.addEventListener('load', function() {
+addTabListener();
+});
+function loadGraphData() {
+console.log('----> generate graph <----');
+$.getJSON('/tc_data_blocks_read', function(data) {
+var currenttime = [];
+var time = [];
+var channel1 = [];
+var channel2 = [];
+var channel3 = [];
+var channel4 = [];
+for (var i = 0; i < data['tcdata'].length; i++) {
+time.push(data['tcdata'][i]['hour'] + ':' + (data['tcdata'][i]['min'] < 10 ? '0' : '') + data['tcdata'][i]['min']);
+channel1.push(data['tcdata'][i]['ch1']);
+channel2.push(data['tcdata'][i]['ch2']);
+channel3.push(data['tcdata'][i]['ch3']);
+channel4.push(data['tcdata'][i]['ch4']);
+}
+currenttime.push(data['currenttime']['hour']);
+currenttime.push(data['currenttime']['min']);
+console.log(currenttime);
+var currentTimeStr = currenttime[0] + ':' + (currenttime[1] < 10 ? '0' : '') + currenttime[1];
+var index = time.indexOf(currentTimeStr);
+if (index === -1) {
+var lowerIndex = -1;
+var upperIndex = -1;
+for (var i = 0; i < time.length - 1; i++) {
+console.log(time[i] + ' <= ' + currentTimeStr + ' >= ' + time[i + 1]);
+const currentDate = new Date();
+const year = currentDate.getFullYear();
+const month = (currentDate.getMonth() + 1).toString().padStart(2, '0');
+const day = currentDate.getDate().toString().padStart(2, '0');
+const dateString = `${year}-${month}-${day}`;
+const start = moment(dateString + ' ' + time[i], 'YYYY-MM-DD HH:mm');
+const curr = moment(dateString + ' ' + currentTimeStr, 'YYYY-MM-DD HH:mm');
+const end = moment(dateString + ' ' + time[i + 1], 'YYYY-MM-DD HH:mm');
+console.log(start.format('YYYY-MM-DD HH:mm') + ' <= ' + curr.format('YYYY-MM-DD HH:mm') + ' >= ' + end.format('YYYY-MM-DD HH:mm'));
+console.log(curr.isBetween(start, end));
+if (curr.isBetween(start, end)) {
+lowerIndex = i;
+upperIndex = i + 1;
+break;
+}
+}
+console.log('lowerIndex=' + lowerIndex);
+console.log('upperIndex=' + upperIndex);
+if (lowerIndex === -1 || upperIndex === -1) {
+console.log("Error: Current time not found in time array and not between two elements in time array.");
+lowerIndex = 0;
+upperIndex = 1;
+var tmp1 = time[0].split(':');
+console.log('tmp1 = ' + tmp1);
+currenttime[0] = tmp1[0];
+currenttime[1] = tmp1[1];
+}
+var lowerTime = time[lowerIndex].split(":");
+var upperTime = time[upperIndex].split(":");
+var timeDiff = (currenttime[0] - lowerTime[0]) + ((currenttime[1] - lowerTime[1]) / 60);
+var indexFloat = lowerIndex + timeDiff / ((upperTime[0] - lowerTime[0]) + ((upperTime[1] - lowerTime[1]) / 60));
+console.log("Index (float): " + indexFloat);
+} else {
+console.log("Index (integer): " + index);
+console.log("Index (float): " + index);
+}
+if (indexFloat > index) {
+index = indexFloat;
+}
+console.log("index in graph >>>" + index);
+var trace1 = {
+x: time,
+y: channel1,
+name: 'Channel 1',
+type: 'scatter',
+mode: 'lines+markers',
+};
+var trace2 = {
+x: time,
+y: channel2,
+name: 'Channel 2',
+type: 'scatter',
+mode: 'lines+markers',
+};
+var trace3 = {
+x: time,
+y: channel3,
+name: 'Channel 3',
+type: 'scatter',
+mode: 'lines+markers',
+};
+var trace4 = {
+x: time,
+y: channel4,
+name: 'Channel 4',
+type: 'scatter',
+mode: 'lines+markers',
+};
+var layout = {
+title: 'Timing Control Data Blocks',
+xaxis: {
+title: 'Time',
+tickangle: -45,
+},
+yaxis: {
+title: 'Brightness',
+range: [0, 255],
+},
+shapes: [{
+type: 'line',
+x0: index,
+y0: 0,
+x1: index,
+y1: 255,
+line: {
+color: 'lightgrey',
+width: 3,
+dash: 'dot'
+}
+}]
+};
+Plotly.newPlot('plot_chart', [trace1, trace2, trace3, trace4], layout);
+});
+}
+setInterval(loadGraphData, 10000);
+loadGraphData();
+function updateLightState() {
+console.log('----> setting bri and power switch <----');
+for (let i = 1; i <= {{LIGHT_COUNT}}; i++) {
+const lightURL = `http://{{IP_ADDRESS}}/state?light=${i}`;
+fetch(lightURL).then(response => response.json()).then(data => {
+const briSlider = document.getElementById(`bri${i - 1}`);
+const briSliderVal = document.getElementById(`bri${i - 1}_val`);
+const onLinkOn = document.getElementById(`on${i - 1}_on`);
+const onLinkOff = document.getElementById(`on${i - 1}_off`);
+briSlider.value = data.bri;
+briSliderVal.innerHTML = (Math.round((data.bri * 100.0 / 255.0) * 100) / 100).toFixed(2);
+console.log('data.on ' + i + ' = ' + data.on);
+if (data.on == true) {
+onLinkOn.classList.add('pure-button-primary');
+onLinkOff.classList.remove('pure-button-primary');
+} else {
+onLinkOn.classList.remove('pure-button-primary');
+onLinkOff.classList.add('pure-button-primary');
+}
+}).catch(error => console.error(error));
+}
+}
+setInterval(updateLightState, 10000);
+updateLightState();
+function updatePWMValues() {
+console.log('----> setting pwm data <----');
+for (let i = 0; i < {{LIGHT_COUNT}}; i++) {
+const lightID = i + 1;
+const pwmElement = document.getElementById(`light${i}_pwm`);
+const pwmElementTxt = document.getElementById(`light${i}_pwm_txt`);
+if (pwmElement) {
+const url = `http://{{IP_ADDRESS}}/state?light=${lightID}`;
+fetch(url).then(response => response.json()).then(data => {
+const pwmValue = ((Math.round((data.curpwm - ((data.curpwm >= {{PWM_MIN}}) ? {{PWM_MIN}} : 0)) / {{PWM_MAX}} * 10000) / 100).toFixed(2));
+console.log('curpwm[' + i + '] = ' + data.curpwm + ' = ' + pwmValue);
+pwmElement.innerText = pwmValue.toString();
+pwmElement.value = pwmValue;
+pwmElementTxt.innerText = pwmValue.toString();
+}).catch(error => console.error(error));
+}
+}
+}
+updatePWMValues();
+setInterval(updatePWMValues, 5000);
+var links = document.querySelectorAll('[id^="on"][id$="_off"]');
+links.forEach(function(link) {
+link.addEventListener('click', function(event) {
+event.preventDefault();
+var id = this.id.replace('on', '').replace('_off', '');
+var xhr = new XMLHttpRequest();
+xhr.open('GET', 'http://{{IP_ADDRESS}}/?on' + id + '=false&transition=' + document.getElementById('transition').value, true);
+xhr.send();
+updateLightState();
+this.classList.add('pure-button-primary');
+document.getElementById('on'+id+'_on').classList.remove('pure-button-primary');
+});
+});
+var links = document.querySelectorAll('[id^="on"][id$="_on"]');
+links.forEach(function(link) {
+link.addEventListener('click', function(event) {
+event.preventDefault();
+var id = this.id.replace('on', '').replace('_on', '');
+var xhr = new XMLHttpRequest();
+xhr.open('GET', 'http://{{IP_ADDRESS}}/?on' + id + '=true&transition=' + document.getElementById('transition').value, true);
+xhr.send();
+updateLightState();
+this.classList.add('pure-button-primary');
+document.getElementById('on'+id+'_off').classList.remove('pure-button-primary');
+});
+});
+function createTable() {
+var table = document.createElement("table");
+var headerRow = document.createElement("tr");
+var headers = ["Stunde", "Minute", "ch1", "ch2", "ch3", "ch4"];
+for (var i = 0; i < headers.length; i++) {
+var header = document.createElement("th");
+header.innerHTML = headers[i];
+headerRow.appendChild(header);
+}
+table.appendChild(headerRow);
+for (var row = 0; row < 10; row++) {
+var contentRow = document.createElement("tr");
+var hourCell = document.createElement("td");
+var hourInput = document.createElement("input");
+hourInput.type = "number";
+hourInput.min = 0;
+hourInput.max = 23;
+hourCell.appendChild(hourInput);
+contentRow.appendChild(hourCell);
+var minuteCell = document.createElement("td");
+var minuteSelect = document.createElement("select");
+for (var minute = 0; minute <= 50; minute += 10) {
+var option = document.createElement("option");
+option.value = minute;
+option.text = minute.toString().padStart(2, "0");
+minuteSelect.appendChild(option);
+}
+minuteCell.appendChild(minuteSelect);
+contentRow.appendChild(minuteCell);
+for (var channel = 1; channel <= 4; channel++) {
+var channelCell = document.createElement("td");
+var channelInput = document.createElement("input");
+channelInput.type = "number";
+channelInput.min = 0;
+channelInput.max = 100;
+channelCell.appendChild(channelInput);
+contentRow.appendChild(channelCell);
+}
+table.appendChild(contentRow);
+}
+var button = document.createElement("button");
+button.innerHTML = "Export als JSON";
+button.onclick = function() {
+var data = [];
+var rows = table.getElementsByTagName("tr");
+for (var row = 1; row < rows.length; row++) {
+var cells = rows[row].getElementsByTagName("td");
+var hour = cells[0].querySelector("input").value;
+var minute = cells[1].querySelector("select").value;
+var ch1 = cells[2].querySelector("input").value;
+var ch2 = cells[3].querySelector("input").value;
+var ch3 = cells[4].querySelector("input").value;
+var ch4 = cells[5].querySelector("input").value;
+var rowObject = {"hour": hour, "min": minute, "ch1": ch1, "ch2": ch2, "ch3": ch3, "ch4": ch4};
+data.push(rowObject);
+}
+var json = JSON.stringify(data);
+console.log(json);
+var xhr = new XMLHttpRequest();
+xhr.open("POST", "http://{{IP_ADDRESS}}/tc_data_save?data=" + encodeURIComponent(json), true);
+xhr.send();
+};
+var container = document.getElementById("table-container");
+container.innerHTML = "";
+container.appendChild(table);
+container.appendChild(button);
+}
\ No newline at end of file
diff --git a/firmware/data/index_template_bottom.html b/firmware/data/index_template_bottom.html
index 7bec1c0..990ac03 100644
--- a/firmware/data/index_template_bottom.html
+++ b/firmware/data/index_template_bottom.html
@@ -1,249 +1,14 @@
-
+
+
+
+
+
-
diff --git a/firmware/data/index_template_top.html b/firmware/data/index_template_top.html
index fc985f7..d0cadaf 100644
--- a/firmware/data/index_template_top.html
+++ b/firmware/data/index_template_top.html
@@ -57,48 +57,7 @@ display: block;
-
diff --git a/firmware/data/tc_data_edit.html b/firmware/data/tc_data_edit.html
deleted file mode 100644
index e358441..0000000
--- a/firmware/data/tc_data_edit.html
+++ /dev/null
@@ -1,184 +0,0 @@
-
-
-Daten senden
-
\ No newline at end of file
diff --git a/firmware/data/top.js b/firmware/data/top.js
new file mode 100644
index 0000000..42eebbd
--- /dev/null
+++ b/firmware/data/top.js
@@ -0,0 +1,41 @@
+var links = document.querySelectorAll('[id^="tc_on"]');
+links.forEach(function(link) {
+link.addEventListener('click', function(event) {
+event.preventDefault();
+var xhr = new XMLHttpRequest();
+xhr.open('GET', 'http://{{IP_ADDRESS}}/?tc=true', true);
+xhr.send();
+console.log('tc=true call');
+document.getElementById('tc_on').classList.add('pure-button-primary');
+document.getElementById('tc_off').classList.remove('pure-button-primary');
+});
+});
+var links = document.querySelectorAll('[id^="tc_off"]');
+links.forEach(function(link) {
+link.addEventListener('click', function(event) {
+event.preventDefault();
+var xhr = new XMLHttpRequest();
+xhr.open('GET', 'http://{{IP_ADDRESS}}/?tc=false', true);
+xhr.send();
+console.log('tc=false call');
+document.getElementById('tc_off').classList.add('pure-button-primary');
+document.getElementById('tc_on').classList.remove('pure-button-primary');
+});
+});
+let timeoutId;
+function sendSliderValue(x) {
+x = x - 1;
+clearTimeout(timeoutId);
+timeoutId = setTimeout(() => {
+var value = document.getElementById(`bri${x}`).value;
+var url = `http://{{IP_ADDRESS}}/?bri${x}=${value}`;
+fetch(url).then(response => {
+if (!response.ok) {
+throw new Error(`HTTP error! status: ${response.status}`);
+}
+console.log(`Sent slider value ${value} to ${url}`);
+}).catch(error => {
+console.error(`Error sending slider value to ${url}: ${error}`);
+});
+}, 500);
+}
\ No newline at end of file
diff --git a/firmware/firmware.ino b/firmware/firmware.ino
index 538203b..d498186 100644
--- a/firmware/firmware.ino
+++ b/firmware/firmware.ino
@@ -489,9 +489,14 @@ void init_webserver()
}
});
- server.on("/tc_data_edit", []()
+ server.on("/js_top", []()
{
- server.send(200, "text/html", genTCEditHTML());
+ server.send(200, "text/html", replacePlaceholder(loadSPIFFSFile("/top.js")));
+ });
+
+ server.on("/js_bottom", []()
+ {
+ server.send(200, "text/html", replacePlaceholder(loadSPIFFSFile("/bottom.js")));
});
server.on("/", []()
diff --git a/firmware/html/bottom.js b/firmware/html/bottom.js
new file mode 100644
index 0000000..a01ff19
--- /dev/null
+++ b/firmware/html/bottom.js
@@ -0,0 +1,344 @@
+function addTabListener() {
+ try {
+ var tabMain = document.getElementById("tab-lights");
+ var tabConfig = document.getElementById("tab-config");
+ var tabTDE = document.getElementById("tab-tde");
+
+ var amain = document.getElementById("tab-a-lights");
+ var acfg = document.getElementById("tab-a-config");
+ var atde = document.getElementById("tab-a-tde");
+
+
+ amain.addEventListener("click", function() {
+ tabMain.classList.add("visible");
+ tabConfig.classList.remove("visible");
+ tabTDE.classList.remove("visible");
+
+ amain.classList.add("pure-button-primary");
+ acfg.classList.remove("pure-button-primary");
+ atde.classList.remove("pure-button-primary");
+ });
+
+ acfg.addEventListener("click", function() {
+ tabMain.classList.remove("visible");
+ tabConfig.classList.add("visible");
+ tabTDE.classList.remove("visible");
+
+ amain.classList.remove("pure-button-primary");
+ acfg.classList.add("pure-button-primary");
+ atde.classList.remove("pure-button-primary");
+ });
+
+ atde.addEventListener("click", function() {
+ tabMain.classList.remove("visible");
+ tabConfig.classList.remove("visible");
+ tabTDE.classList.add("visible");
+
+ amain.classList.remove("pure-button-primary");
+ acfg.classList.remove("pure-button-primary");
+ atde.classList.add("pure-button-primary");
+
+ createTable();
+ });
+ } catch (error) {
+ console.log("Fehler in load listener of the tab action listener management: " + error.message);
+ }
+}
+window.addEventListener('load', function() {
+ addTabListener();
+});
+
+function loadGraphData() {
+ console.log('----> generate graph <----');
+ $.getJSON('/tc_data_blocks_read', function(data) {
+ var currenttime = [];
+ var time = [];
+ var channel1 = [];
+ var channel2 = [];
+ var channel3 = [];
+ var channel4 = [];
+ for (var i = 0; i < data['tcdata'].length; i++) {
+ time.push(data['tcdata'][i]['hour'] + ':' + (data['tcdata'][i]['min'] < 10 ? '0' : '') + data['tcdata'][i]['min']);
+ channel1.push(data['tcdata'][i]['ch1']);
+ channel2.push(data['tcdata'][i]['ch2']);
+ channel3.push(data['tcdata'][i]['ch3']);
+ channel4.push(data['tcdata'][i]['ch4']);
+ }
+ currenttime.push(data['currenttime']['hour']);
+ currenttime.push(data['currenttime']['min']);
+ console.log(currenttime);
+ var currentTimeStr = currenttime[0] + ':' + (currenttime[1] < 10 ? '0' : '') + currenttime[1];
+ var index = time.indexOf(currentTimeStr);
+ if (index === -1) {
+ var lowerIndex = -1;
+ var upperIndex = -1;
+ for (var i = 0; i < time.length - 1; i++) {
+ console.log(time[i] + ' <= ' + currentTimeStr + ' >= ' + time[i + 1]);
+ const currentDate = new Date();
+ const year = currentDate.getFullYear();
+ const month = (currentDate.getMonth() + 1).toString().padStart(2, '0');
+ const day = currentDate.getDate().toString().padStart(2, '0');
+ const dateString = `${year}-${month}-${day}`;
+ const start = moment(dateString + ' ' + time[i], 'YYYY-MM-DD HH:mm');
+ const curr = moment(dateString + ' ' + currentTimeStr, 'YYYY-MM-DD HH:mm');
+ const end = moment(dateString + ' ' + time[i + 1], 'YYYY-MM-DD HH:mm');
+ console.log(start.format('YYYY-MM-DD HH:mm') + ' <= ' + curr.format('YYYY-MM-DD HH:mm') + ' >= ' + end.format('YYYY-MM-DD HH:mm'));
+ console.log(curr.isBetween(start, end));
+ if (curr.isBetween(start, end)) {
+ lowerIndex = i;
+ upperIndex = i + 1;
+ break;
+ }
+ }
+ console.log('lowerIndex=' + lowerIndex);
+ console.log('upperIndex=' + upperIndex);
+ if (lowerIndex === -1 || upperIndex === -1) {
+ console.log("Error: Current time not found in time array and not between two elements in time array.");
+ lowerIndex = 0;
+ upperIndex = 1;
+ var tmp1 = time[0].split(':');
+ console.log('tmp1 = ' + tmp1);
+ currenttime[0] = tmp1[0];
+ currenttime[1] = tmp1[1];
+ }
+ var lowerTime = time[lowerIndex].split(":");
+ var upperTime = time[upperIndex].split(":");
+ var timeDiff = (currenttime[0] - lowerTime[0]) + ((currenttime[1] - lowerTime[1]) / 60);
+ var indexFloat = lowerIndex + timeDiff / ((upperTime[0] - lowerTime[0]) + ((upperTime[1] - lowerTime[1]) / 60));
+ console.log("Index (float): " + indexFloat);
+ } else {
+ console.log("Index (integer): " + index);
+ console.log("Index (float): " + index);
+ }
+ if (indexFloat > index) {
+ index = indexFloat;
+ }
+ console.log("index in graph >>>" + index);
+ var trace1 = {
+ x: time,
+ y: channel1,
+ name: 'Channel 1',
+ type: 'scatter',
+ mode: 'lines+markers',
+ };
+ var trace2 = {
+ x: time,
+ y: channel2,
+ name: 'Channel 2',
+ type: 'scatter',
+ mode: 'lines+markers',
+ };
+ var trace3 = {
+ x: time,
+ y: channel3,
+ name: 'Channel 3',
+ type: 'scatter',
+ mode: 'lines+markers',
+ };
+ var trace4 = {
+ x: time,
+ y: channel4,
+ name: 'Channel 4',
+ type: 'scatter',
+ mode: 'lines+markers',
+ };
+ var layout = {
+ title: 'Timing Control Data Blocks',
+ xaxis: {
+ title: 'Time',
+ tickangle: -45,
+ },
+ yaxis: {
+ title: 'Brightness',
+ range: [0, 255],
+ },
+ shapes: [{
+ type: 'line',
+ x0: index,
+ y0: 0,
+ x1: index,
+ y1: 255,
+ line: {
+ color: 'lightgrey',
+ width: 3,
+ dash: 'dot'
+ }
+ }]
+ };
+ Plotly.newPlot('plot_chart', [trace1, trace2, trace3, trace4], layout);
+ });
+}
+setInterval(loadGraphData, 10000);
+loadGraphData();
+
+function updateLightState() {
+ console.log('----> setting bri and power switch <----');
+ for (let i = 1; i <= {{LIGHT_COUNT}}; i++) {
+ const lightURL = `http://{{IP_ADDRESS}}/state?light=${i}`;
+ fetch(lightURL).then(response => response.json()).then(data => {
+ const briSlider = document.getElementById(`bri${i - 1}`);
+ const briSliderVal = document.getElementById(`bri${i - 1}_val`);
+ const onLinkOn = document.getElementById(`on${i - 1}_on`);
+ const onLinkOff = document.getElementById(`on${i - 1}_off`);
+ briSlider.value = data.bri;
+ briSliderVal.innerHTML = (Math.round((data.bri * 100.0 / 255.0) * 100) / 100).toFixed(2);
+ console.log('data.on ' + i + ' = ' + data.on);
+ if (data.on == true) {
+ onLinkOn.classList.add('pure-button-primary');
+ onLinkOff.classList.remove('pure-button-primary');
+ } else {
+ onLinkOn.classList.remove('pure-button-primary');
+ onLinkOff.classList.add('pure-button-primary');
+ }
+ }).catch(error => console.error(error));
+ }
+}
+setInterval(updateLightState, 10000);
+updateLightState();
+
+function updatePWMValues() {
+ console.log('----> setting pwm data <----');
+ for (let i = 0; i < {{LIGHT_COUNT}}; i++) {
+ const lightID = i + 1;
+ const pwmElement = document.getElementById(`light${i}_pwm`);
+ const pwmElementTxt = document.getElementById(`light${i}_pwm_txt`);
+ if (pwmElement) {
+ const url = `http://{{IP_ADDRESS}}/state?light=${lightID}`;
+ fetch(url).then(response => response.json()).then(data => {
+ const pwmValue = ((Math.round((data.curpwm - ((data.curpwm >= {{PWM_MIN}}) ? {{PWM_MIN}} : 0)) / {{PWM_MAX}} * 10000) / 100).toFixed(2));
+ console.log('curpwm[' + i + '] = ' + data.curpwm + ' = ' + pwmValue);
+ pwmElement.innerText = pwmValue.toString();
+ pwmElement.value = pwmValue;
+ pwmElementTxt.innerText = pwmValue.toString();
+ }).catch(error => console.error(error));
+ }
+ }
+}
+updatePWMValues();
+setInterval(updatePWMValues, 5000);
+
+// Suche nach allen Links auf der Seite mit IDs von on0_off bis on3_off
+var links = document.querySelectorAll('[id^="on"][id$="_off"]');
+// Füge einen Klick-Listener zu jedem Link hinzu
+links.forEach(function(link) {
+ link.addEventListener('click', function(event) {
+ // Verhindere, dass der Link die Seite neu lädt
+ event.preventDefault();
+ // Extrahiere die Zahl aus der ID des Links
+ var id = this.id.replace('on', '').replace('_off', '');
+ // Erstelle eine neue Anfrage an die entsprechende URL
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', 'http://{{IP_ADDRESS}}/?on' + id + '=false&transition=' + document.getElementById('transition').value, true);
+ // Sende die Anfrage im Hintergrund
+ xhr.send();
+ updateLightState();
+ this.classList.add('pure-button-primary');
+ document.getElementById('on'+id+'_on').classList.remove('pure-button-primary');
+ });
+});
+// Suche nach allen Links auf der Seite mit IDs von on0_off bis on3_off
+var links = document.querySelectorAll('[id^="on"][id$="_on"]');
+// Füge einen Klick-Listener zu jedem Link hinzu
+links.forEach(function(link) {
+ link.addEventListener('click', function(event) {
+ // Verhindere, dass der Link die Seite neu lädt
+ event.preventDefault();
+ // Extrahiere die Zahl aus der ID des Links
+ var id = this.id.replace('on', '').replace('_on', '');
+ // Erstelle eine neue Anfrage an die entsprechende URL
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', 'http://{{IP_ADDRESS}}/?on' + id + '=true&transition=' + document.getElementById('transition').value, true);
+ // Sende die Anfrage im Hintergrund
+ xhr.send();
+ updateLightState();
+ this.classList.add('pure-button-primary');
+ document.getElementById('on'+id+'_off').classList.remove('pure-button-primary');
+ });
+});
+
+function createTable() {
+var table = document.createElement("table");
+
+// Headerzeile
+var headerRow = document.createElement("tr");
+var headers = ["Stunde", "Minute", "ch1", "ch2", "ch3", "ch4"];
+for (var i = 0; i < headers.length; i++) {
+ var header = document.createElement("th");
+ header.innerHTML = headers[i];
+ headerRow.appendChild(header);
+}
+table.appendChild(headerRow);
+
+// Inhaltszeilen
+for (var row = 0; row < 10; row++) {
+ var contentRow = document.createElement("tr");
+
+ // Spalte "Stunde"
+ var hourCell = document.createElement("td");
+ var hourInput = document.createElement("input");
+ hourInput.type = "number";
+ hourInput.min = 0;
+ hourInput.max = 23;
+ hourCell.appendChild(hourInput);
+ contentRow.appendChild(hourCell);
+
+ // Spalte "Minute"
+ var minuteCell = document.createElement("td");
+ var minuteSelect = document.createElement("select");
+ for (var minute = 0; minute <= 50; minute += 10) {
+ var option = document.createElement("option");
+ option.value = minute;
+ option.text = minute.toString().padStart(2, "0");
+ minuteSelect.appendChild(option);
+ }
+ minuteCell.appendChild(minuteSelect);
+ contentRow.appendChild(minuteCell);
+
+ // Spalten "ch1" bis "ch4"
+ for (var channel = 1; channel <= 4; channel++) {
+ var channelCell = document.createElement("td");
+ var channelInput = document.createElement("input");
+ channelInput.type = "number";
+ channelInput.min = 0;
+ channelInput.max = 100;
+ channelCell.appendChild(channelInput);
+ contentRow.appendChild(channelCell);
+ }
+
+ table.appendChild(contentRow);
+}
+
+// Button
+var button = document.createElement("button");
+button.innerHTML = "Export als JSON";
+button.onclick = function() {
+ var data = [];
+
+ var rows = table.getElementsByTagName("tr");
+ for (var row = 1; row < rows.length; row++) {
+ var cells = rows[row].getElementsByTagName("td");
+
+ var hour = cells[0].querySelector("input").value;
+ var minute = cells[1].querySelector("select").value;
+ var ch1 = cells[2].querySelector("input").value;
+ var ch2 = cells[3].querySelector("input").value;
+ var ch3 = cells[4].querySelector("input").value;
+ var ch4 = cells[5].querySelector("input").value;
+
+ var rowObject = {"hour": hour, "min": minute, "ch1": ch1, "ch2": ch2, "ch3": ch3, "ch4": ch4};
+ data.push(rowObject);
+ }
+
+ var json = JSON.stringify(data);
+ console.log(json);
+ var xhr = new XMLHttpRequest();
+ xhr.open("POST", "http://{{IP_ADDRESS}}/tc_data_save?data=" + encodeURIComponent(json), true);
+ xhr.send();
+
+};
+
+var container = document.getElementById("table-container");
+container.innerHTML = "";
+container.appendChild(table);
+container.appendChild(button);
+}
\ No newline at end of file
diff --git a/firmware/html/index_template_bottom.html b/firmware/html/index_template_bottom.html
index 8ab0126..0d0142e 100644
--- a/firmware/html/index_template_bottom.html
+++ b/firmware/html/index_template_bottom.html
@@ -1,283 +1,15 @@
-
+
+
+
+
+
-
diff --git a/firmware/html/index_template_top.html b/firmware/html/index_template_top.html
index d70d8b7..a2fbf43 100644
--- a/firmware/html/index_template_top.html
+++ b/firmware/html/index_template_top.html
@@ -1,108 +1,66 @@
-
-
-
-
-
- Light setup - {{LIGHT_NAME}}
-
-
-
-
-
-
-
-
-