6 Commits

Author SHA1 Message Date
0aa3803f20 Add espnow 2025-04-21 21:52:20 +12:00
ed3351a20b Have single json endpoint 2025-04-21 21:51:55 +12:00
c8bcb85062 Add mac. Remove wifi settings 2025-04-21 21:51:15 +12:00
17d33d98e2 Send to a single json endpoint 2025-04-21 21:50:27 +12:00
afaf4c02e4 Change and and defaults
Add set_settings
2025-04-21 21:49:51 +12:00
e1e472b2e4 Remove wifi connect 2025-04-21 21:48:36 +12:00
6 changed files with 265 additions and 303 deletions

View File

@@ -1,19 +1,8 @@
import settings
import wifi
import time
from settings import Settings
print(wifi.ap('qwerty'))
s = Settings()
settings = Settings()
ssid = settings.get('wifi', {}).get('ssid', None)
password = settings.get('wifi', {}).get('password', None)
ip = settings.get('wifi', {}).get('ip', None)
gateway = settings.get('wifi', {}).get('gateway', None)
for i in range(10):
config = wifi.connect(ssid, password, ip, gateway)
if config:
print(config)
break
time.sleep(0.1)
name = s.get('name', 'led')
wifi.ap(name, '')

View File

@@ -1,11 +1,11 @@
import asyncio
from settings import Settings
import aioespnow
from settings import Settings, set_settings
from web import web
from patterns import Patterns
import gc
import utime
import machine
import ntptime
import time
import wifi
@@ -16,7 +16,7 @@ async def main():
settings = Settings()
patterns = Patterns(4, settings["num_leds"], selected=settings["selected_pattern"])
patterns = Patterns(4, settings["num_leds"], selected=settings["pattern"])
patterns.set_color1(tuple(int(settings["color1"][i:i+2], 16) for i in (1, 5, 3)))
patterns.set_color2(tuple(int(settings["color2"][i:i+2], 16) for i in (1, 5, 3)))
patterns.set_brightness(int(settings["brightness"]))
@@ -27,29 +27,34 @@ async def main():
# start the server in a bacakground task
print("Starting")
server = asyncio.create_task(w.start_server(host="0.0.0.0", port=80))
wdt = machine.WDT(timeout=10000)
wdt.feed()
#wdt = machine.WDT(timeout=10000)
#wdt.feed()
async def tick():
while True:
patterns.tick()
await asyncio.sleep_ms(1)
asyncio.create_task(tick())
async def espnow():
e = aioespnow.AIOESPNow() # Returns AIOESPNow enhanced with async support
e.active(True)
async for mac, msg in e:
print(msg)
set_settings(msg, settings, patterns)
first = True
asyncio.create_task(tick())
asyncio.create_task(espnow())
while True:
#print(time.localtime())
# gc.collect()
for i in range(60):
wdt.feed()
#wdt.feed()
await asyncio.sleep_ms(500)
# cleanup before ending the application
await server
asyncio.run(main())

View File

@@ -1,4 +1,7 @@
import json
import wifi
import ubinascii
import machine
class Settings(dict):
SETTINGS_FILE = "/settings.json"
@@ -9,12 +12,13 @@ class Settings(dict):
def set_defaults(self):
self["num_leds"] = 50
self["selected_pattern"] = "blink"
self["color1"] = "#000f00"
self["color2"] = "#0f0000"
self["pattern"] = "on"
self["color1"] = "#00ff00"
self["color2"] = "#ff0000"
self["delay"] = 100
self["brightness"] = 100
self["wifi"] = {"ssid": "", "password": ""}
self["brightness"] = 10
self["name"] = f"led-{ubinascii.hexlify(wifi.get_mac()).decode()}"
self["ap_password"] = ""
def save(self):
try:
@@ -48,6 +52,43 @@ def main():
print(f"Loaded number of LEDs: {new_settings['num_leds']}")
print(settings)
def set_settings(raw_json, settings, patterns):
try:
data = json.loads(raw_json)
print(data)
for key, value in data.items():
print(key, value)
if key == "color1":
patterns.set_color1(tuple(int(value[i:i+2], 16) for i in (1, 3, 5))) # Convert hex to RGB
elif key == "color2":
patterns.set_color2(tuple(int(value[i:i+2], 16) for i in (1, 3, 5))) # Convert hex to RGB
elif key == "num_leds":
patterns.update_num_leds(4, value)
elif key == "pattern":
if not patterns.select(value):
return "Pattern doesn't exist", 400
elif key == "delay":
delay = int(data["delay"])
patterns.set_delay(delay)
elif key == "brightness":
brightness = int(data["brightness"])
patterns.set_brightness(brightness)
elif key == "name":
settings[key] = value
settings.save()
machine.reset()
elif key == "sync":
patterns.sync()
return "OK", 200
else:
return "Invalid key", 400
settings[key] = value
settings.save()
return "OK", 200
except (KeyError, ValueError):
return "Bad request", 400
# Run the example
if __name__ == "__main__":
main()

