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