This commit is contained in:
2025-05-05 22:19:34 +12:00
parent 28fa71b8ad
commit 2fa02086c9
10 changed files with 786 additions and 0 deletions

75
src/static/main.css Normal file
View File

@@ -0,0 +1,75 @@
body {
font-family: Arial, sans-serif;
max-width: 600px;
margin: 0 auto;
padding: 20px;
line-height: 1.6;
}
h1 {
text-align: center;
}
form {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 5px;
}
input[type="text"], input[type="submit"], input[type="range"], input[type="color"] {
width: 100%;
margin-bottom: 10px;
box-sizing: border-box;
}
input[type="range"] {
-webkit-appearance: none;
appearance: none;
height: 25px;
background: #d3d3d3;
outline: none;
opacity: 0.7;
transition: opacity .2s;
}
input[type="range"]:hover {
opacity: 1;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 25px;
height: 25px;
background: #4CAF50;
cursor: pointer;
border-radius: 50%;
}
input[type="range"]::-moz-range-thumb {
width: 25px;
height: 25px;
background: #4CAF50;
cursor: pointer;
border-radius: 50%;
}
#pattern_buttons {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-bottom: 20px;
}
#pattern_buttons button {
flex: 1 0 calc(33.333% - 10px);
padding: 10px;
background-color: #4CAF50;
color: white;
border: none;
cursor: pointer;
transition: background-color 0.3s;
}
#pattern_buttons button:hover {
background-color: #45a049;
}
@media (max-width: 480px) {
#pattern_buttons button {
flex: 1 0 calc(50% - 10px);
}
}

156
src/static/main.js Normal file
View File

@@ -0,0 +1,156 @@
let delayTimeout;
let brightnessTimeout;
let colorTimeout;
let color2Timeout;
let socket;
const host = window.location.host;
async function post(path, data) {
console.log(`POST to ${path}`, data);
try {
const response = await fetch(path, {
method: "POST",
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data) // Convert data to JSON string
});
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
} catch (error) {
console.error('Error during POST request:', error);
}
}
async function get(path) {
try {
const response = await fetch(path);
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return await response.json(); // Assuming you are expecting JSON response
} catch (error) {
console.error('Error during GET request:', error);
}
}
async function updateColor(event) {
event.preventDefault();
clearTimeout(colorTimeout);
colorTimeout = setTimeout(async function() {
const color = document.getElementById('color').value;
await post("/color", { color }); // Send as JSON
}, 500);
}
async function updateColor2(event) {
event.preventDefault();
clearTimeout(color2Timeout);
color2Timeout = setTimeout(async function() {
const color = document.getElementById('color2').value;
await post("/color2", { color }); // Send as JSON
}, 500);
}
async function updatePattern(pattern) {
await post("/pattern", { pattern }); // Send as JSON
//socket.send(JSON.stringify({"selected_pattern":pattern}))
}
async function updateBrightness(event) {
event.preventDefault();
clearTimeout(brightnessTimeout);
brightnessTimeout = setTimeout(async function() {
const brightness = document.getElementById('brightness').value;
//await post('/brightness', { brightness }); // Send as JSON
}, 500);
}
async function updateDelay(event) {
event.preventDefault();
clearTimeout(delayTimeout);
delayTimeout = setTimeout(async function() {
const delay = document.getElementById('delay').value;
await post('/delay', { delay }); // Send as JSON
}, 500);
}
async function updateNumLeds(event) {
event.preventDefault();
const numLeds = document.getElementById('num_leds').value;
await post('/num_leds', { num_leds: numLeds }); // Send as JSON
}
async function updateWifi(event) {
event.preventDefault();
const ssid = document.getElementById('ssid').value;
const password = document.getElementById('password').value;
const ip = document.getElementById('ip').value;
const gateway = document.getElementById('gateway').value;
const wifiSettings = { ssid, password, ip, gateway }; // Create JSON object
console.log(wifiSettings);
const response = await post('/wifi_settings', wifiSettings); // Send as JSON
if (response === 500) {
alert("Failed to connect to Wi-Fi");
}
}
function createPatternButtons(patterns) {
const container = document.getElementById('pattern_buttons');
container.innerHTML = ''; // Clear previous buttons
patterns.forEach(pattern => {
const button = document.createElement('button');
button.type = 'button'; // Use 'button' instead of 'submit'
button.textContent = pattern;
button.value = pattern;
button.addEventListener('click', async function(event) {
event.preventDefault();
await updatePattern(pattern);
});
container.appendChild(button);
});
}
document.addEventListener('DOMContentLoaded', async function() {
document.getElementById('color').addEventListener('input', updateColor);
document.getElementById('color2').addEventListener('input', updateColor2);
document.getElementById('delay').addEventListener('input', updateDelay);
document.getElementById('brightness').addEventListener('input', updateBrightness);
document.getElementById('num_leds_form').addEventListener('submit', updateNumLeds);
document.getElementById('wifi_form').addEventListener('submit', updateWifi);
document.getElementById('delay').addEventListener('touchend', updateDelay);
document.getElementById('brightness').addEventListener('touchend', updateBrightness);
document.querySelectorAll(".pattern_button").forEach(button => {
console.log(button.value);
button.addEventListener('click', async event => {
event.preventDefault();
await updatePattern(button.value);
});
});
socket = new WebSocket(`ws://${host}/settings`)
});
// Function to toggle the display of the settings menu
function selectSettings() {
const settingsMenu = document.getElementById('settings_menu');
controls = document.getElementById('controls');
settingsMenu.style.display = 'block';
controls.style.display = 'none';
}
function selectControls() {
const settingsMenu = document.getElementById('settings_menu');
controls = document.getElementById('controls');
settingsMenu.style.display = 'none';
controls.style.display = 'block';
}

