This commit is contained in:
Ádám Kovács
2023-11-07 14:00:40 +01:00
parent 36638e2dd1
commit ec52bd7384
7 changed files with 301 additions and 0 deletions

26
components/layout.py Normal file
View 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
View 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 (&deg;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
View File

@@ -0,0 +1,2 @@
BY_DAY = 1
BY_WEEK = 2

16
globals.py Normal file
View 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
View 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
View 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')

14
type.py Normal file
View File

@@ -0,0 +1,14 @@
class Time:
def __init__(self, hour, minute):
self.hour = hour
self.minute = minute
def format(self):
return "%02d:%02d" % (self.hour, self.minute)
class Setting:
def __init__(self, time, temp):
self.time = time
self.temp = temp