Skip to content

Instantly share code, notes, and snippets.

@fgouin2014
Created July 9, 2024 20:14
Show Gist options
  • Save fgouin2014/f9eed88dd3fa931fc13e1526410623c0 to your computer and use it in GitHub Desktop.
Save fgouin2014/f9eed88dd3fa931fc13e1526410623c0 to your computer and use it in GitHub Desktop.
LightFullyWorking-Beta
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Multi-Bulb Smart Light Control</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jscolor/2.4.5/jscolor.min.js"></script>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1>Multi-Bulb Smart Light Control</h1>
<div id="masterControlSection" class="control-section">
<h2>Master Control</h2>
<div class="control-item">
<label class="switch">
<input type="checkbox" id="masterToggle">
<span class="slider"></span>
</label>
<svg class="bulb-icon" viewBox="0 0 24 24">
<path d="M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6C7.8 12.16 7 10.63 7 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z" fill="#000000" />
<path class="bulb-inner" d="M11 4c-2.76 0-5 2.24-5 5c0 1.63.8 3.16 2.15 4.1l.85.6V16h4v-2.3l.85-.6C15.2 12.16 16 10.63 16 9c0-2.76-2.24-5-5-5z" fill="#FFEB3B" />
</svg>
</div>
<div class="control-item">
<label for="masterBrightness">Master Brightness:</label>
<input type="range" id="masterBrightness" min="0" max="100" value="50">
<span id="masterBrightnessValue">50%</span>
</div>
<div class="control-item">
<label for="masterColor">Master Color:</label>
<input type="text" id="masterColor" data-jscolor="" value="FFFFFF">
</div>
<div class="color-presets" id="masterColorPresets"></div>
</div>
<div id="bulbControls"></div>
<script src="script.js"></script>
</body>
</html>
document.addEventListener("DOMContentLoaded", function () {
const bulbCount = 5;
const bulbControls = document.getElementById("bulbControls");
const masterToggle = document.getElementById("masterToggle");
const masterBrightness = document.getElementById("masterBrightness");
const masterBrightnessValue = document.getElementById(
"masterBrightnessValue"
);
const masterColor = document.getElementById("masterColor");
const masterColorPresets = document.getElementById("masterColorPresets");
const masterBulbIcon = document.querySelector(
"#masterControlSection .bulb-icon"
);
const colorPresets = {
"White Tones": [
{
name: "Cool White",
color: "F0F8FF"
},
{
name: "Daylight",
color: "F5F5F5"
},
{
name: "Neutral White",
color: "F5DEB3"
},
{
name: "Warm White",
color: "FFD700"
},
{
name: "Candlelight",
color: "FFA07A"
}
],
"Basic Colors": [
{
name: "Red",
color: "FF0000"
},
{
name: "Green",
color: "00FF00"
},
{
name: "Blue",
color: "0000FF"
},
{
name: "Yellow",
color: "FFFF00"
},
{
name: "Magenta",
color: "FF00FF"
}
],
"Pastel Colors": [
{
name: "Pastel Pink",
color: "FFB6C1"
},
{
name: "Pastel Blue",
color: "ADD8E6"
},
{
name: "Pastel Green",
color: "98FB98"
},
{
name: "Pastel Yellow",
color: "FFFACD"
},
{
name: "Pastel Lavender",
color: "E6E6FA"
}
]
};
function createColorPresets(container, colorPickerId) {
Object.entries(colorPresets).forEach(([category, presets]) => {
const categoryDiv = document.createElement("div");
categoryDiv.className = "preset-category";
const labelDiv = document.createElement("div");
labelDiv.className = "preset-label";
labelDiv.textContent = category;
categoryDiv.appendChild(labelDiv);
const presetRow = document.createElement("div");
presetRow.className = "preset-row";
presets.forEach((preset) =>
createPresetButton(preset, presetRow, colorPickerId)
);
categoryDiv.appendChild(presetRow);
container.appendChild(categoryDiv);
});
}
function createPresetButton(preset, container, colorPickerId) {
const presetElement = document.createElement("div");
presetElement.className = "color-preset";
presetElement.style.backgroundColor = `#${preset.color}`;
presetElement.title = preset.name;
presetElement.addEventListener("click", () => {
const colorPicker = document.getElementById(colorPickerId);
if (colorPicker.jscolor) {
colorPicker.jscolor.fromString(preset.color);
updateBulbColor(colorPickerId, preset.color);
}
});
container.appendChild(presetElement);
}
function updateBulbColor(colorPickerId, color) {
const bulbId = colorPickerId.replace("color", "");
console.log(`Set bulb ${bulbId} color to #${color}`);
if (colorPickerId === "masterColor") {
syncMasterColor(color);
}
saveSettings();
}
function createBulbControl(id) {
const bulbDiv = document.createElement("div");
bulbDiv.className = "bulb-control control-section";
bulbDiv.innerHTML = `
<h2>Bulb ${id}</h2>
<div class="control-item">
<label class="switch">
<input type="checkbox" class="bulbToggle" data-id="${id}">
<span class="slider"></span>
</label>
<svg class="bulb-icon" viewBox="0 0 24 24">
<path d="M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6C7.8 12.16 7 10.63 7 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z" fill="#000000"/>
<path class="bulb-inner" d="M11 4c-2.76 0-5 2.24-5 5c0 1.63.8 3.16 2.15 4.1l.85.6V16h4v-2.3l.85-.6C15.2 12.16 16 10.63 16 9c0-2.76-2.24-5-5-5z" fill="#FFEB3B"/>
</svg>
</div>
<div class="control-item">
<label for="brightness${id}">Brightness:</label>
<input type="range" id="brightness${id}" class="brightnessSlider" data-id="${id}" min="0" max="100" value="50">
<span class="brightnessValue" data-id="${id}">50%</span>
</div>
<div class="control-item">
<label for="color${id}">Color:</label>
<input type="text" id="color${id}" class="colorPicker" data-jscolor="" data-id="${id}" value="FFFFFF">
</div>
<div class="color-presets" id="colorPresets${id}"></div>
`;
bulbControls.appendChild(bulbDiv);
createColorPresets(
bulbDiv.querySelector(`#colorPresets${id}`),
`color${id}`
);
}
// Create controls for each bulb
for (let i = 1; i <= bulbCount; i++) {
createBulbControl(i);
}
// Initialize jscolor for all color pickers
jscolor.install();
// Create color presets for master control
createColorPresets(masterColorPresets, "masterColor");
// Add event listeners for individual bulb controls
document.querySelectorAll(".bulbToggle").forEach((toggle) => {
toggle.addEventListener("change", (e) => {
const id = e.target.dataset.id;
const isOn = e.target.checked;
const bulbIcon = e.target
.closest(".control-item")
.querySelector(".bulb-icon");
console.log(`Toggle bulb ${id} ${isOn ? "on" : "off"}`);
updateBulbVisualState(id, isOn);
updateMasterToggle();
saveSettings();
});
});
document.querySelectorAll(".brightnessSlider").forEach((slider) => {
slider.addEventListener("input", (e) => {
const id = e.target.dataset.id;
const brightness = e.target.value;
document.querySelector(
`.brightnessValue[data-id="${id}"]`
).textContent = `${brightness}%`;
console.log(`Set bulb ${id} brightness to ${brightness}%`);
saveSettings();
});
});
document.querySelectorAll(".colorPicker").forEach((picker) => {
picker.addEventListener("change", (e) => {
const id = e.target.dataset.id;
const color = e.target.jscolor.toString();
console.log(`Set bulb ${id} color to #${color}`);
const bulbInner = document.querySelector(
`.bulb-control:nth-child(${id}) .bulb-inner`
);
if (bulbInner) {
bulbInner.style.fill = `#${color}`;
}
saveSettings();
});
});
// Add event listeners for master controls
masterToggle.addEventListener("change", (e) => {
const isOn = e.target.checked;
console.log(`Master toggle ${isOn ? "on" : "off"}`);
updateAllBulbsVisualState(isOn);
masterBulbIcon.classList.toggle("on", isOn);
masterBulbIcon.classList.toggle("off", !isOn);
saveSettings();
});
masterBrightness.addEventListener("input", (e) => {
const brightness = e.target.value;
masterBrightnessValue.textContent = `${brightness}%`;
console.log(`Set all bulbs brightness to ${brightness}%`);
syncMasterBrightness(brightness);
saveSettings();
});
masterColor.addEventListener("change", (e) => {
const color = e.target.jscolor.toString();
console.log(`Set all bulbs color to #${color}`);
syncMasterColor(color);
saveSettings();
});
// Helper functions for synchronization
function updateMasterToggle() {
const anyBulbOn = Array.from(document.querySelectorAll(".bulbToggle")).some(
(toggle) => toggle.checked
);
masterToggle.checked = anyBulbOn;
masterBulbIcon.classList.toggle("on", anyBulbOn);
masterBulbIcon.classList.toggle("off", !anyBulbOn);
}
function syncMasterBrightness(brightness) {
document.querySelectorAll(".brightnessSlider").forEach((slider) => {
slider.value = brightness;
const id = slider.dataset.id;
document.querySelector(
`.brightnessValue[data-id="${id}"]`
).textContent = `${brightness}%`;
});
}
function syncMasterColor(color) {
document.querySelectorAll(".colorPicker").forEach((picker) => {
if (picker.jscolor) {
picker.jscolor.fromString(color);
}
const id = picker.dataset.id;
const bulbInner = document.querySelector(
`.bulb-control:nth-child(${id}) .bulb-inner`
);
if (bulbInner) {
bulbInner.style.fill = `#${color}`;
}
});
}
function updateBulbVisualState(id, isOn) {
const bulbToggle = document.querySelector(`.bulbToggle[data-id="${id}"]`);
const bulbIcon = bulbToggle
.closest(".control-item")
.querySelector(".bulb-icon");
const switchLabel = bulbToggle.closest(".switch");
if (masterToggle.checked) {
bulbIcon.classList.toggle("on", isOn);
bulbIcon.classList.toggle("off", !isOn);
switchLabel.classList.remove("disabled");
} else {
bulbIcon.classList.remove("on");
bulbIcon.classList.add("off");
switchLabel.classList.add("disabled");
}
}
function updateAllBulbsVisualState(masterIsOn) {
document.querySelectorAll(".bulbToggle").forEach((toggle) => {
const id = toggle.dataset.id;
updateBulbVisualState(id, masterIsOn && toggle.checked);
});
}
// Save settings to localStorage
function saveSettings() {
const settings = {
masterToggle: masterToggle.checked,
masterBrightness: masterBrightness.value,
masterColor: masterColor.value,
bulbs: {}
};
document.querySelectorAll(".bulb-control").forEach((bulb, index) => {
const id = index + 1;
settings.bulbs[id] = {
isOn: bulb.querySelector(".bulbToggle").checked,
brightness: bulb.querySelector(".brightnessSlider").value,
color: bulb.querySelector(".colorPicker").value
};
});
localStorage.setItem("bulbSettings", JSON.stringify(settings));
}
// Load settings from localStorage
function loadSettings() {
const settings = JSON.parse(localStorage.getItem("bulbSettings"));
if (settings) {
masterToggle.checked = settings.masterToggle;
masterBrightness.value = settings.masterBrightness;
masterBrightnessValue.textContent = `${settings.masterBrightness}%`;
masterColor.jscolor.fromString(settings.masterColor);
Object.entries(settings.bulbs).forEach(([id, bulbSettings]) => {
const bulbControl = document.querySelector(
`.bulb-control:nth-child(${id})`
);
if (bulbControl) {
const bulbToggle = bulbControl.querySelector(".bulbToggle");
bulbToggle.checked = bulbSettings.isOn;
updateBulbVisualState(id, settings.masterToggle && bulbSettings.isOn);
const brightnessSlider = bulbControl.querySelector(
".brightnessSlider"
);
brightnessSlider.value = bulbSettings.brightness;
bulbControl.querySelector(
".brightnessValue"
).textContent = `${bulbSettings.brightness}%`;
const colorPicker = bulbControl.querySelector(".colorPicker");
colorPicker.jscolor.fromString(bulbSettings.color);
const bulbInner = bulbControl.querySelector(".bulb-inner");
if (bulbInner) {
bulbInner.style.fill = `#${bulbSettings.color}`;
}
}
});
updateMasterToggle();
updateAllBulbsVisualState(settings.masterToggle);
}
}
// Load settings when the page loads
loadSettings();
});
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
background-color: #f0f0f0;
}
h1, h2 {
text-align: center;
color: #333;
}
.control-section {
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
padding: 20px;
margin-bottom: 20px;
}
.bulb-control {
border-top: 1px solid #eee;
padding-top: 15px;
margin-top: 15px;
}
.control-item {
display: grid;
grid-template-columns: auto 1fr auto;
align-items: start;
gap: 10px;
margin-bottom: 15px;
}
.control-item label {
grid-column: 1 / 2;
}
.control-item input[type="range"],
.control-item input[type="text"] {
grid-column: 2 / 3;
}
.control-item .brightnessValue {
grid-column: 3 / 4;
}
.color-presets {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 10px;
}
.preset-category {
flex-basis: 100%;
}
.preset-label {
font-weight: bold;
margin-bottom: 5px;
color: #555;
}
.preset-row {
display: flex;
gap: 5px;
}
.color-preset {
width: 25px;
height: 25px;
border-radius: 50%;
cursor: pointer;
transition: transform 0.2s;
}
.color-preset:hover {
transform: scale(1.1);
}
.switch {
position: relative;
display: inline-block;
width: 24px;
height: 50px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
transition: .4s;
border-radius: 24px;
}
.slider:before {
position: absolute;
content: "";
height: 18px;
width: 18px;
left: 3px;
bottom: 3px;
background-color: white;
transition: .4s;
border-radius: 50%;
}
input:checked + .slider {
background-color: #2196F3;
}
input:checked + .slider:before {
transform: translateY(-26px);
}
.switch.disabled .slider {
background-color: #ddd;
cursor: not-allowed;
}
.switch.disabled input:checked + .slider {
background-color: #999;
}
.bulb-icon {
width: 24px;
height: 24px;
transition: all 0.3s ease;
margin-top: 13px;
position: relative;
z-index: 1;
}
.bulb-icon .bulb-inner {
transition: fill 0.3s ease;
}
.bulb-icon.on .bulb-inner {
filter: drop-shadow(0 0 5px currentColor);
}
.bulb-icon.off .bulb-inner {
fill: #D3D3D3;
}
input[type="range"] {
-webkit-appearance: none;
width: 100%;
height: 15px;
border-radius: 5px;
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;
border-radius: 50%;
background: #2196F3;
cursor: pointer;
}
input[type="range"]::-moz-range-thumb {
width: 25px;
height: 25px;
border-radius: 50%;
background: #2196F3;
cursor: pointer;
}
</style>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment