v.0.6.0 redesign weather checker wlan

This commit is contained in:
tiijay
2025-11-13 12:46:00 +01:00
parent bf1a3ac36c
commit 794e296614
7 changed files with 129 additions and 77 deletions

View File

@@ -0,0 +1,4 @@
from .weather import Weather, AirQuality, Condition, CurrentCondition
from .location import Location
__all__ = ["Weather","AirQuality","Condition","CurrentCondition","Location"]

View File

@@ -30,6 +30,13 @@ font_5x7 = {
'X': [0x11, 0x11, 0x0A, 0x04, 0x0A, 0x11, 0x11], # Width: 5
'Y': [0x11, 0x11, 0x0A, 0x04, 0x04, 0x04, 0x04], # Width: 5
'Z': [0x1F, 0x01, 0x02, 0x04, 0x08, 0x10, 0x1F], # Width: 5
# German Uppercase Umlaute
'Ä': [0x0A, 0x00, 0x0E, 0x11, 0x1F, 0x11, 0x11], # Width: 5 (A Umlaut)
'Ö': [0x0A, 0x00, 0x0E, 0x11, 0x11, 0x11, 0x0E], # Width: 5 (O Umlaut)
'Ü': [0x0A, 0x00, 0x11, 0x11, 0x11, 0x11, 0x0E], # Width: 5 (U Umlaut)
'': [0x0E, 0x11, 0x11, 0x1E, 0x11, 0x11, 0x1E], # Width: 5 (Eszett - scharfes S)
# Lowercase letters (a-z) - Optimized for compact display
'a': [0x00, 0x00, 0x0E, 0x01, 0x0F, 0x11, 0x0F], # Width: 4
'b': [0x10, 0x10, 0x16, 0x19, 0x11, 0x11, 0x1E], # Width: 5
@@ -59,7 +66,13 @@ font_5x7 = {
'x': [0x00, 0x00, 0x11, 0x0A, 0x04, 0x0A, 0x11], # Width: 5
'y': [0x00, 0x00, 0x11, 0x11, 0x0F, 0x01, 0x0E], # Width: 5
'z': [0x00, 0x00, 0x1F, 0x02, 0x04, 0x08, 0x1F], # Width: 5
# Numbers (0-9) - Optimized for consistent width
# German Lowercase Umlaute
'ä': [0x0A, 0x00, 0x0E, 0x01, 0x0F, 0x11, 0x0F], # Width: 4 (a Umlaut)
'ö': [0x0A, 0x00, 0x0E, 0x11, 0x11, 0x11, 0x0E], # Width: 4 (o Umlaut)
'ü': [0x0A, 0x00, 0x11, 0x11, 0x11, 0x13, 0x0D], # Width: 5 (u Umlaut)
'ß': [0x00, 0x00, 0x0E, 0x11, 0x1E, 0x11, 0x1E], # Width: 5 (Eszett - scharfes S)
# Numbers (0-9) - Optimized for consistent width
'0': [0x0E, 0x11, 0x13, 0x15, 0x19, 0x11, 0x0E], # Width: 5
'1': [0x02, 0x06, 0x02, 0x02, 0x02, 0x02, 0x07], # Width: 3
# '1': [0x04, 0x0C, 0x04, 0x04, 0x04, 0x04, 0x0E], # Width: 3

View File

@@ -1,39 +1,69 @@
from app.classes.weather import Weather
import urequests # type: ignore
import json
from app.classes import Weather, Location, CurrentCondition
from app.display.neopixel_64x64 import NeoPixel_64x64
from app.web.wlan import Wlan
import app.utils.colors as colors
import app.display.fonts as fonts
from app.utils import URLEncoder
API_KEY = '3545ce42d0ba436e8dc164532250410'
ACTUAL_WEATHER_URL = 'http://api.weatherapi.com/v1/current.json?key={API_KEY}&q={city}&aqi=yes&lang={lang}'
class Weather_Checker():
wlan: Wlan
def __init__(self, display: NeoPixel_64x64):
def __init__(self, city: str, display: NeoPixel_64x64):
self.display = display
self.city = URLEncoder.encode(city) # url_encode
self.display.set_font( font=fonts.font_5x7)
def setup_wlan(self):
self.wlan = Wlan('WOKWI-Guest', '12345678')
def mock_weather_data(self):
filepath = 'restapi/mock-weather.json'
try:
with open(filepath, 'r', encoding='utf-8') as file:
return json.load(file)
except OSError: # Use OSError instead of FileNotFoundError
print(f"Error: File '{filepath}' not found.")
return None
except FileNotFoundError:
print(f"Error: File '{filepath}' not found.")
return None
except json.JSONDecodeError as e:
print(f"Error: Invalid JSON in '{filepath}': {e}")
return None
def actual_weather(self, city='grosshansdorf', lang='de', test_mode=False) -> Weather:
weather_url = ACTUAL_WEATHER_URL.format(API_KEY=API_KEY, city=city, lang=lang)
if not test_mode:
print(f'query: {weather_url}')
r = urequests.get(weather_url)
print('Status-Code:', r.status_code)
json_resp = r.json()
r.close()
else:
print('Status-Code: test_mode')
# json_resp = WEATHER_QUERY_MOCK
json_resp = self.mock_weather_data()
loc = Location(**json_resp['location'])
cc = CurrentCondition(**json_resp['current'])
weather = Weather(location=loc, current=cc)
return weather
def check(self, test_mode: bool = False):
try:
self.display.clear()
self.display.write_text('search wlan', 0, 0, color=colors.WHITE)
self.setup_wlan()
self.display.clear()
# self.display.set_font(fonts.font_3x5)
ypos = 0
self.display.write_text('wlan connected', 0, ypos, color=colors.RAINBOW[0])
ypos += self.display.font_height + 1
self.display.write_text('querying', 0, ypos, color=colors.RAINBOW[1])
ypos += self.display.font_height + 1
self.display.write_text('weather data', 0, ypos, color=colors.RAINBOW[2])
w_resp: Weather = self.wlan.actual_weather(lang='de', test_mode=test_mode)
self.display.clear()
w_resp: Weather = self.actual_weather(city=self.city, lang='de', test_mode=test_mode)
ypos = 0
self.display.write_text(f'{str(w_resp.location.name)}', 0, ypos, color=colors.RAINBOW[0])

View File

@@ -3,3 +3,4 @@ from .colors import *
from .time_utils import *
from .font_utils import *
from .math_utils import *
from .url_encode import URLEncoder

47
app/utils/url_encode.py Normal file
View File

@@ -0,0 +1,47 @@
class URLEncoder:
@staticmethod
def encode(string):
"""URL encode a string without using string methods"""
result = []
safe_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~"
for char in string:
if char in safe_chars:
result.append(char)
else:
# result.append('%' + format(ord(char), '02X'))
# Converted to % formatting:
result.append("%%%02X" % ord(char))
return ''.join(result)
@staticmethod
def encode_plus(string):
encoded = URLEncoder.encode(string)
return encoded.replace('%20', '+')
@staticmethod
def decode(encoded_string):
result = []
i = 0
while i < len(encoded_string):
if encoded_string[i] == '%' and i + 2 < len(encoded_string):
hex_code = encoded_string[i+1:i+3]
result.append(chr(int(hex_code, 16)))
i += 3
elif encoded_string[i] == '+':
result.append(' ')
i += 1
else:
result.append(encoded_string[i])
i += 1
return ''.join(result)
@staticmethod
def encode_params(params):
pairs = []
for key, value in params.items():
pairs.append(f"{URLEncoder.encode(str(key))}={URLEncoder.encode(str(value))}")
return '&'.join(pairs)

View File

@@ -1,66 +1,20 @@
import time
import network # type: ignore
import urequests # type: ignore
from ..classes.location import Location
from ..classes.weather import CurrentCondition, Weather
import json
API_KEY = '3545ce42d0ba436e8dc164532250410'
ACTUAL_WEATHER_URL = 'http://api.weatherapi.com/v1/current.json?key={API_KEY}&q={city}&aqi=yes&lang={lang}'
class Wlan:
def __init__(self, ssid, password):
def __init__(self):
print("Wlan::__init__")
self.wlan = network.WLAN(network.STA_IF)
self.ssid = ssid
self.password = password
def mock_weather_data(self):
filepath = 'restapi/mock-weather.json'
try:
with open(filepath, 'r', encoding='utf-8') as file:
return json.load(file)
except OSError: # Use OSError instead of FileNotFoundError
print(f"Error: File '{filepath}' not found.")
return None
except FileNotFoundError:
print(f"Error: File '{filepath}' not found.")
return None
except json.JSONDecodeError as e:
print(f"Error: Invalid JSON in '{filepath}': {e}")
return None
def connect(self):
def connect(self, ssid:str, password:str):
self.wlan.active(True)
self.wlan.connect(self.ssid, self.password)
self.wlan.connect(ssid, password)
while not self.wlan.isconnected:
print('connecting, please wait ...')
time.sleep(1)
print('connected! IP=', self.wlan.ifconfig()[0])
def actual_weather(self, city='grosshansdorf', lang='de', test_mode=False) -> Weather:
weather_url = ACTUAL_WEATHER_URL.format(API_KEY=API_KEY, city=city, lang=lang)
if not test_mode:
if not self.wlan.isconnected:
self.connect()
print(f'query: {weather_url}')
r = urequests.get(weather_url)
print('Status-Code:', r.status_code)
json_resp = r.json()
r.close()
else:
print('Status-Code: test_mode')
# json_resp = WEATHER_QUERY_MOCK
json_resp = self.mock_weather_data()
loc = Location(**json_resp['location'])
cc = CurrentCondition(**json_resp['current'])
weather = Weather(location=loc, current=cc)
return weather

View File

@@ -1,20 +1,23 @@
from app.display.neopixel_64x64 import NeoPixel_64x64
from app.tryout import Font_Checker, Weather_Checker, Emoji_Checker
from app.utils import show_system_load
from app.web.wlan import Wlan
# Programm Startpunkt
if __name__ == '__main__':
display = NeoPixel_64x64()
wlan: Wlan = Wlan( )
wlan.connect( ssid="Wokwi-Wlan", password="12345678")
# font_checker : Font_Checker = Font_Checker( display)
# font_checker.fonts_check(pretty=False)
# tryout.emojis_check(display)
emoji_checker : Emoji_Checker = Emoji_Checker(display)
emoji_checker.check()
# emoji_checker : Emoji_Checker = Emoji_Checker(display)
# emoji_checker.check()
# tryout.weather_check(display, test_mode=False)
weather_checker: Weather_Checker = Weather_Checker( display)
weather_checker: Weather_Checker = Weather_Checker( city="München", display=display)
weather_checker.check(test_mode=False)
show_system_load()