Base UI
This commit is contained in:
26
components/layout.py
Normal file
26
components/layout.py
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
html = """<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Thermostate</title>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<script type="text/javascript">
|
||||||
|
window.tailwind = window.tailwind || {};
|
||||||
|
window.tailwind.config = {
|
||||||
|
corePlugins: {
|
||||||
|
preflight: false,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
|
||||||
|
<script defer src="https://cdn.tailwindcss.com"></script>
|
||||||
|
<script defer src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" crossorigin="anonymous"></script>
|
||||||
|
<script defer src="https://unpkg.com/htmx.org@1.9.8"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
%s
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
68
components/main.py
Normal file
68
components/main.py
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import constants
|
||||||
|
import globals
|
||||||
|
|
||||||
|
_content = """
|
||||||
|
<h1>Thermostat</h1>
|
||||||
|
<div id="header_buttons" class="flex flex-row">
|
||||||
|
%s
|
||||||
|
</div>
|
||||||
|
%s
|
||||||
|
"""
|
||||||
|
|
||||||
|
_header_buttons = """
|
||||||
|
<button class="btn %s" hx-get="/set_by_week" hx-target="#header_buttons">Same for all days</button>
|
||||||
|
<button class="btn %s" hx-get="/set_by_day" hx-target="#header_buttons">By day</button>
|
||||||
|
"""
|
||||||
|
|
||||||
|
_by_week_content = """
|
||||||
|
<form hx-post="/save_by_week" hx-target="#settings">
|
||||||
|
<h2>Settings for all day</h2>
|
||||||
|
<div id="settings">
|
||||||
|
%s
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-row">
|
||||||
|
<button class="btn btn-success" hx-get="/add_by_week" hx-target="#settings">Add</button>
|
||||||
|
<input class="btn btn-success" type="submit" value="Save" />
|
||||||
|
<button class="btn btn-danger" hx-get="/reset_by_week" hx-target="#settings">Cancel</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
"""
|
||||||
|
|
||||||
|
def _format_edit_line(time, temp, i):
|
||||||
|
return """
|
||||||
|
<div class="flex flex-row mb-3">
|
||||||
|
<input type="time" value="%s" class="form-control mr-10" placeholder="Time" name="time%s" />
|
||||||
|
<input type="number" value="%s" class="form-control mr-10" placeholder="Temperature (°C)" name="value%s" />
|
||||||
|
</div>
|
||||||
|
""" % (time, i, temp, i)
|
||||||
|
|
||||||
|
def get_main(day_mode):
|
||||||
|
return _content % (get_header_buttons(day_mode), get_settings_with_id(day_mode))
|
||||||
|
|
||||||
|
def get_header_buttons(day_mode):
|
||||||
|
return _header_buttons % (
|
||||||
|
"btn-primary" if day_mode == constants.BY_WEEK else "btn-secondary",
|
||||||
|
"btn-primary" if day_mode == constants.BY_DAY else "btn-secondary"
|
||||||
|
)
|
||||||
|
|
||||||
|
def _get_settings_container(oob):
|
||||||
|
return ("""<div id="settings-container">""" if not oob
|
||||||
|
else """<div id="settings-container" hx-swap-oob="true">""") + "%s</div>"
|
||||||
|
|
||||||
|
def get_settings_with_id(day_mode, oob = False):
|
||||||
|
return _get_settings_container(oob) % get_settings(day_mode)
|
||||||
|
|
||||||
|
def get_settings(day_mode):
|
||||||
|
return get_byweek_settings() if day_mode == constants.BY_WEEK else "WIP"
|
||||||
|
|
||||||
|
def get_byweek_settings():
|
||||||
|
return _by_week_content % get_byweek_settings_content()
|
||||||
|
|
||||||
|
def get_byweek_settings_content():
|
||||||
|
content = ""
|
||||||
|
i = 0
|
||||||
|
for setting in globals.by_week_settings:
|
||||||
|
content += _format_edit_line(setting.time.format(), setting.temp, i)
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
return content
|
||||||
2
constants.py
Normal file
2
constants.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
BY_DAY = 1
|
||||||
|
BY_WEEK = 2
|
||||||
16
globals.py
Normal file
16
globals.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import constants
|
||||||
|
from type import Setting, Time
|
||||||
|
|
||||||
|
global day_mode
|
||||||
|
global by_day_settings
|
||||||
|
global by_week_settings
|
||||||
|
|
||||||
|
|
||||||
|
def init():
|
||||||
|
global day_mode
|
||||||
|
global by_day_settings
|
||||||
|
global by_week_settings
|
||||||
|
|
||||||
|
by_day_settings = []
|
||||||
|
by_week_settings = [Setting(Time(10,10), 20.5)]
|
||||||
|
day_mode = constants.BY_WEEK
|
||||||
8
helper.py
Normal file
8
helper.py
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import uos
|
||||||
|
|
||||||
|
def file_or_dir_exists(filename):
|
||||||
|
try:
|
||||||
|
uos.stat(filename)
|
||||||
|
return True
|
||||||
|
except OSError:
|
||||||
|
return False
|
||||||
167
main.py
Normal file
167
main.py
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
import os
|
||||||
|
import network
|
||||||
|
import socket
|
||||||
|
import time
|
||||||
|
from machine import Pin
|
||||||
|
|
||||||
|
import constants
|
||||||
|
import globals
|
||||||
|
import helper
|
||||||
|
import components.layout as layout
|
||||||
|
import components.main as main
|
||||||
|
from secrets import secrets
|
||||||
|
from type import Setting, Time
|
||||||
|
|
||||||
|
# while True:
|
||||||
|
# time.sleep_ms(200)
|
||||||
|
|
||||||
|
globals.init()
|
||||||
|
|
||||||
|
global day_mode_filename
|
||||||
|
day_mode_filename = "day_mode.txt"
|
||||||
|
|
||||||
|
led = Pin('LED', Pin.OUT)
|
||||||
|
def blink_onboard_led(num_blinks):
|
||||||
|
for i in range(num_blinks):
|
||||||
|
led.value(1)
|
||||||
|
time.sleep_ms(200)
|
||||||
|
led.value(0)
|
||||||
|
time.sleep_ms(200)
|
||||||
|
|
||||||
|
blink_onboard_led(1)
|
||||||
|
|
||||||
|
if not helper.file_or_dir_exists(day_mode_filename):
|
||||||
|
print('File does not exist.')
|
||||||
|
with open(day_mode_filename, "w") as f:
|
||||||
|
f.write("byweek")
|
||||||
|
f.close()
|
||||||
|
else:
|
||||||
|
with open(day_mode_filename) as f:
|
||||||
|
content = f.readline()
|
||||||
|
|
||||||
|
if content == "byweek":
|
||||||
|
print("byweek")
|
||||||
|
globals.day_mode = constants.BY_WEEK
|
||||||
|
else:
|
||||||
|
print("byday")
|
||||||
|
globals.day_mode = constants.BY_DAY
|
||||||
|
|
||||||
|
ssid = secrets['ssid']
|
||||||
|
password = secrets['pw']
|
||||||
|
|
||||||
|
wlan = network.WLAN(network.STA_IF)
|
||||||
|
wlan.active(True)
|
||||||
|
wlan.connect(ssid, password)
|
||||||
|
|
||||||
|
max_wait = 10
|
||||||
|
while max_wait > 0:
|
||||||
|
if wlan.status() < 0 or wlan.status() >= 3:
|
||||||
|
break
|
||||||
|
max_wait -= 1
|
||||||
|
print('waiting for connection...')
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
|
||||||
|
if wlan.status() != 3:
|
||||||
|
blink_onboard_led(3)
|
||||||
|
raise RuntimeError('network connection failed')
|
||||||
|
else:
|
||||||
|
print('connected')
|
||||||
|
status = wlan.ifconfig()
|
||||||
|
print( 'ip = ' + status[0] )
|
||||||
|
|
||||||
|
blink_onboard_led(2)
|
||||||
|
|
||||||
|
addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1]
|
||||||
|
|
||||||
|
s = socket.socket()
|
||||||
|
s.bind(addr)
|
||||||
|
s.listen(1)
|
||||||
|
|
||||||
|
print('listening on', addr)
|
||||||
|
|
||||||
|
def result_ok(cl, response = None):
|
||||||
|
cl.send('HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n')
|
||||||
|
cl.send(response if response is not None else "Ok")
|
||||||
|
cl.close()
|
||||||
|
|
||||||
|
def result_notfound(cl, response = None):
|
||||||
|
cl.send('HTTP/1.0 404 NotFound\r\nContent-type: text/html\r\n\r\n')
|
||||||
|
cl.send(response if response is not None else "Not Found")
|
||||||
|
cl.close()
|
||||||
|
|
||||||
|
def check_header_button_endpoints(cl, request):
|
||||||
|
if request.find('/set_by_week') == 6:
|
||||||
|
set_day_mode(constants.BY_WEEK)
|
||||||
|
result_ok(cl, main.get_header_buttons(day_mode = globals.day_mode) + main.get_settings_with_id(day_mode = globals.day_mode, oob = True))
|
||||||
|
return True
|
||||||
|
elif request.find('/set_by_day') == 6:
|
||||||
|
set_day_mode(constants.BY_DAY)
|
||||||
|
result_ok(cl, main.get_header_buttons(day_mode = globals.day_mode) + main.get_settings_with_id(day_mode = globals.day_mode, oob = True))
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def check_by_week_endpoints(cl, request):
|
||||||
|
if request.find('/add_by_week') == 6:
|
||||||
|
globals.by_week_settings.append(Setting(Time(10,0), 22))
|
||||||
|
result_ok(cl, main.get_byweek_settings_content())
|
||||||
|
return True
|
||||||
|
|
||||||
|
def set_day_mode(mode):
|
||||||
|
global day_mode_filename
|
||||||
|
if mode == constants.BY_WEEK:
|
||||||
|
globals.day_mode = constants.BY_WEEK
|
||||||
|
elif mode == constants.BY_DAY:
|
||||||
|
globals.day_mode = constants.BY_DAY
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
with open(day_mode_filename, "w") as f:
|
||||||
|
value = "byday" if mode == constants.BY_DAY else "byweek"
|
||||||
|
print("Setting day mode to " + value)
|
||||||
|
f.write(value)
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
# Listen for connections
|
||||||
|
while True:
|
||||||
|
cl, addr = s.accept()
|
||||||
|
try:
|
||||||
|
print('client connected from', addr)
|
||||||
|
request = cl.recv(1024)
|
||||||
|
print(request)
|
||||||
|
|
||||||
|
request = str(request)
|
||||||
|
|
||||||
|
if request.find('/ ') == 6:
|
||||||
|
response = layout.html % main.get_main(day_mode = globals.day_mode)
|
||||||
|
|
||||||
|
result_ok(cl, response)
|
||||||
|
elif request.find('/light/on') == 6:
|
||||||
|
print("led on")
|
||||||
|
led.value(1)
|
||||||
|
|
||||||
|
response = layout.html % main.get_main(day_mode = globals.day_mode)
|
||||||
|
result_ok(cl, response)
|
||||||
|
elif request.find('/light/off') == 6:
|
||||||
|
print("led off")
|
||||||
|
led.value(0)
|
||||||
|
|
||||||
|
response = layout.html % main.get_main(day_mode = globals.day_mode)
|
||||||
|
result_ok(cl, response)
|
||||||
|
elif request.find('/exit') == 6:
|
||||||
|
print("exiting")
|
||||||
|
blink_onboard_led(5)
|
||||||
|
s.close()
|
||||||
|
break
|
||||||
|
elif check_header_button_endpoints(cl, request):
|
||||||
|
pass
|
||||||
|
elif check_by_week_endpoints(cl, request):
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
result_notfound(cl)
|
||||||
|
|
||||||
|
|
||||||
|
except OSError as e:
|
||||||
|
cl.close()
|
||||||
|
print('connection closed')
|
||||||
Reference in New Issue
Block a user