diff --git a/src/patterns.py b/src/patterns.py deleted file mode 100644 index 58de83d..0000000 --- a/src/patterns.py +++ /dev/null @@ -1,291 +0,0 @@ -from machine import Pin -from neopixel import NeoPixel -import utime -import random - -class Patterns: - def __init__(self, pin, num_leds, color1=(0,0,0), color2=(0,0,0), brightness=127, selected="rainbow_cycle", delay=100): - self.n = NeoPixel(Pin(pin, Pin.OUT), num_leds) - self.num_leds = num_leds - self.pattern_step = 0 - self.last_update = utime.ticks_ms() - self.delay = delay - self.brightness = brightness - self.patterns = { - "off": self.off, - "on" : self.on, - "color_wipe": self.color_wipe_step, - "rainbow_cycle": self.rainbow_cycle_step, - "theater_chase": self.theater_chase_step, - "blink": self.blink_step, - "random_color_wipe": self.random_color_wipe_step, - "random_rainbow_cycle": self.random_rainbow_cycle_step, - "random_theater_chase": self.random_theater_chase_step, - "random_blink": self.random_blink_step, - "color_transition": self.color_transition_step, - "external": None - } - self.selected = selected - self.color1 = color1 - self.color2 = color2 - self.transition_duration = 50 # Duration of color transition in milliseconds - self.transition_step = 0 - - def sync(self): - self.pattern_step=0 - self.last_update = utime.ticks_ms() - - def tick(self): - if self.patterns[self.selected]: - self.patterns[self.selected]() - - def update_num_leds(self, pin, num_leds): - self.n = NeoPixel(Pin(pin, Pin.OUT), num_leds) - self.num_leds = num_leds - self.pattern_step = 0 - - def set_delay(self, delay): - self.delay = delay - - def set_brightness(self, brightness): - self.brightness = brightness - - def set_color1(self, color): - print(color) - self.color1 = self.apply_brightness(color) - - def set_color2(self, color): - self.color2 = self.apply_brightness(color) - - def apply_brightness(self, color): - return tuple(int(c * self.brightness / 255) for c in color) - - def select(self, pattern): - if pattern in self.patterns: - self.selected = pattern - return True - return False - - def set(self, i, color): - self.n[i] = color - - def write(self): - self.n.write() - - def fill(self): - for i in range(self.num_leds): - self.n[i] = self.color1 - self.n.write() - - def off(self): - color = self.color1 - self.color1 = (0,0,0) - self.fill() - self.color1 = color - - def on(self): - color = self.color1 - self.color1 = self.apply_brightness(self.color1) - self.fill() - self.color1 = color - - - def color_wipe_step(self): - color = self.apply_brightness(self.color1) - current_time = utime.ticks_ms() - if utime.ticks_diff(current_time, self.last_update) >= self.delay: - if self.pattern_step < self.num_leds: - for i in range(self.num_leds): - self.n[i] = (0, 0, 0) - self.n[self.pattern_step] = self.apply_brightness(color) - self.n.write() - self.pattern_step += 1 - else: - self.pattern_step = 0 - self.last_update = current_time - - def rainbow_cycle_step(self): - current_time = utime.ticks_ms() - if utime.ticks_diff(current_time, self.last_update) >= self.delay/5: - def wheel(pos): - if pos < 85: - return (pos * 3, 255 - pos * 3, 0) - elif pos < 170: - pos -= 85 - return (255 - pos * 3, 0, pos * 3) - else: - pos -= 170 - return (0, pos * 3, 255 - pos * 3) - - for i in range(self.num_leds): - rc_index = (i * 256 // self.num_leds) + self.pattern_step - self.n[i] = self.apply_brightness(wheel(rc_index & 255)) - self.n.write() - self.pattern_step = (self.pattern_step + 1) % 256 - self.last_update = current_time - - def theater_chase_step(self): - current_time = utime.ticks_ms() - if utime.ticks_diff(current_time, self.last_update) >= self.delay: - for i in range(self.num_leds): - if (i + self.pattern_step) % 3 == 0: - self.n[i] = self.apply_brightness(self.color1) - else: - self.n[i] = (0, 0, 0) - self.n.write() - self.pattern_step = (self.pattern_step + 1) % 3 - self.last_update = current_time - - def blink_step(self): - current_time = utime.ticks_ms() - if utime.ticks_diff(current_time, self.last_update) >= self.delay: - if self.pattern_step % 2 == 0: - for i in range(self.num_leds): - self.n[i] = self.apply_brightness(self.color1) - else: - for i in range(self.num_leds): - self.n[i] = (0, 0, 0) - self.n.write() - self.pattern_step = (self.pattern_step + 1) % 2 - self.last_update = current_time - - def random_color_wipe_step(self): - current_time = utime.ticks_ms() - if utime.ticks_diff(current_time, self.last_update) >= self.delay: - color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) - if self.pattern_step < self.num_leds: - for i in range(self.num_leds): - self.n[i] = (0, 0, 0) - self.n[self.pattern_step] = self.apply_brightness(color) - self.n.write() - self.pattern_step += 1 - else: - self.pattern_step = 0 - self.last_update = current_time - - def random_rainbow_cycle_step(self): - current_time = utime.ticks_ms() - if utime.ticks_diff(current_time, self.last_update) >= self.delay: - def wheel(pos): - if pos < 85: - return (pos * 3, 255 - pos * 3, 0) - elif pos < 170: - pos -= 85 - return (255 - pos * 3, 0, pos * 3) - else: - pos -= 170 - return (0, pos * 3, 255 - pos * 3) - - random_offset = random.randint(0, 255) - for i in range(self.num_leds): - rc_index = (i * 256 // self.num_leds) + self.pattern_step + random_offset - self.n[i] = self.apply_brightness(wheel(rc_index & 255)) - self.n.write() - self.pattern_step = (self.pattern_step + 1) % 256 - self.last_update = current_time - - def random_theater_chase_step(self): - current_time = utime.ticks_ms() - if utime.ticks_diff(current_time, self.last_update) >= self.delay: - color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) - for i in range(self.num_leds): - if (i + self.pattern_step) % 3 == 0: - self.n[i] = self.apply_brightness(color) - else: - self.n[i] = (0, 0, 0) - self.n.write() - self.pattern_step = (self.pattern_step + 1) % 3 - self.last_update = current_time - - def random_blink_step(self): - current_time = utime.ticks_ms() - if utime.ticks_diff(current_time, self.last_update) >= self.delay: - color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) - if self.pattern_step % 2 == 0: - for i in range(self.num_leds): - self.n[i] = self.apply_brightness(color) - else: - for i in range(self.num_leds): - self.n[i] = (0, 0, 0) - self.n.write() - self.pattern_step = (self.pattern_step + 1) % 2 - self.last_update = current_time - - def color_transition_step(self): - current_time = utime.ticks_ms() - if utime.ticks_diff(current_time, self.last_update) >= self.delay: - # Calculate transition factor based on elapsed time - transition_factor = (self.pattern_step * 100) / self.transition_duration - if transition_factor > 100: - transition_factor = 100 - color = self.interpolate_color(self.color1, self.color2, transition_factor / 100) - - # Apply the interpolated color to all LEDs - for i in range(self.num_leds): - self.n[i] = self.apply_brightness(color) - self.n.write() - - self.pattern_step += self.delay - if self.pattern_step > self.transition_duration: - self.pattern_step = 0 - - self.last_update = current_time - - def interpolate_color(self, color1, color2, factor): - return ( - int(color1[0] + (color2[0] - color1[0]) * factor), - int(color1[1] + (color2[1] - color1[1]) * factor), - int(color1[2] + (color2[2] - color1[2]) * factor) - ) - - def two_steps_forward_one_step_back_step(self): - current_time = utime.ticks_ms() - if utime.ticks_diff(current_time, self.last_update) >= self.delay: - # Move forward 2 steps and backward 1 step - if self.direction == 1: # Moving forward - if self.scanner_position < self.num_leds - 2: - self.scanner_position += 2 # Move forward 2 steps - else: - self.direction = -1 # Change direction to backward - else: # Moving backward - if self.scanner_position > 0: - self.scanner_position -= 1 # Move backward 1 step - else: - self.direction = 1 # Change direction to forward - - # Set all LEDs to off - for i in range(self.num_leds): - self.n[i] = (0, 0, 0) - - # Set the current position to the color - self.n[self.scanner_position] = self.apply_brightness(self.color1) - - # Apply the color transition - transition_factor = (self.pattern_step * 100) / self.transition_duration - if transition_factor > 100: - transition_factor = 100 - color = self.interpolate_color(self.color1, self.color2, transition_factor / 100) - self.n[self.scanner_position] = self.apply_brightness(color) - - self.n.write() - self.pattern_step += self.delay - if self.pattern_step > self.transition_duration: - self.pattern_step = 0 - - self.last_update = current_time - -if __name__ == "__main__": - p = Patterns(4, 180) - p.set_color1((255,0,0)) - p.set_color2((0,255,0)) - #p.set_delay(10) - try: - while True: - for key in p.patterns: - print(key) - p.select(key) - for _ in range(2000): - p.tick() - utime.sleep_ms(1) - except KeyboardInterrupt: - p.fill((0, 0, 0)) diff --git a/src/static/light-component.js b/src/static/light-component.js deleted file mode 100644 index 1f32642..0000000 --- a/src/static/light-component.js +++ /dev/null @@ -1,143 +0,0 @@ -import { getWebSocket } from "./websocket.js"; - -export class LightComponent extends HTMLElement { - constructor() { - super(); - - // Create a shadow DOM for encapsulation - const shadow = this.attachShadow({ mode: "open" }); - - // Create the content for the component - const style = document.createElement("style"); - style.textContent = ` - :host { - display: block; - width: 100px; - height: 100px; - background-color: #4caf50; - color: white; - text-align: center; - line-height: 100px; - cursor: grab; - position: absolute; - border-radius: 8px; - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); - } - - :host:active { - cursor: grabbing; - box-shadow: 0 8px 16px rgba(0, 0, 0, 0.3); - } - - .color-picker { - position: absolute; - top: 0; - right: 0; - width: 50px; - height: 50px; - cursor: pointer; - } - `; - - // Create the main content (draggable area) - const content = document.createElement("div"); - content.textContent = this.textContent || "Light Me Up!"; - content.style.position = "absolute"; - content.style.top = "0"; - content.style.left = "0"; - content.style.width = "100%"; - content.style.height = "100%"; - content.style.display = "flex"; - content.style.justifyContent = "center"; - content.style.alignItems = "center"; - - // Create the color picker - const colorPicker = document.createElement("input"); - colorPicker.type = "color"; - colorPicker.classList.add("color-picker"); - colorPicker.value = "#4caf50"; // Default color - colorPicker.addEventListener("input", () => { - this.style.backgroundColor = colorPicker.value; - this.dispatchEvent( - new CustomEvent("color-change", { - detail: { lightId: this.lightId, color: colorPicker.value }, - }), - ); - }); - - // Append the style, content, and color picker to the shadow DOM - shadow.appendChild(style); - shadow.appendChild(content); - shadow.appendChild(colorPicker); - - // Add event listeners for drag-and-drop - content.addEventListener("mousedown", this.handleMouseDown.bind(this)); - document.addEventListener("mousemove", this.handleMouseMove.bind(this)); - document.addEventListener("mouseup", this.handleMouseUp.bind(this)); - } - - // Track the initial mouse position and component position - handleMouseDown(event) { - event.preventDefault(); - - // Get the initial mouse position relative to the component - this.initialMouseX = event.clientX; - this.initialMouseY = event.clientY; - - // Get the initial position of the component - const rect = this.getBoundingClientRect(); - this.initialComponentX = rect.left; - this.initialComponentY = rect.top; - - // Add a class to indicate dragging - this.classList.add("dragging"); - } - - // Update the component's position as the mouse moves - handleMouseMove(event) { - if (!this.classList.contains("dragging")) return; - - // Calculate the new position of the component - const newX = this.initialComponentX + (event.clientX - this.initialMouseX); - const newY = this.initialComponentY + (event.clientY - this.initialMouseY); - - // Update the component's position - this.style.left = `${newX}px`; - this.style.top = `${newY}px`; - } - - // Stop dragging when the mouse is released - handleMouseUp() { - // Check if the component is being dragged - if (!this.classList.contains("dragging")) { - return; // Do nothing if not dragging - } - - // Remove the dragging class - this.classList.remove("dragging"); - - // Get the current position of the component - const rect = this.getBoundingClientRect(); - const newX = rect.left; - const newY = rect.top; - - // Dispatch an event to notify the parent about the updated position - this.dispatchEvent( - new CustomEvent("position-change", { - detail: { lightId: this.lightId, x: newX, y: newY }, - }), - ); - } - - // Add a property to hold the lightId - set lightId(id) { - this._lightId = id; - } - - get lightId() { - return this._lightId; - } -} - -// Define the custom element -customElements.define("light-component", LightComponent); diff --git a/src/static/light-components.js b/src/static/light-components.js deleted file mode 100644 index 14b5953..0000000 --- a/src/static/light-components.js +++ /dev/null @@ -1,90 +0,0 @@ -// light-components.js -import { LightComponent } from "./light-component.js"; -import { getWebSocket } from "./websocket.js"; - -// Map to store backend IDs and their corresponding components -const componentMap = new Map(); - -// Function to create and configure a light component -function createLightComponent(data, key, appContainer) { - const lightComponent = document.createElement("light-component"); - lightComponent.style.left = `${data.x}px`; // Set the x position - lightComponent.style.top = `${data.y}px`; // Set the y position - lightComponent.style.backgroundColor = data.settings?.color || "#4caf50"; // Set the background color - lightComponent.textContent = data.name || "Light Me Up!"; // Set the text content - - // Set the lightId property - lightComponent.lightId = key; // Use the backend ID as the lightId - - // Store the component in the map - componentMap.set(key, lightComponent); - - // Append the light component to the container - appContainer.appendChild(lightComponent); - - // Handle position change - lightComponent.addEventListener("position-change", (event) => { - const { lightId, x, y } = event.detail; - updatePositionOnServer(lightId, x, y); - }); - - // Handle color change - lightComponent.addEventListener("color-change", (event) => { - const { lightId, color } = event.detail; - sendColorToServer(lightId, color); - }); - - // Example: Add a click event listener to the light-component - lightComponent.addEventListener("click", () => { - console.log(`Light component clicked! ID: ${lightComponent.lightId}`); - }); -} - -// Function to create light components from the fetched data -export function createLightComponents(appContainer, lightData) { - for (const key in lightData) { - if (lightData.hasOwnProperty(key)) { - const light = lightData[key]; - createLightComponent(light, key, appContainer); // Pass the backend ID - } - } -} - -// Function to send the updated position to the server via a PATCH request -async function updatePositionOnServer(componentId, x, y) { - try { - const response = await fetch(`/light/${componentId}`, { - method: "PATCH", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ x, y }), - }); - - if (!response.ok) { - throw new Error(`HTTP error! Status: ${response.status}`); - } - - console.log( - `Updated position for component ${componentId}: x=${x}, y=${y}`, - ); - } catch (error) { - console.error("Error updating position on server:", error); - } -} - -// Function to send the selected color to the server via WebSocket -function sendColorToServer(componentId, color) { - const websocket = getWebSocket(); - const message = JSON.stringify({ - componentId, - color, - }); - - if (websocket.readyState === WebSocket.OPEN) { - websocket.send(message); - console.log("Sent color to server:", message); - } else { - console.warn("WebSocket is not open. Unable to send color."); - } -} diff --git a/src/static/main.js b/src/static/main.js index cdf56ce..6dce033 100644 --- a/src/static/main.js +++ b/src/static/main.js @@ -1,32 +1,81 @@ -// main.js -import { createLightComponents } from "./light-components.js"; -import { getWebSocket } from "./websocket.js"; +import "./rgb-slider.js"; -// Wait for the DOM to be fully loaded -document.addEventListener("DOMContentLoaded", async () => { - // Select the container where the light-components will be added - const appContainer = document.getElementById("app"); +const ws = new WebSocket("ws://localhost:8000/ws"); - // Fetch the JSON data from the /light endpoint - try { - const response = await fetch("/light"); - if (!response.ok) { - throw new Error(`HTTP error! Status: ${response.status}`); +ws.onopen = () => { + console.log("WebSocket connection established"); +}; + +ws.onclose = () => { + console.log("WebSocket connection closed"); +}; + +ws.onerror = (error) => { + console.error("WebSocket error:", error); +}; + +// Number of sliders (tabs) you want to create +const numTabs = 3; + +// Select the container for tabs and content +const tabsContainer = document.querySelector(".tabs"); +const tabContentContainer = document.querySelector(".tab-content"); + +// Create tabs dynamically +for (let i = 1; i <= numTabs; i++) { + // Create the tab button + const tabButton = document.createElement("button"); + tabButton.classList.add("tab"); + tabButton.id = `tab${i}`; + tabButton.textContent = `Tab ${i}`; + + // Add the tab button to the container + tabsContainer.appendChild(tabButton); + + // Create the corresponding tab content (RGB slider) + const tabContent = document.createElement("div"); + tabContent.classList.add("tab-pane"); + tabContent.id = `content${i}`; + const slider = document.createElement("rgb-slider"); + slider.id = i; + tabContent.appendChild(slider); + + // Add the tab content to the container + tabContentContainer.appendChild(tabContent); + + // Listen for color change on each RGB slider + slider.addEventListener("color-change", (e) => { + const { r, g, b } = e.detail; + console.log(`Color changed in tab ${i}:`, e.detail); + // Send RGB data to WebSocket server + if (ws.readyState === WebSocket.OPEN) { + const colorData = { r, g, b }; + ws.send(JSON.stringify(colorData)); } - const lightData = await response.json(); + }); +} - // Create and configure light components - createLightComponents(appContainer, lightData); +// Function to switch tabs +function switchTab(tabId) { + const tabs = document.querySelectorAll(".tab"); + const tabContents = document.querySelectorAll(".tab-pane"); - // Initialize WebSocket connection - const websocket = getWebSocket(); - websocket.addEventListener("open", () => { - console.log("WebSocket connection established."); - }); - websocket.addEventListener("message", (event) => { - console.log("Message from server:", event.data); - }); - } catch (error) { - console.error("Error fetching light data:", error); + tabs.forEach((tab) => tab.classList.remove("active")); + tabContents.forEach((content) => content.classList.remove("active")); + + // Activate the clicked tab and corresponding content + document.getElementById(tabId).classList.add("active"); + document + .getElementById("content" + tabId.replace("tab", "")) + .classList.add("active"); +} + +// Add event listeners to tabs +tabsContainer.addEventListener("click", (e) => { + if (e.target.classList.contains("tab")) { + switchTab(e.target.id); } }); + +// Initially set the first tab as active +switchTab("tab1"); diff --git a/src/static/styles.css b/src/static/styles.css index e238081..e3ecf1c 100644 --- a/src/static/styles.css +++ b/src/static/styles.css @@ -1,20 +1,37 @@ -/* Default styles for the light component */ -light-component { - display: block; - width: 100px; - height: 100px; - background-color: #4caf50; - color: white; - text-align: center; - line-height: 100px; - cursor: grab; - position: absolute; - border-radius: 8px; - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); +/* General tab styles */ +.tabs { + display: flex; + justify-content: center; + margin-bottom: 20px; } -/* Styles when the component is being dragged */ -light-component:active { - cursor: grabbing; - box-shadow: 0 8px 16px rgba(0, 0, 0, 0.3); +.tab { + padding: 10px 20px; + margin: 0 10px; + cursor: pointer; + background-color: #f1f1f1; + border: 1px solid #ccc; + border-radius: 4px; + transition: background-color 0.3s ease; +} + +.tab:hover { + background-color: #ddd; +} + +.tab.active { + background-color: #ccc; +} + +.tab-content { + display: flex; + justify-content: center; +} + +.tab-pane { + display: none; +} + +.tab-pane.active { + display: block; } diff --git a/src/static/websocket.js b/src/static/websocket.js deleted file mode 100644 index 10c8333..0000000 --- a/src/static/websocket.js +++ /dev/null @@ -1,26 +0,0 @@ -// websocket.js -let websocket = null; - -export function getWebSocket() { - if (!websocket) { - // Replace 'ws://your-server-url' with your WebSocket server URL - websocket = new WebSocket(`ws://${window.location.host}/ws`); - - // Handle WebSocket connection open - websocket.onopen = () => { - console.log("WebSocket connection established"); - }; - - // Handle WebSocket connection close - websocket.onclose = () => { - console.log("WebSocket connection closed"); - }; - - // Handle WebSocket errors - websocket.onerror = (error) => { - console.error("WebSocket error:", error); - }; - } - - return websocket; -} diff --git a/src/templates/index.html b/src/templates/index.html index 3e4bbc4..5431954 100644 --- a/src/templates/index.html +++ b/src/templates/index.html @@ -2,15 +2,13 @@
- -