Compare commits
No commits in common. "fc9baddfbdcdecd0ebf228d9160925869c6601e8" and "076a0162c4fd79936a303545ec032a1e8401be72" have entirely different histories.
fc9baddfbd
...
076a0162c4
21 changed files with 788 additions and 409 deletions
BIN
firmware/20230502_dev_firmware.ino.bin
Normal file
BIN
firmware/20230502_dev_firmware.ino.bin
Normal file
Binary file not shown.
BIN
firmware/20230502_firmware.ino.bin
Normal file
BIN
firmware/20230502_firmware.ino.bin
Normal file
Binary file not shown.
|
@ -6,8 +6,7 @@
|
|||
#define LOCAL_TIMEZONE_STRING "CET-1CEST-2,M3.5.0/02:00:00,M10.5.0/03:00:00" // Berlin, Germany - https://sites.google.com/a/usapiens.com/opnode/time-zones
|
||||
|
||||
//#define DEVELOPMENT
|
||||
|
||||
#define LIGHT_NAME_STR "Dimmable Hue Light" // default light name
|
||||
#define light_name "Dimmable Hue Light" // default light name
|
||||
|
||||
#define LIGHTS_COUNT 4 // do not modify because luminie p30 only has 4 channel, never set above 80
|
||||
|
||||
|
@ -24,3 +23,4 @@
|
|||
#define TIME_LIGHTENGINE_INTERVAL_MS (1000UL / BRI_MOD_STEPS_PER_SEC) // BRI_MOD_STEPS_PER_SEC steps per second to in-/decrease the brightness
|
||||
|
||||
#define MY_NTP_SERVER "de.pool.ntp.org"
|
||||
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
<div id="tab-config" class="">
|
||||
<form class="pure-form pure-form-aligned" action="/" method="post">
|
||||
<br>
|
||||
<label for="startup">
|
||||
<strong>Light initialization</strong>
|
||||
</label>
|
||||
<h3>Config</h3>
|
||||
<div class="pure-control-group">
|
||||
<label for="startup">
|
||||
<strong>Startup</strong>
|
||||
|
@ -25,9 +22,7 @@
|
|||
</select>
|
||||
</div>
|
||||
<br>
|
||||
<label for="startup">
|
||||
<strong>Wifi</strong>
|
||||
</label>
|
||||
<h3>Wifi</h3>
|
||||
<div class="pure-control-group">
|
||||
<label for="ip">SSID</label>
|
||||
<input id="ssid" name="ssid" type="text" value="{{WIFI_SSID}}">
|
||||
|
@ -37,9 +32,7 @@
|
|||
<input id="wpw" name="wpw" type="text" placeholder="1234password">
|
||||
</div>
|
||||
<br>
|
||||
<label for="startup">
|
||||
<strong>Network</strong>
|
||||
</label>
|
||||
<h3>Network</h3>
|
||||
<div class="pure-control-group">
|
||||
<label for="dip">
|
||||
<strong>Dynamic-IP</strong>
|
||||
|
|
63
firmware/data/config_template.html
Normal file
63
firmware/data/config_template.html
Normal file
|
@ -0,0 +1,63 @@
|
|||
<div id="tab-config" class="">
|
||||
<form class="pure-form pure-form-aligned" action="/" method="post">
|
||||
<h3>Config</h3>
|
||||
<div class="pure-control-group">
|
||||
<label for="startup">
|
||||
<strong>Startup</strong>
|
||||
</label>
|
||||
<select onchange="this.form.submit()" id="startup" name="startup">
|
||||
<option {{STARTUP_SELECTED_LS_0}} value="0">Last state</option>
|
||||
<option {{STARTUP_SELECTED_ON_1}} value="1">On</option>
|
||||
<option {{STARTUP_SELECTED_OFF_2}} value="2">Off</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="pure-control-group">
|
||||
<label for="scene">
|
||||
<strong>Scene</strong>
|
||||
</label>
|
||||
<select onchange="this.form.submit()" id="scene" name="scene">
|
||||
<option {{SCENE_SELECTED_RELAX_0}} value="0">Relax</option>
|
||||
<option {{SCENE_SELECTED_BRIGHT_1}} value="1">Bright</option>
|
||||
<option {{SCENE_SELECTED_NIGHT_2}} value="2">Night</option>
|
||||
</select>
|
||||
</div>
|
||||
<br>
|
||||
<h3>Wifi</h3>
|
||||
<div class="pure-control-group">
|
||||
<label for="ip">SSID</label>
|
||||
<input id="ssid" name="ssid" type="text" value="{{WIFI_SSID}}">
|
||||
</div>
|
||||
<div class="pure-control-group">
|
||||
<label for="wpw">Passphrase</label>
|
||||
<input id="wpw" name="wpw" type="text" placeholder="1234password">
|
||||
</div>
|
||||
<br>
|
||||
<h3>Network</h3>
|
||||
<div class="pure-control-group">
|
||||
<label for="dip">
|
||||
<strong>Dynamic-IP</strong>
|
||||
</label>
|
||||
<a class="pure-button {{DIP_LINK_ON_PRIMARY}}" href="/?dip=true">ON</a>
|
||||
<a class="pure-button {{DIP_LINK_OFF_PRIMARY}}" href="/?dip=false">OFF</a>
|
||||
</div>
|
||||
<div class="pure-control-group">
|
||||
<label for="ip">IP</label>
|
||||
<input id="ip" name="ip" type="text" value="{{WIFI_CFG_IP}}">
|
||||
</div>
|
||||
<div class="pure-control-group">
|
||||
<label for="gwip">Gateway IP</label>
|
||||
<input id="gwip" name="gwip" type="text" value="{{WIFI_CFG_GW}}">
|
||||
</div>
|
||||
<div class="pure-control-group">
|
||||
<label for="ip">Netmask</label>
|
||||
<input id="netmask" name="netmas" type="text" value="{{WIFI_CFG_NM}}">
|
||||
</div>
|
||||
<div class="pure-control-group">
|
||||
<label for="ip">DNS</label>
|
||||
<input id="netmask" name="dns" type="text" value="{{WIFI_CFG_DNS}}">
|
||||
</div>
|
||||
<div class="pure-controls">
|
||||
<button type="submit" class="pure-button pure-button-primary">Save</button>
|
||||
</div>
|
||||
</form>
|
||||
</div><!-- end of config div -->
|
|
@ -1,44 +1,25 @@
|
|||
<!-- bottom -->
|
||||
<script>
|
||||
var tabMain = document.getElementById("tab-lights");
|
||||
var tabMain = document.getElementById("tab-main");
|
||||
var tabConfig = document.getElementById("tab-config");
|
||||
var tabTDE = document.getElementById("tab-tde");
|
||||
var amain = document.getElementById("tab1-a");
|
||||
var acfg = document.getElementById("tab2-a");
|
||||
|
||||
var amain = document.getElementById("tab-a-lights");
|
||||
var acfg = document.getElementById("tab-a-config");
|
||||
var atde = document.getElementById("tab-a-tde");
|
||||
|
||||
|
||||
document.getElementById("tab-a-lights").addEventListener("click", function() {
|
||||
document.getElementById("tab1-a").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");
|
||||
});
|
||||
|
||||
document.getElementById("tab-a-config").addEventListener("click", function() {
|
||||
document.getElementById("tab2-a").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");
|
||||
});
|
||||
|
||||
document.getElementById("tab-a-tde").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");
|
||||
});
|
||||
|
||||
</script>
|
||||
<script>
|
||||
function loadGraphData() {
|
||||
console.log('----> generate graph <----');
|
||||
$.getJSON('/tc_data_blocks', function(data) {
|
||||
|
@ -207,43 +188,41 @@ pwmElementTxt.innerText = pwmValue.toString();
|
|||
}
|
||||
updatePWMValues();
|
||||
setInterval(updatePWMValues, 5000);
|
||||
</script>
|
||||
|
||||
|
||||
<script>
|
||||
// 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');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
|
233
firmware/data/index_template_bottom.html
Normal file
233
firmware/data/index_template_bottom.html
Normal file
|
@ -0,0 +1,233 @@
|
|||
<!-- bottom -->
|
||||
<script>
|
||||
var tabMain = document.getElementById("tab-main");
|
||||
var tabConfig = document.getElementById("tab-config");
|
||||
var amain = document.getElementById("tab1-a");
|
||||
var acfg = document.getElementById("tab2-a");
|
||||
|
||||
document.getElementById("tab1-a").addEventListener("click", function() {
|
||||
tabMain.classList.add("visible");
|
||||
tabConfig.classList.remove("visible");
|
||||
amain.classList.add("pure-button-primary");
|
||||
acfg.classList.remove("pure-button-primary");
|
||||
});
|
||||
|
||||
document.getElementById("tab2-a").addEventListener("click", function() {
|
||||
tabMain.classList.remove("visible");
|
||||
tabConfig.classList.add("visible");
|
||||
acfg.classList.add("pure-button-primary");
|
||||
amain.classList.remove("pure-button-primary");
|
||||
});
|
||||
</script>
|
||||
<script>
|
||||
function loadGraphData() {
|
||||
console.log('----> generate graph <----');
|
||||
$.getJSON('/tc_data_blocks', 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);
|
||||
</script>
|
||||
|
||||
<script>
|
||||
// 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', true);
|
||||
// Sende die Anfrage im Hintergrund
|
||||
xhr.send();
|
||||
updateLightState();
|
||||
});
|
||||
});
|
||||
// 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', true);
|
||||
// Sende die Anfrage im Hintergrund
|
||||
xhr.send();
|
||||
updateLightState();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</div>
|
||||
</fieldset>
|
||||
</body>
|
||||
</html>
|
|
@ -5,8 +5,4 @@
|
|||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div><!-- end of lights control tab -->
|
||||
|
||||
<div id="tab-tde"> <!-- timing editor -->
|
||||
Not implemented yet.
|
||||
</div>
|
||||
</div><!-- end of main-tab div -->
|
||||
|
|
8
firmware/data/index_template_middle.html
Normal file
8
firmware/data/index_template_middle.html
Normal file
|
@ -0,0 +1,8 @@
|
|||
<!-- middle -->
|
||||
</td>
|
||||
<td>
|
||||
<div id="plot_chart"></div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div><!-- end of main-tab div -->
|
|
@ -1,6 +1,7 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<style></style>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Light setup - {{LIGHT_NAME}}</title>
|
||||
|
@ -10,21 +11,15 @@
|
|||
<link rel="stylesheet" href="https://unpkg.com/purecss@0.6.2/build/pure-min.css">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>
|
||||
#tab-lights, #tab-config, #tab-tde {
|
||||
#tab-main, #tab-config {
|
||||
display: none;
|
||||
background-color: #ffffff;
|
||||
color: black;
|
||||
font-weight: bold;
|
||||
}
|
||||
#tab-lights.visible {
|
||||
#tab-main.visible {
|
||||
display: block;
|
||||
}
|
||||
#tab-config.visible {
|
||||
display: block;
|
||||
}
|
||||
#tab-tde.visible {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
@ -32,31 +27,32 @@ display: block;
|
|||
<h3>{{LIGHT_NAME}}</h3>
|
||||
<div class="pure-form pure-form-aligned">
|
||||
<div class="pure-controls">
|
||||
<a href="/update" class="pure-button">update</a>
|
||||
<a href="/?reset=1" class="pure-button">reset light</a>
|
||||
<a href="/?resettc" class="pure-button">reset timing control data</a>
|
||||
<a href="/?alert=1" class="pure-button">alert</a>
|
||||
<span class="pure-form-message">
|
||||
<a href="/?alert=1">alert</a>
|
||||
|
||||
<a href="/?reset=1">reset</a>
|
||||
|
||||
<a href="/?resettc">reset timing control data</a>
|
||||
|
||||
<a href="/update">update</a>
|
||||
</span>
|
||||
<label for="cb" class="pure-checkbox"></label>
|
||||
</div>
|
||||
<br>
|
||||
<div><!-- Tab links -->
|
||||
<a href="#" id="tab-a-lights" class="pure-button pure-button-primary">Lights control</a>
|
||||
<a href="#" id="tab-a-config" class="pure-button">Config</a>
|
||||
<a href="#" id="tab-a-tde" class="pure-button">Timing data edit</a>
|
||||
|
||||
<div>
|
||||
<a href="#" id="tab1-a" class="pure-button pure-button-primary">Main</a>
|
||||
<a href="#" id="tab2-a" class="pure-button">Config</a>
|
||||
</div>
|
||||
<br>
|
||||
<div id="tab-lights" class="visible"> <!-- lights control tab -->
|
||||
<br>
|
||||
|
||||
<div class="pure-control-group">
|
||||
<label for="tc_on">
|
||||
<strong>Timing control</strong>
|
||||
</label>
|
||||
<a id="tc_on" class="pure-button {{TC_LINK_PRIMARY_ON}}" href="#">ON</a>
|
||||
<a id="tc_off" class="pure-button {{TC_LINK_PRIMARY_OFF}}" href="#">OFF</a>
|
||||
<br>
|
||||
<label for="transition">Transition time (s)</label>
|
||||
<input id="transition" name="transition" type="text" placeholder="10" value="{{TRANSITION_TIME}}">
|
||||
</div>
|
||||
<br>
|
||||
<script>
|
||||
var links = document.querySelectorAll('[id^="tc_on"]');
|
||||
links.forEach(function(link) {
|
||||
|
@ -82,7 +78,15 @@ document.getElementById('tc_off').classList.add('pure-button-primary');
|
|||
document.getElementById('tc_on').classList.remove('pure-button-primary');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<br>
|
||||
<div class="pure-control-group">
|
||||
<label for="transition">Transition time (s)</label>
|
||||
<input id="transition" name="transition" type="text" placeholder="10" value="{{TRANSITION_TIME}}">
|
||||
</div>
|
||||
<br>
|
||||
|
||||
<script>
|
||||
let timeoutId;
|
||||
function sendSliderValue(x) {
|
||||
x = x - 1;
|
||||
|
@ -102,6 +106,7 @@ console.error(`Error sending slider value to ${url}: ${error}`);
|
|||
}
|
||||
</script>
|
||||
<br>
|
||||
<div id="tab-main" class="visible">
|
||||
<table border=0>
|
||||
<tr>
|
||||
<td>
|
||||
|
|
113
firmware/data/index_template_top.html
Normal file
113
firmware/data/index_template_top.html
Normal file
|
@ -0,0 +1,113 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<style></style>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Light setup - {{LIGHT_NAME}}</title>
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
|
||||
<link rel="stylesheet" href="https://unpkg.com/purecss@0.6.2/build/pure-min.css">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>
|
||||
#tab-main, #tab-config {
|
||||
display: none;
|
||||
}
|
||||
#tab-main.visible {
|
||||
display: block;
|
||||
}
|
||||
#tab-config.visible {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<fieldset>
|
||||
<h3>{{LIGHT_NAME}}</h3>
|
||||
<div class="pure-form pure-form-aligned">
|
||||
<div class="pure-controls">
|
||||
<span class="pure-form-message">
|
||||
<a href="/?alert=1">alert</a>
|
||||
|
||||
<a href="/?reset=1">reset</a>
|
||||
|
||||
<a href="/?resettc">reset timing control data</a>
|
||||
|
||||
<a href="/update">update</a>
|
||||
</span>
|
||||
<label for="cb" class="pure-checkbox"></label>
|
||||
</div>
|
||||
<br>
|
||||
|
||||
<div>
|
||||
<a href="#" id="tab1-a" class="pure-button pure-button-primary">Main</a>
|
||||
<a href="#" id="tab2-a" class="pure-button">Config</a>
|
||||
</div>
|
||||
<br>
|
||||
|
||||
<div class="pure-control-group">
|
||||
<label for="tc_on">
|
||||
<strong>Timing control</strong>
|
||||
</label>
|
||||
<a id="tc_on" class="pure-button {{TC_LINK_PRIMARY_ON}}" href="#">ON</a>
|
||||
<a id="tc_off" class="pure-button {{TC_LINK_PRIMARY_OFF}}" href="#">OFF</a>
|
||||
</div>
|
||||
<script>
|
||||
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');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<br>
|
||||
<div class="pure-control-group">
|
||||
<label for="transition">Transition time (s)</label>
|
||||
<input id="transition" name="transition" type="text" placeholder="10" value="{{TRANSITION_TIME}}">
|
||||
</div>
|
||||
<br>
|
||||
|
||||
<script>
|
||||
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);
|
||||
}
|
||||
</script>
|
||||
<br>
|
||||
<div id="tab-main" class="visible">
|
||||
<table border=0>
|
||||
<tr>
|
||||
<td>
|
||||
|
29
firmware/data/light_control_template.html
Normal file
29
firmware/data/light_control_template.html
Normal file
|
@ -0,0 +1,29 @@
|
|||
<h4>Light {{LIGHT_NUMBER}}</h4>
|
||||
<div class="pure-control-group">
|
||||
<label for="power">
|
||||
<strong>Power</strong>
|
||||
</label>
|
||||
<a id="on{{LIGHT_NUMBER_DEC}}_on" class="pure-button" href="#">ON</a>
|
||||
<a id="on{{LIGHT_NUMBER_DEC}}_off" class="pure-button" href="#">OFF</a>
|
||||
</div>
|
||||
<div class="pure-control-group">
|
||||
<label for="bri{{LIGHT_NUMBER_DEC}}">Bri</label>
|
||||
<input id="bri{{LIGHT_NUMBER_DEC}}" onchange="sendSliderValue({{LIGHT_NUMBER}})" name="bri{{LIGHT_NUMBER_DEC}}" type="range" min="0" max="255" value="25">
|
||||
|
||||
<span id="bri{{LIGHT_NUMBER_DEC}}_val" name="bri{{LIGHT_NUMBER_DEC}}">9</span>
|
||||
%
|
||||
<br>
|
||||
<label for="light{{LIGHT_NUMBER_DEC}}_pwm">PWM-Value</label>
|
||||
<input type="range" min="0" max="100" value="0" id="light{{LIGHT_NUMBER_DEC}}_pwm" disabled>
|
||||
|
||||
<span id="light{{LIGHT_NUMBER_DEC}}_pwm_txt"></span>
|
||||
%
|
||||
<script>
|
||||
var slider{{LIGHT_NUMBER_DEC}} = document.getElementById("bri{{LIGHT_NUMBER_DEC}}");
|
||||
var output{{LIGHT_NUMBER_DEC}} = document.getElementById("bri{{LIGHT_NUMBER_DEC}}_val");
|
||||
output{{LIGHT_NUMBER_DEC}}.innerHTML = (Math.round((slider{{LIGHT_NUMBER_DEC}}.value * 100.0 / 255.0) * 100) / 100).toFixed(2);
|
||||
slider{{LIGHT_NUMBER_DEC}}.oninput = function() {
|
||||
output{{LIGHT_NUMBER_DEC}}.innerHTML = (Math.round((this.value * 100.0 / 255.0) * 100) / 100).toFixed(2);
|
||||
}
|
||||
</script>
|
||||
</div>
|
|
@ -1,9 +1,6 @@
|
|||
<div id="tab-config" class="">
|
||||
<form class="pure-form pure-form-aligned" action="/" method="post">
|
||||
<br>
|
||||
<label for="startup">
|
||||
<strong>Light initialization</strong>
|
||||
</label>
|
||||
<h3>Config</h3>
|
||||
<div class="pure-control-group">
|
||||
<label for="startup">
|
||||
<strong>Startup</strong>
|
||||
|
@ -25,9 +22,7 @@
|
|||
</select>
|
||||
</div>
|
||||
<br>
|
||||
<label for="startup">
|
||||
<strong>Wifi</strong>
|
||||
</label>
|
||||
<h3>Wifi</h3>
|
||||
<div class="pure-control-group">
|
||||
<label for="ip">SSID</label>
|
||||
<input id="ssid" name="ssid" type="text" value="{{WIFI_SSID}}">
|
||||
|
@ -37,9 +32,7 @@
|
|||
<input id="wpw" name="wpw" type="text" placeholder="1234password">
|
||||
</div>
|
||||
<br>
|
||||
<label for="startup">
|
||||
<strong>Network</strong>
|
||||
</label>
|
||||
<h3>Network</h3>
|
||||
<div class="pure-control-group">
|
||||
<label for="dip">
|
||||
<strong>Dynamic-IP</strong>
|
233
firmware/data/org/index_template_bottom.html
Normal file
233
firmware/data/org/index_template_bottom.html
Normal file
|
@ -0,0 +1,233 @@
|
|||
<!-- bottom -->
|
||||
<script>
|
||||
var tabMain = document.getElementById("tab-main");
|
||||
var tabConfig = document.getElementById("tab-config");
|
||||
var amain = document.getElementById("tab1-a");
|
||||
var acfg = document.getElementById("tab2-a");
|
||||
|
||||
document.getElementById("tab1-a").addEventListener("click", function() {
|
||||
tabMain.classList.add("visible");
|
||||
tabConfig.classList.remove("visible");
|
||||
amain.classList.add("pure-button-primary");
|
||||
acfg.classList.remove("pure-button-primary");
|
||||
});
|
||||
|
||||
document.getElementById("tab2-a").addEventListener("click", function() {
|
||||
tabMain.classList.remove("visible");
|
||||
tabConfig.classList.add("visible");
|
||||
acfg.classList.add("pure-button-primary");
|
||||
amain.classList.remove("pure-button-primary");
|
||||
});
|
||||
</script>
|
||||
<script>
|
||||
function loadGraphData() {
|
||||
console.log('----> generate graph <----');
|
||||
$.getJSON('/tc_data_blocks', 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);
|
||||
</script>
|
||||
|
||||
<script>
|
||||
// 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();
|
||||
});
|
||||
});
|
||||
// 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();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</div>
|
||||
</fieldset>
|
||||
</body>
|
||||
</html>
|
|
@ -5,8 +5,4 @@
|
|||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div><!-- end of lights control tab -->
|
||||
|
||||
<div id="tab-tde"> <!-- timing editor -->
|
||||
Not implemented yet.
|
||||
</div>
|
||||
</div><!-- end of main-tab div -->
|
|
@ -1,6 +1,7 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<style></style>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Light setup - {{LIGHT_NAME}}</title>
|
||||
|
@ -8,55 +9,50 @@
|
|||
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
|
||||
<link rel="stylesheet" href="https://unpkg.com/purecss@0.6.2/build/pure-min.css">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>
|
||||
#tab-lights, #tab-config, #tab-tde {
|
||||
display: none;
|
||||
background-color: #ffffff;
|
||||
color: black;
|
||||
font-weight: bold;
|
||||
}
|
||||
#tab-lights.visible {
|
||||
display: block;
|
||||
}
|
||||
#tab-config.visible {
|
||||
display: block;
|
||||
}
|
||||
#tab-tde.visible {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>
|
||||
#tab-main, #tab-config {
|
||||
display: none;
|
||||
}
|
||||
#tab-main.visible {
|
||||
display: block;
|
||||
}
|
||||
#tab-config.visible {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<fieldset>
|
||||
<h3>{{LIGHT_NAME}}</h3>
|
||||
<div class="pure-form pure-form-aligned">
|
||||
<div class="pure-controls">
|
||||
<a href="/update" class="pure-button">update</a>
|
||||
<a href="/?reset=1" class="pure-button">reset light</a>
|
||||
<a href="/?resettc" class="pure-button">reset timing control data</a>
|
||||
<a href="/?alert=1" class="pure-button">alert</a>
|
||||
<span class="pure-form-message">
|
||||
<a href="/?alert=1">alert</a>
|
||||
|
||||
<a href="/?reset=1">reset</a>
|
||||
|
||||
<a href="/?resettc">reset timing control data</a>
|
||||
|
||||
<a href="/update">update</a>
|
||||
</span>
|
||||
<label for="cb" class="pure-checkbox"></label>
|
||||
</div>
|
||||
<br>
|
||||
<div><!-- Tab links -->
|
||||
<a href="#" id="tab-a-lights" class="pure-button pure-button-primary">Lights control</a>
|
||||
<a href="#" id="tab-a-config" class="pure-button">Config</a>
|
||||
<a href="#" id="tab-a-tde" class="pure-button">Timing data edit</a>
|
||||
|
||||
<div>
|
||||
<a href="#" id="tab1-a" class="pure-button pure-button-primary">Main</a>
|
||||
<a href="#" id="tab2-a" class="pure-button">Config</a>
|
||||
</div>
|
||||
<br>
|
||||
<div id="tab-lights" class="visible"> <!-- lights control tab -->
|
||||
<br>
|
||||
|
||||
<div class="pure-control-group">
|
||||
<label for="tc_on">
|
||||
<strong>Timing control</strong>
|
||||
</label>
|
||||
<a id="tc_on" class="pure-button {{TC_LINK_PRIMARY_ON}}" href="#">ON</a>
|
||||
<a id="tc_off" class="pure-button {{TC_LINK_PRIMARY_OFF}}" href="#">OFF</a>
|
||||
<br>
|
||||
<label for="transition">Transition time (s)</label>
|
||||
<input id="transition" name="transition" type="text" placeholder="10" value="{{TRANSITION_TIME}}">
|
||||
<label for="tc_on">
|
||||
<strong>Timing control</strong>
|
||||
</label>
|
||||
<a id="tc_on" class="pure-button {{TC_LINK_PRIMARY_ON}}" href="#">ON</a>
|
||||
<a id="tc_off" class="pure-button {{TC_LINK_PRIMARY_OFF}}" href="#">OFF</a>
|
||||
</div>
|
||||
<br>
|
||||
<script>
|
||||
var links = document.querySelectorAll('[id^="tc_on"]');
|
||||
links.forEach(function(link) {
|
||||
|
@ -82,7 +78,15 @@
|
|||
document.getElementById('tc_on').classList.remove('pure-button-primary');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<br>
|
||||
<div class="pure-control-group">
|
||||
<label for="transition">Transition time (s)</label>
|
||||
<input id="transition" name="transition" type="text" placeholder="10" value="{{TRANSITION_TIME}}">
|
||||
</div>
|
||||
<br>
|
||||
|
||||
<script>
|
||||
let timeoutId;
|
||||
function sendSliderValue(x) {
|
||||
x = x - 1;
|
||||
|
@ -102,6 +106,7 @@
|
|||
}
|
||||
</script>
|
||||
<br>
|
||||
<div id="tab-main" class="visible">
|
||||
<table border=0>
|
||||
<tr>
|
||||
<td>
|
|
@ -11,16 +11,7 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
//********* preprocessor block *********//
|
||||
|
||||
#ifdef DEVELOPMENT
|
||||
#define LIGHT_NAME "Dimmable Hue Light (DEV)"
|
||||
#else
|
||||
#define LIGHT_NAME LIGHT_NAME_STR
|
||||
#endif
|
||||
|
||||
//********* Config block *********//
|
||||
|
||||
// blue, warmwhite, purple, white&red&green
|
||||
// blau, schwarz, rot, weiß
|
||||
// ch1, ch2, ch3, ch4
|
||||
|
@ -279,8 +270,6 @@ void setup()
|
|||
Serial.flush();
|
||||
delay(1000);
|
||||
|
||||
//Serial.println("Flash size: " + (String)ESP.getFlashChipSize());
|
||||
|
||||
Dir dir = SPIFFS.openDir("/");
|
||||
Serial.println("\n\nSPIFFS directory content:");
|
||||
while (dir.next())
|
||||
|
@ -305,7 +294,7 @@ void setup()
|
|||
WiFi.mode(WIFI_STA);
|
||||
WiFiManager wifiManager;
|
||||
wifiManager.setConfigPortalTimeout(120);
|
||||
wifiManager.autoConnect(LIGHT_NAME);
|
||||
wifiManager.autoConnect(light_name);
|
||||
|
||||
IPAddress myIP = WiFi.localIP();
|
||||
Serial.print("IP: ");
|
||||
|
@ -460,7 +449,7 @@ void init_webserver()
|
|||
char macString[32] = { 0 };
|
||||
sprintf(macString, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
DynamicJsonDocument root(1024);
|
||||
root["name"] = LIGHT_NAME;
|
||||
root["name"] = light_name;
|
||||
root["lights"] = LIGHTS_COUNT;
|
||||
root["protocol"] = "native_multi";
|
||||
root["modelid"] = "LWB010";
|
||||
|
@ -728,7 +717,7 @@ String genLightControlHTML()
|
|||
String replacePlaceholder(String http_content)
|
||||
{
|
||||
|
||||
http_content.replace("{{LIGHT_NAME}}", (String)LIGHT_NAME);
|
||||
http_content.replace("{{LIGHT_NAME}}", (String)light_name);
|
||||
|
||||
http_content.replace("{{LIGHT_COUNT}}", (String)LIGHTS_COUNT);
|
||||
|
||||
|
@ -736,14 +725,12 @@ String replacePlaceholder(String http_content)
|
|||
if (tc_val == TIMING_CONTROL_ENABLED)
|
||||
{
|
||||
http_content.replace("{{TC_LINK_PRIMARY_ON}}", "pure-button-primary");
|
||||
|
||||
} else {
|
||||
http_content.replace("{{TC_LINK_PRIMARY_ON}}", "");
|
||||
}
|
||||
if (tc_val == TIMING_CONTROL_DISABLED)
|
||||
{
|
||||
http_content.replace("{{TC_LINK_PRIMARY_OFF}}", "pure-button-primary");
|
||||
|
||||
} else {
|
||||
http_content.replace("{{TC_LINK_PRIMARY_OFF}}", "");
|
||||
}
|
||||
|
|
|
@ -1,254 +0,0 @@
|
|||
<!-- bottom -->
|
||||
<script>
|
||||
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");
|
||||
|
||||
|
||||
document.getElementById("tab-a-lights").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");
|
||||
});
|
||||
|
||||
document.getElementById("tab-a-config").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");
|
||||
});
|
||||
|
||||
document.getElementById("tab-a-tde").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");
|
||||
});
|
||||
|
||||
function loadGraphData() {
|
||||
console.log('----> generate graph <----');
|
||||
$.getJSON('/tc_data_blocks', 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');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</div>
|
||||
</fieldset>
|
||||
</body>
|
||||
</html>
|
|
@ -1,4 +1,4 @@
|
|||
#!/bin/bash
|
||||
|
||||
cat $1 | sed -e"s/^ *//ig" | sed -e "s/^\/\/.*//ig"
|
||||
cat $1 | sed -e"s/^ *//ig"
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#!/bin/bash
|
||||
|
||||
bash ../../tools/html2string.sh ../html/index_template_top.html > index_template_top.html
|
||||
bash ../../tools/html2string.sh ../html/index_template_middle.html > index_template_middle.html
|
||||
bash ../../tools/html2string.sh ../html/index_template_bottom.html > index_template_bottom.html
|
||||
bash ../../tools/html2string.sh ../html/config_template.html > config_template.html
|
||||
bash ../../tools/html2string.sh ../html/light_control_template.html > light_control_template.html
|
||||
bash ../../tools/html2string.sh org/index_template_top.html > index_template_top.html
|
||||
bash ../../tools/html2string.sh org/index_template_middle.html > index_template_middle.html
|
||||
bash ../../tools/html2string.sh org/index_template_bottom.html > index_template_bottom.html
|
||||
bash ../../tools/html2string.sh org/config_template.html > config_template.html
|
||||
bash ../../tools/html2string.sh org/light_control_template.html > light_control_template.html
|
||||
|
|
Loading…
Reference in a new issue