diff --git a/main.py b/main.py index 59c9a12..6cae175 100644 --- a/main.py +++ b/main.py @@ -33,24 +33,22 @@ def get_weather_data(url): pass - - # Global variables latitude = 50.9097 longitude = -1.4043 today_forecast = None current_view = "simple" -station = None # Global WiFi station -display = None # Global display object -weather_disp = None # Global weather display object -setup_complete = False # Flag to indicate if setup is complete -weather_update_in_progress = False # Flag to prevent multiple concurrent updates +station = None +display = None +weather_disp = None +setup_complete = False +weather_update_in_progress = False # WiFi credentials SSID = 'octopod' PASSWORD = 'amniotic-duo-portfolio' -# Event flags for button presses + update_requested = False view_change_requested = False @@ -63,10 +61,10 @@ def ensure_wifi_connected(): print("WiFi disconnected, reconnecting...") station.connect(SSID, PASSWORD) - # Wait for connection with timeout + start_time = time.time() 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") return False time.sleep(1) @@ -138,7 +136,6 @@ def update_weather(): def trigger_weather_update(): _thread.start_new_thread(update_weather, ()) -# Update display based on current data def update_display(): 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] def weekday(year, month, day): - # Zeller’s congruence (0=Saturday, 1=Sunday, ..., 6=Friday) if month < 3: month += 12 year -= 1 @@ -175,13 +171,12 @@ def last_sunday(year, month): """Return (day, hour) of the last Sunday in a given month.""" dim = days_in_month(year, month) for d in range(dim, dim - 7, -1): - if weekday(year, month, d) == 1: # Sunday + if weekday(year, month, d) == 1: return d - return None # should never happen + return None def is_dst(year, month, day, hour=12): """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) 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 -# Main loop (synchronous version) def main_loop(): global setup_complete, update_requested, view_change_requested - # Wait for setup to complete before proceeding while not setup_complete: time.sleep(0.1) print("Main loop starting...") display.auto_text("Ready. Press KEY0 to update") - update_weather() # initial call + update_weather() last_weather = time.ticks_ms() - WEATHER_MS = 10 * 60 * 1000 # 10 min in milliseconds + WEATHER_MS = 10 * 60 * 1000 while True: update_display() - - # Has 10 minutes elapsed? if time.ticks_diff(time.ticks_ms(), last_weather) >= WEATHER_MS: update_weather() last_weather = time.ticks_ms() @@ -225,13 +216,12 @@ def main_loop(): def setup(): global station, display, weather_disp, setup_complete - # --- WiFi Connection Setup --- station = network.WLAN(network.STA_IF) station.active(True) station.connect(SSID, PASSWORD) print("Connecting to WiFi...") - timeout = 15 # Extended timeout + timeout = 15 start_time = time.time() while not station.isconnected(): if time.time() - start_time > timeout: @@ -240,14 +230,11 @@ def setup(): time.sleep(1) if station.isconnected(): - ip = station.ifconfig()[0] # Standard way to get IP + ip = station.ifconfig()[0] print("Connected to WiFi! IP address:", ip) - - # Set up the RTC using NTP rtc = RTC() try: print("Syncing time with NTP server...") - # Retry NTP time sync for _ in range(3): try: ntptime.settime() @@ -256,14 +243,11 @@ def setup(): print("NTP retry...") time.sleep(1) - # Get current time from RTC current_time = list(rtc.datetime()) year = current_time[0] month = current_time[1] day = current_time[2] hour = current_time[4] - - # Adjust for UK time (UTC+0/+1) if is_dst(year, month, day, hour): print("British Summer Time (BST) is active - adding 1 hour") current_time[4] += 1 @@ -271,7 +255,6 @@ def setup(): current_time[4] -= 24 current_time[2] += 1 - # Update RTC with the adjusted time rtc.datetime(tuple(current_time)) print("RTC set with UK local time:", rtc.datetime()) except OSError as e: @@ -280,20 +263,13 @@ def setup(): print("WiFi connection not established. Restart and try again.") raise SystemExit - # Initialize display display = get() weather_disp = weather_display.WeatherDisplay(display) - - # Setup button interrupts button0 = Pin(display.KEY0, Pin.IN, Pin.PULL_UP) button1 = Pin(display.KEY1, Pin.IN, Pin.PULL_UP) button0.irq(trigger=Pin.IRQ_FALLING, handler=detailed_view) button1.irq(trigger=Pin.IRQ_FALLING, handler=simple_view) - - # Display initial message display.auto_text("Starting weather display...") - - # Set flag to indicate setup is complete setup_complete = True print("Setup complete!") @@ -311,16 +287,13 @@ def simple_view(pin): # Main function def main(): - # Run setup setup() - - # Start the main loop main_loop() while True: time.sleep(1) -# Run the main program + if __name__ == "__main__": main() \ No newline at end of file diff --git a/weather_display.py b/weather_display.py index 0d97757..12994ae 100644 --- a/weather_display.py +++ b/weather_display.py @@ -1,5 +1,4 @@ import time -import uasyncio as asyncio class WeatherDisplay: @@ -11,7 +10,6 @@ class WeatherDisplay: self.time_thread = None def weathercode_to_text(self, weathercode): - # More detailed weather code interpretations if weathercode == 0: return 'Clear Sky' elif weathercode == 1: @@ -52,14 +50,11 @@ class WeatherDisplay: # Create a clean buffer but don't send to display yet self.display.fill(self.display.black) - # Text starts at left padding text_x = self.text_padding text_y = 8 - # Track regions that need updating update_regions = [] - # Display temperature 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 \ 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) update_regions.append((0, text_y, 16, text_y + 8)) - # Display weather description weathercode = weather_data.get('weathercode', 0) weather_text = self.weathercode_to_text(weathercode) 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) 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" if force_full_update or not self.last_displayed_data or \ self.last_displayed_data.get('precip_mm') != weather_data.get('precip_mm'): self.display.text(precip_text, text_x, text_y + 24) update_regions.append((0, text_y + 24, 16, text_y + 32)) - # Display date date_text = weather_data.get('date', '') if date_text and len(date_text) >= 10: year = date_text[0:4] @@ -94,28 +86,22 @@ class WeatherDisplay: self.display.text(f"Date: {date_text}", text_x, text_y + 36) 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['view_type'] = 'detailed' - # Update display - either full or partial updates if force_full_update: self.display.show() else: - # Merge overlapping regions for more efficient updates if update_regions: - # Simple approach: just update the full range covered by all regions min_y = min(region[1] for region in update_regions) max_y = max(region[3] for region in update_regions) self.display.show(0, min_y, 16, max_y) else: - # No changes detected, no need to update pass def display_weather(self, weather_data): """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: d = weather_data['daily'] weather_data = { @@ -140,7 +126,6 @@ class WeatherDisplay: text_y = 8 update_regions = [] - # Temperature temp_text = f"Temp: {weather_data.get('max_temp', 0):.0f}/" \ f"{weather_data.get('min_temp', 0):.0f}C" if (force_full_update @@ -150,7 +135,6 @@ class WeatherDisplay: self.display.text(temp_text, text_x, text_y) update_regions.append((text_y, text_y + 8)) - # Description code = weather_data.get('weathercode', 0) desc = self.weathercode_to_text(code) if (force_full_update @@ -159,7 +143,6 @@ class WeatherDisplay: self.display.text(desc, text_x, text_y + 12) update_regions.append((text_y + 12, text_y + 20)) - # Precip precip = f"Precip: {weather_data.get('precip_mm', 0):.1f}mm" if (force_full_update 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) update_regions.append((text_y + 24, text_y + 32)) - # Date (YYYY-MM-DD → DD-MM-YYYY) raw_date = weather_data.get('date', '') if raw_date and len(raw_date) >= 10: 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) update_regions.append((text_y + 36, text_y + 44)) - # Save for next time self.last_displayed_data = dict(weather_data) self.last_displayed_data['view_type'] = 'detailed' - # If full, just show everything if force_full_update: self.display.show() return - # Otherwise do a partial update over all pages if update_regions: min_y = min(r[0] for r in update_regions) max_y = max(r[1] for r in update_regions) page_count = self.display.height // 8 self.display.show(1, min_y, page_count, max_y) - # else: nothing changed @@ -203,24 +181,19 @@ class WeatherDisplay: if force_full_update: self.display.clear() else: - # Create a clean buffer but don't send to display yet self.display.fill(self.display.black) - # Text starts at left padding text_x = self.text_padding center_y = self.display.height // 2 - # Track regions that need updating update_regions = [] - # Display temperature large temp_text = f"{weather_data.get('current_temp', 0):.0f}C" if force_full_update or not self.last_displayed_data or \ self.last_displayed_data.get('current_temp') != weather_data.get('current_temp'): 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 - # Display description weather_desc = self.weathercode_to_text(weather_data.get('weathercode', 0)) if force_full_update or not self.last_displayed_data or \ 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)) - - # Display current 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}" 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) 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['view_type'] = 'simple' - # Update display - either full or partial updates if force_full_update: self.display.show() else: - # Merge overlapping regions for more efficient updates if update_regions: - # Simple approach: just update the full range covered by all regions min_y = min(region[1] for region in update_regions) max_y = max(region[3] for region in update_regions) self.display.show(0, min_y, 16, max_y) else: - # No changes detected, no need to update pass def reset_display(self):