195
src/static/rgb-slider.js Normal file
View File

@@ -0,0 +1,195 @@
// rgb-slider.js
export class RGBSlider extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: "open" });
shadow.innerHTML = `
<style>
.container {
display: flex;
flex-direction: column;
align-items: center;
padding: 1em;
border: 1px solid #ccc;
border-radius: 8px;
width: 50%;
font-family: sans-serif;
box-sizing: border-box;
}
.preview {
width: 50%;
height: 60px;
border-radius: 6px;
border: 1px solid #000;
background-color: rgb(0, 0, 0);
margin-bottom: 1em;
}
.sliders {
display: flex;
gap: 50px;
justify-content: center;
margin-bottom: 1em;
}
.slider-group {
display: flex;
flex-direction: column-reverse;
align-items: center;
}
.slider-group input[type="range"] {
writing-mode: vertical-lr;
direction: rtl;
width: 10px;
height: 120px;
}
.slider-group label {
margin-top: 8px;
font-size: 0.8em;
}
.rgb-inputs {
display: flex;
gap: 8px;
}
.rgb-inputs input {
width: 6ch;
padding: 2px;
font-family: monospace;
text-align: right;
}
.rgb-inputs label {
font-size: 0.8em;
text-align: center;
}
.rgb-input-group {
display: flex;
flex-direction: column;
align-items: center;
}
/* Mobile styles */
@media (max-width: 600px) {
.preview {
height: 80px;
}
.slider-group input[type="range"] {
height: 180px;
width: 14px;
}
.rgb-inputs input {
font-size: 1em;
padding: 4px;
width: 7ch;
}
.slider-group label,
.rgb-inputs label {
font-size: 1em;
}
.container {
padding: 1.5em;
}
}
</style>
<div class="container">
<div class="preview" id="preview"></div>
<div class="sliders">
<div class="slider-group">
<input type="range" min="0" max="255" value="0" id="r">
<label>R</label>
</div>
<div class="slider-group">
<input type="range" min="0" max="255" value="0" id="g">
<label>G</label>
</div>
<div class="slider-group">
<input type="range" min="0" max="255" value="0" id="b">
<label>B</label>
</div>
</div>
<div class="rgb-inputs">
<div class="rgb-input-group">
<label for="rInput">R</label>
<input type="number" min="0" max="255" id="rInput" value="0">
</div>
<div class="rgb-input-group">
<label for="gInput">G</label>
<input type="number" min="0" max="255" id="gInput" value="0">
</div>
<div class="rgb-input-group">
<label for="bInput">B</label>
<input type="number" min="0" max="255" id="bInput" value="0">
</div>
</div>
</div>
`;
const get = (id) => shadow.querySelector(id);
this.r = get("#r");
this.g = get("#g");
this.b = get("#b");
this.rInput = get("#rInput");
this.gInput = get("#gInput");
this.bInput = get("#bInput");
this.preview = get("#preview");
const updateColor = (r, g, b) => {
this.preview.style.backgroundColor = `rgb(${r}, ${g}, ${b})`;
this.rInput.value = r;
this.gInput.value = g;
this.bInput.value = b;
this.dispatchEvent(
new CustomEvent("color-change", {
detail: { r, g, b },
bubbles: true,
composed: true,
}),
);
};
const syncFromSliders = () => {
const r = +this.r.value;
const g = +this.g.value;
const b = +this.b.value;
updateColor(r, g, b);
};
const syncFromInputs = () => {
const r = Math.min(255, Math.max(0, +this.rInput.value));
const g = Math.min(255, Math.max(0, +this.gInput.value));
const b = Math.min(255, Math.max(0, +this.bInput.value));
this.r.value = r;
this.g.value = g;
this.b.value = b;
updateColor(r, g, b);
};
this.r.addEventListener("input", syncFromSliders);
this.g.addEventListener("input", syncFromSliders);
this.b.addEventListener("input", syncFromSliders);
this.rInput.addEventListener("change", syncFromInputs);
this.gInput.addEventListener("change", syncFromInputs);
this.bInput.addEventListener("change", syncFromInputs);
}
}
customElements.define("rgb-slider", RGBSlider);