View File

@@ -9,15 +9,15 @@ async function post(path, data) {
const response = await fetch(path, {
method: "POST",
headers: {
'Content-Type': 'application/json'
"Content-Type": "application/json",
},
body: JSON.stringify(data) // Convert data to JSON string
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);
console.error("Error during POST request:", error);
}
}
@@ -29,7 +29,7 @@ async function get(path) {
}
return await response.json(); // Assuming you are expecting JSON response
} catch (error) {
console.error('Error during GET request:', error);
console.error("Error during GET request:", error);
}
}
@@ -37,8 +37,8 @@ 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
const color = document.getElementById("color").value;
await post("settings", { color1: color }); // Send as JSON
}, 500);
}
@@ -46,22 +46,21 @@ 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
const color = document.getElementById("color2").value;
await post("/settings", { color2: color }); // Send as JSON
}, 500);
}
async function updatePattern(pattern) {
event.preventDefault();
await post("/pattern", { pattern }); // Send as JSON
await post("/settings", { pattern: pattern }); // Send as JSON
}
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
const brightness = document.getElementById("brightness").value;
await post("/settings", { brightness: brightness }); // Send as JSON
}, 500);
}
@@ -69,42 +68,33 @@ 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
const delay = document.getElementById("delay").value;
await post("/settings", { 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
const numLeds = document.getElementById("num_leds").value;
await post("/settings", { num_leds: parseInt(numLeds) }); // Send as JSON
}
async function updateWifi(event) {
async function updateName(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");
}
const name = document.getElementById("name").value;
await post("/settings", { name: name }); // Send as JSON
}
function createPatternButtons(patterns) {
const container = document.getElementById('pattern_buttons');
container.innerHTML = ''; // Clear previous buttons
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'
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) {
button.addEventListener("click", async function (event) {
event.preventDefault();
await updatePattern(pattern);
});
@@ -112,19 +102,25 @@ function createPatternButtons(patterns) {
});
}
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.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("name_form").addEventListener("submit", updateName);
document.getElementById("delay").addEventListener("touchend", updateDelay);
document
.getElementById("brightness")
.addEventListener("touchend", updateBrightness);
document.querySelectorAll(".pattern_button").forEach(button => {
document.querySelectorAll(".pattern_button").forEach((button) => {
console.log(button.value);
button.addEventListener('click', async event => {
button.addEventListener("click", async (event) => {
event.preventDefault();
await updatePattern(button.value);
});
@@ -133,15 +129,15 @@ document.addEventListener('DOMContentLoaded', async function() {
// 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';
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';
const settingsMenu = document.getElementById("settings_menu");
controls = document.getElementById("controls");
settingsMenu.style.display = "none";
controls.style.display = "block";
}

View File

@@ -1,12 +1,12 @@
{% args settings, patterns %}
<!DOCTYPE html>
{% args settings, patterns, mac %}
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>LED Control</title>
<script src="static/main.js"></script>
<link rel="stylesheet" href="static/main.css">
<link rel="stylesheet" href="static/main.css" />
</head>
<body>
<h1>Control LEDs</h1>
@@ -24,48 +24,73 @@
</div>
<form id="delay_form" method="post" action="/delay">
<label for="delay">Delay:</label>
<input type="range" id="delay" name="delay" min="1" max="1000" value="{{settings['delay']}}" step="10">
<input
type="range"
id="delay"
name="delay"
min="1"
max="1000"
value="{{settings['delay']}}"
step="10"
/>
</form>
<form id="brightness_form" method="post" action="/brightness">
<label for="brightness">Brightness:</label>
<input type="range" id="brightness" name="brightness" min="0" max="100" value="{{settings['brightness']}}" step="1">
<input
type="range"
id="brightness"
name="brightness"
min="0"
max="100"
value="{{settings['brightness']}}"
step="1"
/>
</form>
<form id="color_form" method="post" action="/color">
<input type="color" id="color" name="color" value="{{settings['color1']}}">
<input
type="color"
id="color"
name="color"
value="{{settings['color1']}}"
/>
</form>
<form id="color2_form" method="post" action="/color2">
<input type="color" id="color2" name="color2" value="{{settings['color2']}}">
<input
type="color"
id="color2"
name="color2"
value="{{settings['color2']}}"
/>
</form>
</div>
<!-- Settings Menu for num_leds, Wi-Fi SSID, and Password -->
<div id="settings_menu" style="display: none;">
<div id="settings_menu" style="display: none">
<h2>Settings</h2>
<form id="name_form" method="post" action="/name">
<label for="name">Name:</label>
<input
type="text"
id="name"
name="num_leds"
value="{{settings['name']}}"
/>
<input type="submit" value="Update Name" />
</form>
<!-- Separate form for submitting num_leds -->
<form id="num_leds_form" method="post" action="/num_leds">
<label for="num_leds">Number of LEDs:</label>
<input type="text" id="num_leds" name="num_leds" value="{{settings['num_leds']}}">
<input type="submit" value="Update Number of LEDs">
</form>
<!-- Form for Wi-Fi SSID and password -->
<form id="wifi_form" method="post" action="/wifi_settings">
<label for="ssid">Wi-Fi SSID:</label>
<input type="text" id="ssid" name="ssid" value="{{settings['wifi']['ssid']}}">
<br>
<label for="password">Wi-Fi Password:</label>
<input type="password" id="password" name="password">
<br>
<label for="ip">Wi-Fi IP:</label>
<input type="ip" id="ip" name="ip" value="{{settings.get('wifi', {}).get('ip', '')}}">
<br>
<label for="gateway">Wi-Fi Gateway:</label>
<input type="gateway" id="gateway" name="gateway" value="{{settings.get('wifi', {}).get('gateway', '')}}">
<br>
<input type="submit" value="Save Wi-Fi Settings">
<input
type="text"
id="num_leds"
name="num_leds"
value="{{settings['num_leds']}}"
/>
<input type="submit" value="Update Number of LEDs" />
</form>
<p>Mac address: {{mac}}</p>
</div>
</body>
</html>

View File

@@ -1,125 +1,31 @@
from microdot import Microdot, send_file, Response
from microdot.utemplate import Template
from microdot.websocket import with_websocket
import json
import machine
from settings import set_settings
import wifi
import json
def web(settings, patterns):
app = Microdot()
Response.default_content_type = 'text/html'
@app.route('/')
async def index(request):
return Template('/index.html').render(settings=settings, patterns=patterns.patterns.keys())
async def index_hnadler(request):
mac = wifi.get_mac().hex()
return Template('/index.html').render(settings=settings, patterns=patterns.patterns.keys(), mac=mac)
@app.route("/static/<path:path>")
def static(request, path):
def static_handler(request, path):
if '..' in path:
# Directory traversal is not allowed
return 'Not found', 404
return send_file('static/' + path)
@app.post("/num_leds")
def num_leds(request):
try:
data = json.loads(request.body.decode('utf-8'))
num_leds = int(data["num_leds"])
patterns.update_num_leds(4, num_leds)
settings["num_leds"] = num_leds
settings.save()
return "OK", 200
except (ValueError, KeyError, json.JSONDecodeError):
return "Bad request", 400
@app.post("/settings")
def settings_handler(request):
return set_settings(request.body.decode('utf-8'), settings, patterns)
@app.post("/pattern")
def pattern(request):
try:
data = json.loads(request.body.decode('utf-8'))
pattern = data["pattern"]
if patterns.select(pattern):
settings["selected_pattern"] = pattern
settings.save()
return "OK", 200
else:
return "Bad request", 400
except (KeyError, json.JSONDecodeError):
return "Bad request", 400
@app.post("/delay")
def delay(request):
try:
data = json.loads(request.body.decode('utf-8'))
delay = int(data["delay"])
patterns.set_delay(delay)
settings["delay"] = delay
settings.save()
return "OK", 200
except (ValueError, KeyError, json.JSONDecodeError):
return "Bad request", 400
@app.post("/brightness")
def brightness(request):
try:
data = json.loads(request.body.decode('utf-8'))
brightness = int(data["brightness"])
patterns.set_brightness(brightness)
settings["brightness"] = brightness
settings.save()
return "OK", 200
except (ValueError, KeyError, json.JSONDecodeError):
return "Bad request", 400
@app.post("/color")
def color(request):
try:
data = json.loads(request.body.decode('utf-8'))
color = data["color"]
patterns.set_color1(tuple(int(color[i:i+2], 16) for i in (1, 3, 5))) # Convert hex to RGB
settings["color1"] = color
settings.save()
return "OK", 200
except (KeyError, json.JSONDecodeError, ValueError):
return "Bad request", 400
@app.post("/color2")
def color2(request):
try:
data = json.loads(request.body.decode('utf-8'))
color = data["color2"]
patterns.set_color2(tuple(int(color[i:i+2], 16) for i in (1, 3, 5))) # Convert hex to RGB
settings["color2"] = color
settings.save()
return "OK", 200
except (KeyError, json.JSONDecodeError, ValueError):
return "Bad request", 400
@app.post("/wifi_settings")
def wifi_settings(request):
try:
data = json.loads(request.body.decode('utf-8'))
print(data)
ssid = settings['wifi']['ssid'] = data['ssid']
password = settings['wifi']['password'] = data.get('password', settings['wifi']['password'])
ip = settings['wifi']['ip'] = data.get('ip', None)
gateway = settings['wifi']['gateway'] = data.get('gateway', None)
print(settings)
if config := wifi.connect(ssid, password, ip, gateway):
print(config)
settings.save()
return "OK", 200
except Exception as e:
print(f"Wifi {e}")
return "Bad request", 400
@app.post("/sync")
def sync(request):
patterns.sync()
return "OK", 200
@app.route("/external")
@with_websocket