Updated to a workable interface right now.
This commit is contained in:
parent
c27e3d661d
commit
ebb25aee9c
@ -3,6 +3,7 @@ import asyncio
|
|||||||
from bleak import BleakScanner, BleakClient
|
from bleak import BleakScanner, BleakClient
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
|
import json
|
||||||
|
|
||||||
# =================================================================================================
|
# =================================================================================================
|
||||||
# APP CONFIGURATION
|
# APP CONFIGURATION
|
||||||
@ -10,7 +11,7 @@ import time
|
|||||||
|
|
||||||
# Set to True to run without a physical BLE device for testing purposes.
|
# Set to True to run without a physical BLE device for testing purposes.
|
||||||
# Set to False to connect to the actual lamp matrix.
|
# Set to False to connect to the actual lamp matrix.
|
||||||
DEBUG_MODE = True
|
DEBUG_MODE = False
|
||||||
|
|
||||||
# --- BLE Device Configuration (Ignored in DEBUG_MODE) ---
|
# --- BLE Device Configuration (Ignored in DEBUG_MODE) ---
|
||||||
DEVICE_NAME = "Pupilometer LED Billboard"
|
DEVICE_NAME = "Pupilometer LED Billboard"
|
||||||
@ -33,8 +34,11 @@ def create_spiral_map(n=5):
|
|||||||
r, c = n // 2, n // 2
|
r, c = n // 2, n // 2
|
||||||
address = 0
|
address = 0
|
||||||
spiral_map[r][c] = address
|
spiral_map[r][c] = address
|
||||||
dr = [0, 1, 0, -1]
|
|
||||||
dc = [1, 0, -1, 0]
|
# Updated directions to start moving UP first instead of right
|
||||||
|
dr = [-1, 0, 1, 0] # Change in row: Up, Right, Down, Left
|
||||||
|
dc = [0, 1, 0, -1] # Change in col: Up, Right, Down, Left
|
||||||
|
|
||||||
direction = 0
|
direction = 0
|
||||||
segment_length = 1
|
segment_length = 1
|
||||||
steps = 0
|
steps = 0
|
||||||
@ -51,6 +55,7 @@ def create_spiral_map(n=5):
|
|||||||
segment_length += 1
|
segment_length += 1
|
||||||
return spiral_map
|
return spiral_map
|
||||||
|
|
||||||
|
|
||||||
def get_spiral_address(row, col, spiral_map):
|
def get_spiral_address(row, col, spiral_map):
|
||||||
n = len(spiral_map)
|
n = len(spiral_map)
|
||||||
if 0 <= row < n and 0 <= col < n:
|
if 0 <= row < n and 0 <= col < n:
|
||||||
@ -60,7 +65,7 @@ def get_spiral_address(row, col, spiral_map):
|
|||||||
|
|
||||||
SPIRAL_MAP_5x5 = create_spiral_map(5)
|
SPIRAL_MAP_5x5 = create_spiral_map(5)
|
||||||
|
|
||||||
async def set_lamp_colors_on_ble(lamps_to_update, new_color):
|
async def set_full_matrix_on_ble(full_matrix):
|
||||||
global ble_client
|
global ble_client
|
||||||
global ble_characteristics
|
global ble_characteristics
|
||||||
|
|
||||||
@ -70,32 +75,55 @@ async def set_lamp_colors_on_ble(lamps_to_update, new_color):
|
|||||||
if not ble_client or not ble_client.is_connected:
|
if not ble_client or not ble_client.is_connected:
|
||||||
print("Failed to reconnect to BLE client.")
|
print("Failed to reconnect to BLE client.")
|
||||||
return
|
return
|
||||||
|
else:
|
||||||
|
print("Confirmed BLE connection status. Proceeding with lamp update.")
|
||||||
|
|
||||||
# Create a full matrix of colors to send
|
print("Initializing blank canvas...")
|
||||||
serial_colors = [b'\x00\x00\x00'] * lampAmount
|
serial_colors = [b'\x00\x00\x00'] * lampAmount
|
||||||
center_lamp_color = b'\x00\x00\x00'
|
print(f"Initialized: {serial_colors}")
|
||||||
center_pos = get_spiral_address(2, 2, SPIRAL_MAP_5x5)
|
|
||||||
|
|
||||||
# Note: A real implementation would query the device for the center lamp's current color
|
for row in range(5):
|
||||||
# to maintain persistence. For simplicity in this example, we'll assume it's set to black initially.
|
for col in range(5):
|
||||||
# We will update this logic later if needed.
|
|
||||||
|
|
||||||
# Apply all other lamps to black
|
lamp_data = full_matrix[row][col]
|
||||||
for char_index in range(lampAmount):
|
print(f"Construcing lamp {row},{col} data: {lamp_data}")
|
||||||
if char_index != center_pos:
|
ww = int(lamp_data['ww'])
|
||||||
serial_colors[char_index] = b'\x00\x00\x00'
|
cw = int(lamp_data['cw'])
|
||||||
|
blue = int(lamp_data['blue'])
|
||||||
|
|
||||||
# Apply the new color to the selected lamps
|
color_bytes = bytes([ww, cw, blue])
|
||||||
for lamp in lamps_to_update:
|
spiral_pos = get_spiral_address(row, col, SPIRAL_MAP_5x5)
|
||||||
spiral_pos = get_spiral_address(lamp['row'], lamp['col'], SPIRAL_MAP_5x5)
|
print(f"Constructed data for {spiral_pos}: {color_bytes}")
|
||||||
if spiral_pos != -1:
|
if spiral_pos != -1:
|
||||||
serial_colors[spiral_pos] = new_color
|
serial_colors[spiral_pos] = color_bytes
|
||||||
|
|
||||||
|
# =====================================================================
|
||||||
|
# SNIPPET TO PATCH SWAPPED LAMP POSITIONS
|
||||||
|
# =====================================================================
|
||||||
|
print("Patching lamp positions 3 <-> 7 and 12 <-> 24.")
|
||||||
|
|
||||||
|
# Swap data for lamps at positions 3 and 7
|
||||||
|
temp_color_3 = serial_colors[3]
|
||||||
|
serial_colors[3] = serial_colors[7]
|
||||||
|
serial_colors[7] = temp_color_3
|
||||||
|
|
||||||
|
# Swap data for lamps at positions 12 and 24
|
||||||
|
temp_color_12 = serial_colors[12]
|
||||||
|
serial_colors[12] = serial_colors[24]
|
||||||
|
serial_colors[24] = temp_color_12
|
||||||
|
# =====================================================================
|
||||||
|
|
||||||
|
# Ensure all characteristics are available before writing
|
||||||
|
if len(ble_characteristics) != lampAmount:
|
||||||
|
print(f"Mismatch in lamp amount. Expected {lampAmount}, got {len(ble_characteristics)}.")
|
||||||
|
return
|
||||||
|
print(f"Constructed the following matrix data: {serial_colors}")
|
||||||
# Write each byte string to its corresponding characteristic
|
# Write each byte string to its corresponding characteristic
|
||||||
for i, char in enumerate(ble_characteristics):
|
for i, char in enumerate(ble_characteristics):
|
||||||
value_to_write = serial_colors[i]
|
value_to_write = serial_colors[i]
|
||||||
|
print(f"Setting Lamp {i} ({char.uuid}) to {value_to_write.hex()}")
|
||||||
await ble_client.write_gatt_char(char.uuid, value_to_write)
|
await ble_client.write_gatt_char(char.uuid, value_to_write)
|
||||||
print(f"Setting Lamp {i} to {value_to_write.hex()}")
|
|
||||||
|
|
||||||
async def connect_to_ble_device():
|
async def connect_to_ble_device():
|
||||||
global ble_client
|
global ble_client
|
||||||
@ -116,11 +144,12 @@ async def connect_to_ble_device():
|
|||||||
await ble_client.connect()
|
await ble_client.connect()
|
||||||
if ble_client.is_connected:
|
if ble_client.is_connected:
|
||||||
print(f"Connected to {target_device.name}")
|
print(f"Connected to {target_device.name}")
|
||||||
services = await ble_client.get_services()
|
services = ble_client.services
|
||||||
ble_characteristics = sorted([
|
# The previous logic for filtering services seems incorrect; let's grab all characteristics
|
||||||
|
characteristics = [
|
||||||
char for service in services for char in service.characteristics
|
char for service in services for char in service.characteristics
|
||||||
], key=lambda char: char.handle)[:lampAmount]
|
]
|
||||||
|
ble_characteristics = sorted(characteristics, key=lambda char: char.handle)
|
||||||
print(f"Found {len(ble_characteristics)} characteristics for lamps.")
|
print(f"Found {len(ble_characteristics)} characteristics for lamps.")
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
@ -145,60 +174,49 @@ def index():
|
|||||||
return render_template('index.html', matrix=lamp_matrix)
|
return render_template('index.html', matrix=lamp_matrix)
|
||||||
else:
|
else:
|
||||||
# In live mode, we'll pass a default black matrix.
|
# In live mode, we'll pass a default black matrix.
|
||||||
# The true state is on the device.
|
|
||||||
initial_matrix = [['#000000' for _ in range(5)] for _ in range(5)]
|
initial_matrix = [['#000000' for _ in range(5)] for _ in range(5)]
|
||||||
return render_template('index.html', matrix=initial_matrix)
|
return render_template('index.html', matrix=initial_matrix)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/set_color', methods=['POST'])
|
@app.route('/set_matrix', methods=['POST'])
|
||||||
# Updated part of your app.py
|
def set_matrix():
|
||||||
@app.route('/set_color', methods=['POST'])
|
|
||||||
def set_color():
|
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
lamps_to_update = data.get('lamps', [])
|
full_matrix = data.get('matrix', [])
|
||||||
r = data.get('ww')
|
|
||||||
g = data.get('cw')
|
|
||||||
b = data.get('blue')
|
|
||||||
|
|
||||||
if not lamps_to_update:
|
if not full_matrix or len(full_matrix) != 5 or len(full_matrix[0]) != 5:
|
||||||
return jsonify(success=False, message="No lamps selected")
|
return jsonify(success=False, message="Invalid matrix data received"), 400
|
||||||
|
else:
|
||||||
|
print(f"Received the following matrix data: {full_matrix}")
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
r, g, b = int(r), int(g), int(b)
|
|
||||||
new_color_hex = f'#{r:02x}{g:02x}{b:02x}'
|
|
||||||
|
|
||||||
if DEBUG_MODE:
|
if DEBUG_MODE:
|
||||||
# === DEBUG MODE: Update in-memory matrix ===
|
# === DEBUG MODE: Update in-memory matrix ===
|
||||||
center_row, center_col = 2, 2
|
|
||||||
center_lamp_color = lamp_matrix[center_row][center_col]
|
|
||||||
|
|
||||||
# First, turn all non-center lamps black
|
|
||||||
for row in range(5):
|
for row in range(5):
|
||||||
for col in range(5):
|
for col in range(5):
|
||||||
if (row, col) != (center_row, center_col):
|
lamp_data = full_matrix[row][col]
|
||||||
lamp_matrix[row][col] = '#000000'
|
ww = lamp_data['ww']
|
||||||
|
cw = lamp_data['cw']
|
||||||
# Apply the new color to the selected lamps
|
blue = lamp_data['blue']
|
||||||
for lamp in lamps_to_update:
|
|
||||||
lamp_matrix[lamp['row']][lamp['col']] = new_color_hex
|
# Convert ww, cw, blue to a hex color for UI display
|
||||||
|
r = min(255, ww + cw)
|
||||||
# The center lamp is handled by its own controls, so it remains persistent
|
g = min(255, ww + cw + blue)
|
||||||
# unless it's part of a region update. We re-apply its color here.
|
b = min(255, blue + cw)
|
||||||
# No, we don't. The logic is that it gets reset unless it's selected.
|
new_color_hex = f'#{r:02x}{g:02x}{b:02x}'
|
||||||
|
lamp_matrix[row][col] = new_color_hex
|
||||||
return jsonify(success=True, new_color=new_color_hex)
|
|
||||||
|
return jsonify(success=True)
|
||||||
else:
|
else:
|
||||||
# === LIVE MODE: Communicate with the BLE device ===
|
# === LIVE MODE: Communicate with the BLE device ===
|
||||||
new_color_bytes = int(f'{r:02x}{g:02x}{b:02x}', 16).to_bytes(3, 'big')
|
|
||||||
|
|
||||||
asyncio.run_coroutine_threadsafe(
|
asyncio.run_coroutine_threadsafe(
|
||||||
set_lamp_colors_on_ble(lamps_to_update, new_color_bytes),
|
set_full_matrix_on_ble(full_matrix),
|
||||||
ble_event_loop
|
ble_event_loop
|
||||||
)
|
)
|
||||||
return jsonify(success=True, new_color=new_color_hex)
|
return jsonify(success=True)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error in set_color route: {e}")
|
print(f"Error in set_matrix route: {e}")
|
||||||
return jsonify(success=False, message=str(e)), 500
|
return jsonify(success=False, message=str(e)), 500
|
||||||
|
|
||||||
# =================================================================================================
|
# =================================================================================================
|
||||||
|
|||||||
@ -48,7 +48,6 @@ async def setLampToColor(bleClient, bleChars, section, color, colorCenter):
|
|||||||
lampPos = bleChars.index(char)
|
lampPos = bleChars.index(char)
|
||||||
value = sectionSerial[lampPos]
|
value = sectionSerial[lampPos]
|
||||||
print(f"Setting Lamp number {lampPos} to {value}")
|
print(f"Setting Lamp number {lampPos} to {value}")
|
||||||
|
|
||||||
await bleClient.write_gatt_char(char.uuid, value)
|
await bleClient.write_gatt_char(char.uuid, value)
|
||||||
|
|
||||||
async def connect_to_ble_device(device_name):
|
async def connect_to_ble_device(device_name):
|
||||||
|
|||||||
@ -2,142 +2,96 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Lamp Matrix Control</title>
|
<title>Lamp Matrix Control</title>
|
||||||
<style>
|
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
||||||
body {
|
|
||||||
font-family: Arial, sans-serif;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
height: 100vh;
|
|
||||||
margin: 0;
|
|
||||||
background-color: #f0f0f0;
|
|
||||||
}
|
|
||||||
.container {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.matrix-grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(5, 70px);
|
|
||||||
grid-template-rows: repeat(5, 70px);
|
|
||||||
gap: 20px;
|
|
||||||
padding: 20px;
|
|
||||||
background-color: #333;
|
|
||||||
border-radius: 10px;
|
|
||||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
.lamp {
|
|
||||||
width: 70px;
|
|
||||||
height: 70px;
|
|
||||||
border-radius: 10%;
|
|
||||||
background-color: #000;
|
|
||||||
transition: box-shadow 0.2s, transform 0.1s;
|
|
||||||
cursor: pointer;
|
|
||||||
border: 2px solid transparent;
|
|
||||||
}
|
|
||||||
.lamp.on {
|
|
||||||
box-shadow: 0 0 15px currentColor, 0 0 25px currentColor;
|
|
||||||
}
|
|
||||||
.lamp.selected {
|
|
||||||
border: 2px solid #fff;
|
|
||||||
transform: scale(1.1);
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
color: #333;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
.region-control {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
.region-control button {
|
|
||||||
padding: 10px 15px;
|
|
||||||
margin: 5px;
|
|
||||||
font-size: 14px;
|
|
||||||
cursor: pointer;
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
border-radius: 5px;
|
|
||||||
background-color: #fff;
|
|
||||||
}
|
|
||||||
.slider-controls {
|
|
||||||
display: flex;
|
|
||||||
gap: 20px;
|
|
||||||
align-items: flex-start;
|
|
||||||
}
|
|
||||||
.control-panel {
|
|
||||||
background-color: #444;
|
|
||||||
padding: 20px;
|
|
||||||
border-radius: 10px;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.slider-group {
|
|
||||||
width: 250px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 5px;
|
|
||||||
}
|
|
||||||
.slider-group input {
|
|
||||||
width: 100%;
|
|
||||||
-webkit-appearance: none;
|
|
||||||
height: 8px;
|
|
||||||
border-radius: 5px;
|
|
||||||
outline: none;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.slider-group input::-webkit-slider-thumb {
|
|
||||||
-webkit-appearance: none;
|
|
||||||
height: 20px;
|
|
||||||
width: 20px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: #fff;
|
|
||||||
cursor: pointer;
|
|
||||||
box-shadow: 0 0 5px rgba(0,0,0,0.5);
|
|
||||||
margin-top: 2px;
|
|
||||||
}
|
|
||||||
.slider-group input::-webkit-slider-runnable-track {
|
|
||||||
height: 24px;
|
|
||||||
border-radius: 12px;
|
|
||||||
background: #aaa; /* Generic solid color */
|
|
||||||
}
|
|
||||||
.slider-label {
|
|
||||||
color: #fff;
|
|
||||||
font-size: 14px;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
.center-lamp-control {
|
|
||||||
margin-top: 10px;
|
|
||||||
background-color: #444;
|
|
||||||
padding: 20px;
|
|
||||||
border-radius: 10px;
|
|
||||||
}
|
|
||||||
.center-lamp-control h2 {
|
|
||||||
color: #fff;
|
|
||||||
font-size: 16px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
|
// State for the entire 5x5 matrix, storing {ww, cw, blue} for each lamp
|
||||||
|
var lampMatrixState = Array(5).fill(null).map(() => Array(5).fill({ww: 0, cw: 0, blue: 0}));
|
||||||
var selectedLamps = [];
|
var selectedLamps = [];
|
||||||
|
|
||||||
// NEW: Function to update sliders based on the three light values
|
// Function to calculate a visual RGB color from the three light values using a proper additive model
|
||||||
function updateSliders(ww, cw, blue) {
|
function calculateRgb(ww, cw, blue) {
|
||||||
$('#ww-slider').val(ww);
|
// Define the RGB components for each light source based on slider track colors
|
||||||
$('#cw-slider').val(cw);
|
const warmWhiteR = 255;
|
||||||
$('#blue-slider').val(blue);
|
const warmWhiteG = 192;
|
||||||
|
const warmWhiteB = 128;
|
||||||
|
|
||||||
|
const coolWhiteR = 192;
|
||||||
|
const coolWhiteG = 224;
|
||||||
|
const coolWhiteB = 255;
|
||||||
|
|
||||||
|
const blueR = 0;
|
||||||
|
const blueG = 0;
|
||||||
|
const blueB = 255;
|
||||||
|
|
||||||
|
// Normalize the slider values (0-255) and apply them to the base colors
|
||||||
|
var r = (ww / 255) * warmWhiteR + (cw / 255) * coolWhiteR + (blue / 255) * blueR;
|
||||||
|
var g = (ww / 255) * warmWhiteG + (cw / 255) * coolWhiteG + (blue / 255) * blueG;
|
||||||
|
var b = (ww / 255) * warmWhiteB + (cw / 255) * coolWhiteB + (blue / 255) * blueB;
|
||||||
|
|
||||||
|
// Clamp the values to 255 and convert to integer
|
||||||
|
r = Math.min(255, Math.round(r));
|
||||||
|
g = Math.min(255, Math.round(g));
|
||||||
|
b = Math.min(255, Math.round(b));
|
||||||
|
|
||||||
|
// Convert to hex string
|
||||||
|
var toHex = (c) => ('0' + c.toString(16)).slice(-2);
|
||||||
|
return '#' + toHex(r) + toHex(g) + toHex(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateLampUI(lamp, colorState) {
|
||||||
|
var newColor = calculateRgb(colorState.ww, colorState.cw, colorState.blue);
|
||||||
|
var lampElement = $(`.lamp[data-row="${lamp.row}"][data-col="${lamp.col}"]`);
|
||||||
|
lampElement.css('background-color', newColor);
|
||||||
|
if (newColor === '#000000') {
|
||||||
|
lampElement.removeClass('on');
|
||||||
|
lampElement.css('box-shadow', `inset 0 0 5px rgba(0,0,0,0.5)`);
|
||||||
|
} else {
|
||||||
|
lampElement.addClass('on');
|
||||||
|
lampElement.css('box-shadow', `0 0 15px ${newColor}, 0 0 25px ${newColor}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NEW: Function to calculate a visual RGB color from the three light values
|
// Function to update the UI and send the full matrix state to the backend
|
||||||
function calculateRgb(ww, cw, blue) {
|
function sendFullMatrixUpdate(lampsToUpdate, isRegionUpdate = false) {
|
||||||
// A simple approximation for UI display
|
var fullMatrixData = lampMatrixState.map(row => row.map(lamp => ({
|
||||||
var r = Math.min(255, ww + cw);
|
ww: lamp.ww,
|
||||||
var g = Math.min(255, ww + cw + blue);
|
cw: lamp.cw,
|
||||||
var b = Math.min(255, blue + cw);
|
blue: lamp.blue
|
||||||
return '#' + ('0' + r.toString(16)).slice(-2) + ('0' + g.toString(16)).slice(-2) + ('0' + b.toString(16)).slice(-2);
|
})));
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: '/set_matrix',
|
||||||
|
type: 'POST',
|
||||||
|
contentType: 'application/json',
|
||||||
|
data: JSON.stringify({ matrix: fullMatrixData }),
|
||||||
|
success: function(response) {
|
||||||
|
if (response.success) {
|
||||||
|
if (isRegionUpdate) {
|
||||||
|
// On a region button click, update the entire matrix UI
|
||||||
|
for (var r = 0; r < 5; r++) {
|
||||||
|
for (var c = 0; c < 5; c++) {
|
||||||
|
updateLampUI({row: r, col: c}, lampMatrixState[r][c]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Otherwise, just update the lamps that changed
|
||||||
|
lampsToUpdate.forEach(function(lamp) {
|
||||||
|
updateLampUI(lamp, lampMatrixState[lamp.row][lamp.col]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateSliders(ww, cw, blue, prefix = '') {
|
||||||
|
$(`#${prefix}ww-slider`).val(ww);
|
||||||
|
$(`#${prefix}cw-slider`).val(cw);
|
||||||
|
$(`#${prefix}blue-slider`).val(blue);
|
||||||
|
$(`#${prefix}ww-number`).val(ww);
|
||||||
|
$(`#${prefix}cw-number`).val(cw);
|
||||||
|
$(`#${prefix}blue-number`).val(blue);
|
||||||
}
|
}
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
@ -169,116 +123,102 @@
|
|||||||
{row: 2, col: 0}, {row: 2, col: 4},
|
{row: 2, col: 0}, {row: 2, col: 4},
|
||||||
{row: 3, col: 0}, {row: 3, col: 4},
|
{row: 3, col: 0}, {row: 3, col: 4},
|
||||||
{row: 4, col: 0}, {row: 4, col: 1}, {row: 4, col: 2}, {row: 4, col: 3}, {row: 4, col: 4},
|
{row: 4, col: 0}, {row: 4, col: 1}, {row: 4, col: 2}, {row: 4, col: 3}, {row: 4, col: 4},
|
||||||
|
],
|
||||||
|
'All': [
|
||||||
|
{row: 0, col: 0}, {row: 0, col: 1}, {row: 0, col: 2}, {row: 0, col: 3}, {row: 0, col: 4},
|
||||||
|
{row: 1, col: 0}, {row: 1, col: 1}, {row: 1, col: 2}, {row: 1, col: 3}, {row: 1, col: 4},
|
||||||
|
{row: 2, col: 0}, {row: 2, col: 1}, {row: 2, col: 3}, {row: 2, col: 4},
|
||||||
|
{row: 3, col: 0}, {row: 3, col: 1}, {row: 3, col: 2}, {row: 3, col: 3}, {row: 3, col: 4},
|
||||||
|
{row: 4, col: 0}, {row: 4, col: 1}, {row: 4, col: 2}, {row: 4, col: 3}, {row: 4, col: 4},
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Exclude the center lamp from the 'All' region
|
||||||
|
var allRegionWithoutCenter = regionMaps['All'].filter(lamp => !(lamp.row === 2 && lamp.col === 2));
|
||||||
|
regionMaps['All'] = allRegionWithoutCenter;
|
||||||
|
|
||||||
|
// Initialize lampMatrixState from the initial HTML colors
|
||||||
|
$('.lamp').each(function() {
|
||||||
|
var row = $(this).data('row');
|
||||||
|
var col = $(this).data('col');
|
||||||
|
var color = $(this).css('background-color');
|
||||||
|
var rgb = color.match(/\d+/g);
|
||||||
|
lampMatrixState[row][col] = {
|
||||||
|
ww: rgb[0], cw: rgb[1], blue: rgb[2]
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
$('.region-button').on('click', function() {
|
$('.region-button').on('click', function() {
|
||||||
var region = $(this).data('region');
|
var region = $(this).data('region');
|
||||||
selectedLamps = regionMaps[region];
|
var newlySelectedLamps = regionMaps[region];
|
||||||
|
|
||||||
|
// Clear selected class from all lamps
|
||||||
$('.lamp').removeClass('selected');
|
$('.lamp').removeClass('selected');
|
||||||
$('.lamp').not('[data-row="2"][data-col="2"]').css('box-shadow', 'inset 0 0 5px rgba(0,0,0,0.5)');
|
|
||||||
|
// Get the current slider values to use as the new default
|
||||||
|
var ww = parseInt($('#ww-slider').val());
|
||||||
|
var cw = parseInt($('#cw-slider').val());
|
||||||
|
var blue = parseInt($('#blue-slider').val());
|
||||||
|
|
||||||
|
// Reset all lamps except the center to black in our state
|
||||||
|
var lampsToUpdate = [];
|
||||||
|
var centerLampState = lampMatrixState[2][2];
|
||||||
|
lampMatrixState = Array(5).fill(null).map(() => Array(5).fill({ww: 0, cw: 0, blue: 0}));
|
||||||
|
lampMatrixState[2][2] = centerLampState; // Preserve center lamp state
|
||||||
|
|
||||||
|
// Set newly selected lamps to the current slider values
|
||||||
|
selectedLamps = newlySelectedLamps;
|
||||||
selectedLamps.forEach(function(lamp) {
|
selectedLamps.forEach(function(lamp) {
|
||||||
$(`.lamp[data-row="${lamp.row}"][data-col="${lamp.col}"]`).addClass('selected');
|
$(`.lamp[data-row="${lamp.row}"][data-col="${lamp.col}"]`).addClass('selected');
|
||||||
|
lampMatrixState[lamp.row][lamp.col] = {ww: ww, cw: cw, blue: blue};
|
||||||
});
|
});
|
||||||
|
|
||||||
if (selectedLamps.length > 0) {
|
if (selectedLamps.length > 0) {
|
||||||
$('.control-panel').show();
|
$('.control-panel').show();
|
||||||
// NEW: Update sliders based on first lamp's current state
|
// Update sliders to reflect the state of the first selected lamp
|
||||||
var firstLamp = selectedLamps[0];
|
var firstLamp = selectedLamps[0];
|
||||||
var firstLampElement = $(`.lamp[data-row="${firstLamp.row}"][data-col="${firstLamp.col}"]`);
|
var firstLampState = lampMatrixState[firstLamp.row][firstLamp.col];
|
||||||
// Note: This requires the Python backend to send back the ww, cw, blue values
|
updateSliders(firstLampState.ww, firstLampState.cw, firstLampState.blue, '');
|
||||||
// in the initial render, which is a required change from previous versions.
|
|
||||||
// For now, sliders will default to 0.
|
|
||||||
updateSliders(0, 0, 0);
|
|
||||||
} else {
|
} else {
|
||||||
$('.control-panel').hide();
|
$('.control-panel').hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Send the full matrix state
|
||||||
|
sendFullMatrixUpdate(lampsToUpdate, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
// NEW: Event listener for the region sliders
|
// Event listener for the region sliders and number inputs
|
||||||
$('.region-slider-group input').on('input', function() {
|
$('.region-slider-group input').on('input', function() {
|
||||||
if (selectedLamps.length === 0) return;
|
if (selectedLamps.length === 0) return;
|
||||||
|
|
||||||
var ww = $('#ww-slider').val();
|
var ww = parseInt($('#ww-slider').val());
|
||||||
var cw = $('#cw-slider').val();
|
var cw = parseInt($('#cw-slider').val());
|
||||||
var blue = $('#blue-slider').val();
|
var blue = parseInt($('#blue-slider').val());
|
||||||
|
|
||||||
var data = {
|
updateSliders(ww, cw, blue, '');
|
||||||
lamps: selectedLamps,
|
|
||||||
ww: ww,
|
var lampsToUpdate = [];
|
||||||
cw: cw,
|
selectedLamps.forEach(function(lamp) {
|
||||||
blue: blue
|
lampMatrixState[lamp.row][lamp.col] = {ww: ww, cw: cw, blue: blue};
|
||||||
};
|
lampsToUpdate.push(lamp);
|
||||||
|
|
||||||
$.ajax({
|
|
||||||
url: '/set_color',
|
|
||||||
type: 'POST',
|
|
||||||
contentType: 'application/json',
|
|
||||||
data: JSON.stringify(data),
|
|
||||||
success: function(response) {
|
|
||||||
if (response.success) {
|
|
||||||
var newColor = calculateRgb(ww, cw, blue);
|
|
||||||
|
|
||||||
$('.lamp').each(function() {
|
|
||||||
var currentLamp = $(this);
|
|
||||||
var lampRow = currentLamp.data('row');
|
|
||||||
var lampCol = currentLamp.data('col');
|
|
||||||
var isCenterLamp = (lampRow === 2 && lampCol === 2);
|
|
||||||
var isSelected = selectedLamps.some(lamp => lamp.row === lampRow && lamp.col === lampCol);
|
|
||||||
|
|
||||||
if (isSelected) {
|
|
||||||
currentLamp.css('background-color', newColor);
|
|
||||||
if (newColor === '#000000') {
|
|
||||||
currentLamp.removeClass('on');
|
|
||||||
} else {
|
|
||||||
currentLamp.addClass('on');
|
|
||||||
currentLamp.css('box-shadow', `0 0 15px ${newColor}, 0 0 25px ${newColor}`);
|
|
||||||
}
|
|
||||||
} else if (!isCenterLamp) {
|
|
||||||
currentLamp.css('background-color', '#000000');
|
|
||||||
currentLamp.removeClass('on');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
sendFullMatrixUpdate(lampsToUpdate);
|
||||||
});
|
});
|
||||||
|
|
||||||
// NEW: Event listener for the center lamp sliders
|
// Event listener for the center lamp sliders and number inputs
|
||||||
$('.center-slider-group input').on('input', function() {
|
$('.center-slider-group input').on('input', function() {
|
||||||
var ww = $('#center-ww-slider').val();
|
var ww = parseInt($('#center-ww-slider').val());
|
||||||
var cw = $('#center-cw-slider').val();
|
var cw = parseInt($('#center-cw-slider').val());
|
||||||
var blue = $('#center-blue-slider').val();
|
var blue = parseInt($('#center-blue-slider').val());
|
||||||
|
|
||||||
var data = {
|
updateSliders(ww, cw, blue, 'center-');
|
||||||
lamps: [{row: 2, col: 2}],
|
|
||||||
ww: ww,
|
var centerLamp = {row: 2, col: 2};
|
||||||
cw: cw,
|
|
||||||
blue: blue
|
lampMatrixState[centerLamp.row][centerLamp.col] = {ww: ww, cw: cw, blue: blue};
|
||||||
};
|
|
||||||
|
sendFullMatrixUpdate([centerLamp]);
|
||||||
$.ajax({
|
|
||||||
url: '/set_color',
|
|
||||||
type: 'POST',
|
|
||||||
contentType: 'application/json',
|
|
||||||
data: JSON.stringify(data),
|
|
||||||
success: function(response) {
|
|
||||||
if (response.success) {
|
|
||||||
var newColor = calculateRgb(ww, cw, blue);
|
|
||||||
var centerLamp = $(`.lamp[data-row="2"][data-col="2"]`);
|
|
||||||
centerLamp.css('background-color', newColor);
|
|
||||||
|
|
||||||
if (newColor === '#000000') {
|
|
||||||
centerLamp.removeClass('on');
|
|
||||||
} else {
|
|
||||||
centerLamp.addClass('on');
|
|
||||||
centerLamp.css('box-shadow', `0 0 15px ${newColor}, 0 0 25px ${newColor}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@ -293,6 +233,7 @@
|
|||||||
<button class="region-button" data-region="Right">Right</button>
|
<button class="region-button" data-region="Right">Right</button>
|
||||||
<button class="region-button" data-region="Inner ring">Inner ring</button>
|
<button class="region-button" data-region="Inner ring">Inner ring</button>
|
||||||
<button class="region-button" data-region="Outer ring">Outer ring</button>
|
<button class="region-button" data-region="Outer ring">Outer ring</button>
|
||||||
|
<button class="region-button" data-region="All">All</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="matrix-grid">
|
<div class="matrix-grid">
|
||||||
{% for row in range(5) %}
|
{% for row in range(5) %}
|
||||||
@ -306,23 +247,42 @@
|
|||||||
<div class="center-lamp-control">
|
<div class="center-lamp-control">
|
||||||
<h2>Center Lamp</h2>
|
<h2>Center Lamp</h2>
|
||||||
<div class="slider-group center-slider-group">
|
<div class="slider-group center-slider-group">
|
||||||
<span class="slider-label">Warm White (3000K)</span>
|
<div class="slider-row">
|
||||||
<input type="range" id="center-ww-slider" min="0" max="255" value="0">
|
<span class="slider-label">Warm White (3000K)</span>
|
||||||
<span class="slider-label">Cool White (6500K)</span>
|
<input type="range" id="center-ww-slider" min="0" max="255" value="0" class="white-3000k">
|
||||||
<input type="range" id="center-cw-slider" min="0" max="255" value="0">
|
<input type="number" id="center-ww-number" min="0" max="255" value="0">
|
||||||
<span class="slider-label">Blue</span>
|
</div>
|
||||||
<input type="range" id="center-blue-slider" min="0" max="255" value="0">
|
<div class="slider-row">
|
||||||
|
<span class="slider-label">Cool White (6500K)</span>
|
||||||
|
<input type="range" id="center-cw-slider" min="0" max="255" value="0" class="white-6500k">
|
||||||
|
<input type="number" id="center-cw-number" min="0" max="255" value="0">
|
||||||
|
</div>
|
||||||
|
<div class="slider-row">
|
||||||
|
<span class="slider-label">Blue</span>
|
||||||
|
<input type="range" id="center-blue-slider" min="0" max="255" value="0" class="blue">
|
||||||
|
<input type="number" id="center-blue-number" min="0" max="255" value="0">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="control-panel">
|
<div class="control-panel">
|
||||||
|
<h2>Selected Region</h2>
|
||||||
<div class="slider-group region-slider-group">
|
<div class="slider-group region-slider-group">
|
||||||
<span class="slider-label">Warm White (3000K)</span>
|
<div class="slider-row">
|
||||||
<input type="range" id="ww-slider" min="0" max="255" value="0">
|
<span class="slider-label">Warm White (3000K)</span>
|
||||||
<span class="slider-label">Cool White (6500K)</span>
|
<input type="range" id="ww-slider" min="0" max="255" value="0" class="white-3000k">
|
||||||
<input type="range" id="cw-slider" min="0" max="255" value="0">
|
<input type="number" id="ww-number" min="0" max="255" value="0">
|
||||||
<span class="slider-label">Blue</span>
|
</div>
|
||||||
<input type="range" id="blue-slider" min="0" max="255" value="0">
|
<div class="slider-row">
|
||||||
|
<span class="slider-label">Cool White (6500K)</span>
|
||||||
|
<input type="range" id="cw-slider" min="0" max="255" value="0" class="white-6500k">
|
||||||
|
<input type="number" id="cw-number" min="0" max="255" value="0">
|
||||||
|
</div>
|
||||||
|
<div class="slider-row">
|
||||||
|
<span class="slider-label">Blue</span>
|
||||||
|
<input type="range" id="blue-slider" min="0" max="255" value="0" class="blue">
|
||||||
|
<input type="number" id="blue-number" min="0" max="255" value="0">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -7,7 +7,7 @@ app = Flask(__name__)
|
|||||||
def create_spiral_map(n=5):
|
def create_spiral_map(n=5):
|
||||||
"""
|
"""
|
||||||
Creates a pre-computed spiral mapping for an n x n matrix.
|
Creates a pre-computed spiral mapping for an n x n matrix.
|
||||||
The spiral starts from the center and moves outwards.
|
The spiral starts from the center, moves up, then right, and spirals outwards.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
n (int): The size of the square matrix.
|
n (int): The size of the square matrix.
|
||||||
@ -25,9 +25,10 @@ def create_spiral_map(n=5):
|
|||||||
address = 0
|
address = 0
|
||||||
spiral_map[r][c] = address
|
spiral_map[r][c] = address
|
||||||
|
|
||||||
# Directions: Right, Down, Left, Up
|
# Directions: Up, Right, Down, Left
|
||||||
dr = [0, 1, 0, -1]
|
# This is the key change to adjust the spiral path
|
||||||
dc = [1, 0, -1, 0]
|
dr = [-1, 0, 1, 0]
|
||||||
|
dc = [0, 1, 0, -1]
|
||||||
direction = 0
|
direction = 0
|
||||||
|
|
||||||
segment_length = 1
|
segment_length = 1
|
||||||
@ -44,7 +45,6 @@ def create_spiral_map(n=5):
|
|||||||
direction = (direction + 1) % 4
|
direction = (direction + 1) % 4
|
||||||
steps += 1
|
steps += 1
|
||||||
|
|
||||||
# Increase segment length after every two turns
|
|
||||||
if steps % 2 == 0:
|
if steps % 2 == 0:
|
||||||
segment_length += 1
|
segment_length += 1
|
||||||
|
|
||||||
@ -119,6 +119,7 @@ def set_color():
|
|||||||
col = lamp['col']
|
col = lamp['col']
|
||||||
if 0 <= row < 5 and 0 <= col < 5:
|
if 0 <= row < 5 and 0 <= col < 5:
|
||||||
lamp_matrix[row][col] = new_color
|
lamp_matrix[row][col] = new_color
|
||||||
|
print(get_spiral_address(row, col, SPIRAL_MAP_5x5))
|
||||||
|
|
||||||
return jsonify(success=True, new_color=new_color)
|
return jsonify(success=True, new_color=new_color)
|
||||||
|
|
||||||
@ -126,4 +127,4 @@ def set_color():
|
|||||||
return jsonify(success=False, message="Invalid data received")
|
return jsonify(success=False, message="Invalid data received")
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
app.run(debug=True)
|
app.run(debug=True,host='0.0.0.0')
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user