Cleaned up code

This commit is contained in:
Alexander Berry-Roe 2025-05-15 00:54:35 +01:00
parent 92defbe958
commit 721e71e4c7
2 changed files with 15 additions and 76 deletions

57
main.py
View File

@ -33,24 +33,22 @@ def get_weather_data(url):
pass pass
# Global variables # Global variables
latitude = 50.9097 latitude = 50.9097
longitude = -1.4043 longitude = -1.4043
today_forecast = None today_forecast = None
current_view = "simple" current_view = "simple"
station = None # Global WiFi station station = None
display = None # Global display object display = None
weather_disp = None # Global weather display object weather_disp = None
setup_complete = False # Flag to indicate if setup is complete setup_complete = False
weather_update_in_progress = False # Flag to prevent multiple concurrent updates weather_update_in_progress = False
# WiFi credentials # WiFi credentials
SSID = 'octopod' SSID = 'octopod'
PASSWORD = 'amniotic-duo-portfolio' PASSWORD = 'amniotic-duo-portfolio'
# Event flags for button presses
update_requested = False update_requested = False
view_change_requested = False view_change_requested = False
@ -63,10 +61,10 @@ def ensure_wifi_connected():
print("WiFi disconnected, reconnecting...") print("WiFi disconnected, reconnecting...")
station.connect(SSID, PASSWORD) station.connect(SSID, PASSWORD)
# Wait for connection with timeout
start_time = time.time() start_time = time.time()
while not station.isconnected(): while not station.isconnected():
if time.time() - start_time > 15: # 15 second timeout if time.time() - start_time > 15:
print("Failed to reconnect to WiFi") print("Failed to reconnect to WiFi")
return False return False
time.sleep(1) time.sleep(1)
@ -138,7 +136,6 @@ def update_weather():
def trigger_weather_update(): def trigger_weather_update():
_thread.start_new_thread(update_weather, ()) _thread.start_new_thread(update_weather, ())
# Update display based on current data
def update_display(): def update_display():
global display, weather_disp, today_forecast, current_view global display, weather_disp, today_forecast, current_view
@ -161,7 +158,6 @@ def days_in_month(year, month):
return [31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 30, 31][month - 1] return [31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 30, 31][month - 1]
def weekday(year, month, day): def weekday(year, month, day):
# Zellers congruence (0=Saturday, 1=Sunday, ..., 6=Friday)
if month < 3: if month < 3:
month += 12 month += 12
year -= 1 year -= 1
@ -175,13 +171,12 @@ def last_sunday(year, month):
"""Return (day, hour) of the last Sunday in a given month.""" """Return (day, hour) of the last Sunday in a given month."""
dim = days_in_month(year, month) dim = days_in_month(year, month)
for d in range(dim, dim - 7, -1): for d in range(dim, dim - 7, -1):
if weekday(year, month, d) == 1: # Sunday if weekday(year, month, d) == 1:
return d return d
return None # should never happen return None
def is_dst(year, month, day, hour=12): def is_dst(year, month, day, hour=12):
"""Returns True if the given UTC time is during UK DST (British Summer Time).""" """Returns True if the given UTC time is during UK DST (British Summer Time)."""
# DST starts last Sunday of March at 1:00
march_last_sunday = last_sunday(year, 3) march_last_sunday = last_sunday(year, 3)
dst_start = (3, march_last_sunday, 1) dst_start = (3, march_last_sunday, 1)
@ -194,25 +189,21 @@ def is_dst(year, month, day, hour=12):
return dst_start <= current < dst_end return dst_start <= current < dst_end
# Main loop (synchronous version)
def main_loop(): def main_loop():
global setup_complete, update_requested, view_change_requested global setup_complete, update_requested, view_change_requested
# Wait for setup to complete before proceeding
while not setup_complete: while not setup_complete:
time.sleep(0.1) time.sleep(0.1)
print("Main loop starting...") print("Main loop starting...")
display.auto_text("Ready. Press KEY0 to update") display.auto_text("Ready. Press KEY0 to update")
update_weather() # initial call update_weather()
last_weather = time.ticks_ms() last_weather = time.ticks_ms()
WEATHER_MS = 10 * 60 * 1000 # 10 min in milliseconds WEATHER_MS = 10 * 60 * 1000
while True: while True:
update_display() update_display()
# Has 10 minutes elapsed?
if time.ticks_diff(time.ticks_ms(), last_weather) >= WEATHER_MS: if time.ticks_diff(time.ticks_ms(), last_weather) >= WEATHER_MS:
update_weather() update_weather()
last_weather = time.ticks_ms() last_weather = time.ticks_ms()
@ -225,13 +216,12 @@ def main_loop():
def setup(): def setup():
global station, display, weather_disp, setup_complete global station, display, weather_disp, setup_complete
# --- WiFi Connection Setup ---
station = network.WLAN(network.STA_IF) station = network.WLAN(network.STA_IF)
station.active(True) station.active(True)
station.connect(SSID, PASSWORD) station.connect(SSID, PASSWORD)
print("Connecting to WiFi...") print("Connecting to WiFi...")
timeout = 15 # Extended timeout timeout = 15
start_time = time.time() start_time = time.time()
while not station.isconnected(): while not station.isconnected():
if time.time() - start_time > timeout: if time.time() - start_time > timeout:
@ -240,14 +230,11 @@ def setup():
time.sleep(1) time.sleep(1)
if station.isconnected(): if station.isconnected():
ip = station.ifconfig()[0] # Standard way to get IP ip = station.ifconfig()[0]
print("Connected to WiFi! IP address:", ip) print("Connected to WiFi! IP address:", ip)
# Set up the RTC using NTP
rtc = RTC() rtc = RTC()
try: try:
print("Syncing time with NTP server...") print("Syncing time with NTP server...")
# Retry NTP time sync
for _ in range(3): for _ in range(3):
try: try:
ntptime.settime() ntptime.settime()
@ -256,14 +243,11 @@ def setup():
print("NTP retry...") print("NTP retry...")
time.sleep(1) time.sleep(1)
# Get current time from RTC
current_time = list(rtc.datetime()) current_time = list(rtc.datetime())
year = current_time[0] year = current_time[0]
month = current_time[1] month = current_time[1]
day = current_time[2] day = current_time[2]
hour = current_time[4] hour = current_time[4]
# Adjust for UK time (UTC+0/+1)
if is_dst(year, month, day, hour): if is_dst(year, month, day, hour):
print("British Summer Time (BST) is active - adding 1 hour") print("British Summer Time (BST) is active - adding 1 hour")
current_time[4] += 1 current_time[4] += 1
@ -271,7 +255,6 @@ def setup():
current_time[4] -= 24 current_time[4] -= 24
current_time[2] += 1 current_time[2] += 1
# Update RTC with the adjusted time
rtc.datetime(tuple(current_time)) rtc.datetime(tuple(current_time))
print("RTC set with UK local time:", rtc.datetime()) print("RTC set with UK local time:", rtc.datetime())
except OSError as e: except OSError as e:
@ -280,20 +263,13 @@ def setup():
print("WiFi connection not established. Restart and try again.") print("WiFi connection not established. Restart and try again.")
raise SystemExit raise SystemExit
# Initialize display
display = get() display = get()
weather_disp = weather_display.WeatherDisplay(display) weather_disp = weather_display.WeatherDisplay(display)
# Setup button interrupts
button0 = Pin(display.KEY0, Pin.IN, Pin.PULL_UP) button0 = Pin(display.KEY0, Pin.IN, Pin.PULL_UP)
button1 = Pin(display.KEY1, Pin.IN, Pin.PULL_UP) button1 = Pin(display.KEY1, Pin.IN, Pin.PULL_UP)
button0.irq(trigger=Pin.IRQ_FALLING, handler=detailed_view) button0.irq(trigger=Pin.IRQ_FALLING, handler=detailed_view)
button1.irq(trigger=Pin.IRQ_FALLING, handler=simple_view) button1.irq(trigger=Pin.IRQ_FALLING, handler=simple_view)
# Display initial message
display.auto_text("Starting weather display...") display.auto_text("Starting weather display...")
# Set flag to indicate setup is complete
setup_complete = True setup_complete = True
print("Setup complete!") print("Setup complete!")
@ -311,16 +287,13 @@ def simple_view(pin):
# Main function # Main function
def main(): def main():
# Run setup
setup() setup()
# Start the main loop
main_loop() main_loop()
while True: while True:
time.sleep(1) time.sleep(1)
# Run the main program
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@ -1,5 +1,4 @@
import time import time
import uasyncio as asyncio
class WeatherDisplay: class WeatherDisplay:
@ -11,7 +10,6 @@ class WeatherDisplay:
self.time_thread = None self.time_thread = None
def weathercode_to_text(self, weathercode): def weathercode_to_text(self, weathercode):
# More detailed weather code interpretations
if weathercode == 0: if weathercode == 0:
return 'Clear Sky' return 'Clear Sky'
elif weathercode == 1: elif weathercode == 1:
@ -52,14 +50,11 @@ class WeatherDisplay:
# Create a clean buffer but don't send to display yet # Create a clean buffer but don't send to display yet
self.display.fill(self.display.black) self.display.fill(self.display.black)
# Text starts at left padding
text_x = self.text_padding text_x = self.text_padding
text_y = 8 text_y = 8
# Track regions that need updating
update_regions = [] update_regions = []
# Display temperature
temp_text = f"Temp: {weather_data.get('max_temp', 0):.0f}/{weather_data.get('min_temp', 0):.0f}C" temp_text = f"Temp: {weather_data.get('max_temp', 0):.0f}/{weather_data.get('min_temp', 0):.0f}C"
if force_full_update or not self.last_displayed_data or \ if force_full_update or not self.last_displayed_data or \
self.last_displayed_data.get('max_temp') != weather_data.get('max_temp') or \ self.last_displayed_data.get('max_temp') != weather_data.get('max_temp') or \
@ -67,7 +62,6 @@ class WeatherDisplay:
self.display.text(temp_text, text_x, text_y) self.display.text(temp_text, text_x, text_y)
update_regions.append((0, text_y, 16, text_y + 8)) update_regions.append((0, text_y, 16, text_y + 8))
# Display weather description
weathercode = weather_data.get('weathercode', 0) weathercode = weather_data.get('weathercode', 0)
weather_text = self.weathercode_to_text(weathercode) weather_text = self.weathercode_to_text(weathercode)
if force_full_update or not self.last_displayed_data or \ if force_full_update or not self.last_displayed_data or \
@ -75,14 +69,12 @@ class WeatherDisplay:
self.display.text(f"{weather_text}", text_x, text_y + 12) self.display.text(f"{weather_text}", text_x, text_y + 12)
update_regions.append((0, text_y + 12, 16, text_y + 20)) update_regions.append((0, text_y + 12, 16, text_y + 20))
# Display precipitation
precip_text = f"Precip: {weather_data.get('precip_mm', 0):.1f}mm" precip_text = f"Precip: {weather_data.get('precip_mm', 0):.1f}mm"
if force_full_update or not self.last_displayed_data or \ if force_full_update or not self.last_displayed_data or \
self.last_displayed_data.get('precip_mm') != weather_data.get('precip_mm'): self.last_displayed_data.get('precip_mm') != weather_data.get('precip_mm'):
self.display.text(precip_text, text_x, text_y + 24) self.display.text(precip_text, text_x, text_y + 24)
update_regions.append((0, text_y + 24, 16, text_y + 32)) update_regions.append((0, text_y + 24, 16, text_y + 32))
# Display date
date_text = weather_data.get('date', '') date_text = weather_data.get('date', '')
if date_text and len(date_text) >= 10: if date_text and len(date_text) >= 10:
year = date_text[0:4] year = date_text[0:4]
@ -94,28 +86,22 @@ class WeatherDisplay:
self.display.text(f"Date: {date_text}", text_x, text_y + 36) self.display.text(f"Date: {date_text}", text_x, text_y + 36)
update_regions.append((0, text_y + 36, 16, text_y + 44)) update_regions.append((0, text_y + 36, 16, text_y + 44))
# Store current data for future comparison
self.last_displayed_data = dict(weather_data) self.last_displayed_data = dict(weather_data)
self.last_displayed_data['view_type'] = 'detailed' self.last_displayed_data['view_type'] = 'detailed'
# Update display - either full or partial updates
if force_full_update: if force_full_update:
self.display.show() self.display.show()
else: else:
# Merge overlapping regions for more efficient updates
if update_regions: if update_regions:
# Simple approach: just update the full range covered by all regions
min_y = min(region[1] for region in update_regions) min_y = min(region[1] for region in update_regions)
max_y = max(region[3] for region in update_regions) max_y = max(region[3] for region in update_regions)
self.display.show(0, min_y, 16, max_y) self.display.show(0, min_y, 16, max_y)
else: else:
# No changes detected, no need to update
pass pass
def display_weather(self, weather_data): def display_weather(self, weather_data):
"""Detailed view with max/min temps, description, precip, date.""" """Detailed view with max/min temps, description, precip, date."""
# If this is the full API response, extract today's values:
if 'daily' in weather_data: if 'daily' in weather_data:
d = weather_data['daily'] d = weather_data['daily']
weather_data = { weather_data = {
@ -140,7 +126,6 @@ class WeatherDisplay:
text_y = 8 text_y = 8
update_regions = [] update_regions = []
# Temperature
temp_text = f"Temp: {weather_data.get('max_temp', 0):.0f}/" \ temp_text = f"Temp: {weather_data.get('max_temp', 0):.0f}/" \
f"{weather_data.get('min_temp', 0):.0f}C" f"{weather_data.get('min_temp', 0):.0f}C"
if (force_full_update if (force_full_update
@ -150,7 +135,6 @@ class WeatherDisplay:
self.display.text(temp_text, text_x, text_y) self.display.text(temp_text, text_x, text_y)
update_regions.append((text_y, text_y + 8)) update_regions.append((text_y, text_y + 8))
# Description
code = weather_data.get('weathercode', 0) code = weather_data.get('weathercode', 0)
desc = self.weathercode_to_text(code) desc = self.weathercode_to_text(code)
if (force_full_update if (force_full_update
@ -159,7 +143,6 @@ class WeatherDisplay:
self.display.text(desc, text_x, text_y + 12) self.display.text(desc, text_x, text_y + 12)
update_regions.append((text_y + 12, text_y + 20)) update_regions.append((text_y + 12, text_y + 20))
# Precip
precip = f"Precip: {weather_data.get('precip_mm', 0):.1f}mm" precip = f"Precip: {weather_data.get('precip_mm', 0):.1f}mm"
if (force_full_update if (force_full_update
or self.last_displayed_data.get('precip_mm') != weather_data.get('precip_mm') or self.last_displayed_data.get('precip_mm') != weather_data.get('precip_mm')
@ -167,7 +150,6 @@ class WeatherDisplay:
self.display.text(precip, text_x, text_y + 24) self.display.text(precip, text_x, text_y + 24)
update_regions.append((text_y + 24, text_y + 32)) update_regions.append((text_y + 24, text_y + 32))
# Date (YYYY-MM-DD → DD-MM-YYYY)
raw_date = weather_data.get('date', '') raw_date = weather_data.get('date', '')
if raw_date and len(raw_date) >= 10: if raw_date and len(raw_date) >= 10:
dd = raw_date[8:10]; mm = raw_date[5:7]; yyyy = raw_date[0:4] dd = raw_date[8:10]; mm = raw_date[5:7]; yyyy = raw_date[0:4]
@ -178,22 +160,18 @@ class WeatherDisplay:
self.display.text(formatted, text_x, text_y + 36) self.display.text(formatted, text_x, text_y + 36)
update_regions.append((text_y + 36, text_y + 44)) update_regions.append((text_y + 36, text_y + 44))
# Save for next time
self.last_displayed_data = dict(weather_data) self.last_displayed_data = dict(weather_data)
self.last_displayed_data['view_type'] = 'detailed' self.last_displayed_data['view_type'] = 'detailed'
# If full, just show everything
if force_full_update: if force_full_update:
self.display.show() self.display.show()
return return
# Otherwise do a partial update over all pages
if update_regions: if update_regions:
min_y = min(r[0] for r in update_regions) min_y = min(r[0] for r in update_regions)
max_y = max(r[1] for r in update_regions) max_y = max(r[1] for r in update_regions)
page_count = self.display.height // 8 page_count = self.display.height // 8
self.display.show(1, min_y, page_count, max_y) self.display.show(1, min_y, page_count, max_y)
# else: nothing changed
@ -203,24 +181,19 @@ class WeatherDisplay:
if force_full_update: if force_full_update:
self.display.clear() self.display.clear()
else: else:
# Create a clean buffer but don't send to display yet
self.display.fill(self.display.black) self.display.fill(self.display.black)
# Text starts at left padding
text_x = self.text_padding text_x = self.text_padding
center_y = self.display.height // 2 center_y = self.display.height // 2
# Track regions that need updating
update_regions = [] update_regions = []
# Display temperature large
temp_text = f"{weather_data.get('current_temp', 0):.0f}C" temp_text = f"{weather_data.get('current_temp', 0):.0f}C"
if force_full_update or not self.last_displayed_data or \ if force_full_update or not self.last_displayed_data or \
self.last_displayed_data.get('current_temp') != weather_data.get('current_temp'): self.last_displayed_data.get('current_temp') != weather_data.get('current_temp'):
self.display.text(temp_text, text_x, center_y - 8, scale=2) self.display.text(temp_text, text_x, center_y - 8, scale=2)
update_regions.append((0, center_y - 8, 16, center_y + 8)) # Scaled text is 16px high update_regions.append((0, center_y - 8, 16, center_y + 8)) # Scaled text is 16px high
# Display description
weather_desc = self.weathercode_to_text(weather_data.get('weathercode', 0)) weather_desc = self.weathercode_to_text(weather_data.get('weathercode', 0))
if force_full_update or not self.last_displayed_data or \ if force_full_update or not self.last_displayed_data or \
self.last_displayed_data.get('weathercode') != weather_data.get('weathercode'): self.last_displayed_data.get('weathercode') != weather_data.get('weathercode'):
@ -228,8 +201,6 @@ class WeatherDisplay:
update_regions.append((0, center_y + 12, 16, center_y + 20)) update_regions.append((0, center_y + 12, 16, center_y + 20))
# Display current time
current_time_tuple = time.localtime(time.time()) current_time_tuple = time.localtime(time.time())
current_time = f"{current_time_tuple[3]:02d}:{current_time_tuple[4]:02d}:{current_time_tuple[5]:02d}" current_time = f"{current_time_tuple[3]:02d}:{current_time_tuple[4]:02d}:{current_time_tuple[5]:02d}"
if current_time and (force_full_update or not self.last_displayed_data or \ if current_time and (force_full_update or not self.last_displayed_data or \
@ -237,22 +208,17 @@ class WeatherDisplay:
self.display.text(current_time, text_x, center_y + 24) self.display.text(current_time, text_x, center_y + 24)
update_regions.append((0, center_y + 24, 16, center_y + 32)) update_regions.append((0, center_y + 24, 16, center_y + 32))
# Store current data for future comparison
self.last_displayed_data = dict(weather_data) self.last_displayed_data = dict(weather_data)
self.last_displayed_data['view_type'] = 'simple' self.last_displayed_data['view_type'] = 'simple'
# Update display - either full or partial updates
if force_full_update: if force_full_update:
self.display.show() self.display.show()
else: else:
# Merge overlapping regions for more efficient updates
if update_regions: if update_regions:
# Simple approach: just update the full range covered by all regions
min_y = min(region[1] for region in update_regions) min_y = min(region[1] for region in update_regions)
max_y = max(region[3] for region in update_regions) max_y = max(region[3] for region in update_regions)
self.display.show(0, min_y, 16, max_y) self.display.show(0, min_y, 16, max_y)
else: else:
# No changes detected, no need to update
pass pass
def reset_display(self): def reset_display(self):