v.0.6.0 redesign weather checker wlan
This commit is contained in:
@@ -0,0 +1,4 @@
|
|||||||
|
from .weather import Weather, AirQuality, Condition, CurrentCondition
|
||||||
|
from .location import Location
|
||||||
|
|
||||||
|
__all__ = ["Weather","AirQuality","Condition","CurrentCondition","Location"]
|
||||||
@@ -30,6 +30,13 @@ font_5x7 = {
|
|||||||
'X': [0x11, 0x11, 0x0A, 0x04, 0x0A, 0x11, 0x11], # Width: 5
|
'X': [0x11, 0x11, 0x0A, 0x04, 0x0A, 0x11, 0x11], # Width: 5
|
||||||
'Y': [0x11, 0x11, 0x0A, 0x04, 0x04, 0x04, 0x04], # Width: 5
|
'Y': [0x11, 0x11, 0x0A, 0x04, 0x04, 0x04, 0x04], # Width: 5
|
||||||
'Z': [0x1F, 0x01, 0x02, 0x04, 0x08, 0x10, 0x1F], # 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
|
# Lowercase letters (a-z) - Optimized for compact display
|
||||||
'a': [0x00, 0x00, 0x0E, 0x01, 0x0F, 0x11, 0x0F], # Width: 4
|
'a': [0x00, 0x00, 0x0E, 0x01, 0x0F, 0x11, 0x0F], # Width: 4
|
||||||
'b': [0x10, 0x10, 0x16, 0x19, 0x11, 0x11, 0x1E], # Width: 5
|
'b': [0x10, 0x10, 0x16, 0x19, 0x11, 0x11, 0x1E], # Width: 5
|
||||||
@@ -59,6 +66,12 @@ font_5x7 = {
|
|||||||
'x': [0x00, 0x00, 0x11, 0x0A, 0x04, 0x0A, 0x11], # Width: 5
|
'x': [0x00, 0x00, 0x11, 0x0A, 0x04, 0x0A, 0x11], # Width: 5
|
||||||
'y': [0x00, 0x00, 0x11, 0x11, 0x0F, 0x01, 0x0E], # Width: 5
|
'y': [0x00, 0x00, 0x11, 0x11, 0x0F, 0x01, 0x0E], # Width: 5
|
||||||
'z': [0x00, 0x00, 0x1F, 0x02, 0x04, 0x08, 0x1F], # Width: 5
|
'z': [0x00, 0x00, 0x1F, 0x02, 0x04, 0x08, 0x1F], # Width: 5
|
||||||
|
# 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
|
# Numbers (0-9) - Optimized for consistent width
|
||||||
'0': [0x0E, 0x11, 0x13, 0x15, 0x19, 0x11, 0x0E], # Width: 5
|
'0': [0x0E, 0x11, 0x13, 0x15, 0x19, 0x11, 0x0E], # Width: 5
|
||||||
'1': [0x02, 0x06, 0x02, 0x02, 0x02, 0x02, 0x07], # Width: 3
|
'1': [0x02, 0x06, 0x02, 0x02, 0x02, 0x02, 0x07], # Width: 3
|
||||||
|
|||||||
@@ -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.display.neopixel_64x64 import NeoPixel_64x64
|
||||||
from app.web.wlan import Wlan
|
|
||||||
import app.utils.colors as colors
|
import app.utils.colors as colors
|
||||||
import app.display.fonts as fonts
|
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():
|
class Weather_Checker():
|
||||||
wlan: Wlan
|
|
||||||
|
|
||||||
def __init__(self, display: NeoPixel_64x64):
|
def __init__(self, city: str, display: NeoPixel_64x64):
|
||||||
self.display = display
|
self.display = display
|
||||||
|
self.city = URLEncoder.encode(city) # url_encode
|
||||||
self.display.set_font( font=fonts.font_5x7)
|
self.display.set_font( font=fonts.font_5x7)
|
||||||
|
|
||||||
def setup_wlan(self):
|
def mock_weather_data(self):
|
||||||
self.wlan = Wlan('WOKWI-Guest', '12345678')
|
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):
|
def check(self, test_mode: bool = False):
|
||||||
try:
|
try:
|
||||||
self.display.clear()
|
self.display.clear()
|
||||||
self.display.write_text('search wlan', 0, 0, color=colors.WHITE)
|
|
||||||
|
|
||||||
self.setup_wlan()
|
w_resp: Weather = self.actual_weather(city=self.city, lang='de', test_mode=test_mode)
|
||||||
|
|
||||||
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()
|
|
||||||
|
|
||||||
ypos = 0
|
ypos = 0
|
||||||
self.display.write_text(f'{str(w_resp.location.name)}', 0, ypos, color=colors.RAINBOW[0])
|
self.display.write_text(f'{str(w_resp.location.name)}', 0, ypos, color=colors.RAINBOW[0])
|
||||||
|
|||||||
@@ -3,3 +3,4 @@ from .colors import *
|
|||||||
from .time_utils import *
|
from .time_utils import *
|
||||||
from .font_utils import *
|
from .font_utils import *
|
||||||
from .math_utils import *
|
from .math_utils import *
|
||||||
|
from .url_encode import URLEncoder
|
||||||
47
app/utils/url_encode.py
Normal file
47
app/utils/url_encode.py
Normal 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)
|
||||||
|
|
||||||
@@ -1,66 +1,20 @@
|
|||||||
import time
|
import time
|
||||||
import network # type: ignore
|
import network # type: ignore
|
||||||
import urequests # type: ignore
|
|
||||||
from ..classes.location import Location
|
|
||||||
from ..classes.weather import CurrentCondition, Weather
|
|
||||||
import json
|
|
||||||
|
|
||||||
API_KEY = '3545ce42d0ba436e8dc164532250410'
|
API_KEY = '3545ce42d0ba436e8dc164532250410'
|
||||||
ACTUAL_WEATHER_URL = 'http://api.weatherapi.com/v1/current.json?key={API_KEY}&q={city}&aqi=yes&lang={lang}'
|
ACTUAL_WEATHER_URL = 'http://api.weatherapi.com/v1/current.json?key={API_KEY}&q={city}&aqi=yes&lang={lang}'
|
||||||
class Wlan:
|
class Wlan:
|
||||||
def __init__(self, ssid, password):
|
def __init__(self):
|
||||||
|
print("Wlan::__init__")
|
||||||
self.wlan = network.WLAN(network.STA_IF)
|
self.wlan = network.WLAN(network.STA_IF)
|
||||||
self.ssid = ssid
|
|
||||||
self.password = password
|
|
||||||
|
|
||||||
def mock_weather_data(self):
|
def connect(self, ssid:str, password:str):
|
||||||
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):
|
|
||||||
|
|
||||||
self.wlan.active(True)
|
self.wlan.active(True)
|
||||||
self.wlan.connect(self.ssid, self.password)
|
self.wlan.connect(ssid, password)
|
||||||
|
|
||||||
while not self.wlan.isconnected:
|
while not self.wlan.isconnected:
|
||||||
print('connecting, please wait ...')
|
print('connecting, please wait ...')
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
print('connected! IP=', self.wlan.ifconfig()[0])
|
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
|
|
||||||
|
|||||||
9
main.py
9
main.py
@@ -1,20 +1,23 @@
|
|||||||
from app.display.neopixel_64x64 import NeoPixel_64x64
|
from app.display.neopixel_64x64 import NeoPixel_64x64
|
||||||
from app.tryout import Font_Checker, Weather_Checker, Emoji_Checker
|
from app.tryout import Font_Checker, Weather_Checker, Emoji_Checker
|
||||||
from app.utils import show_system_load
|
from app.utils import show_system_load
|
||||||
|
from app.web.wlan import Wlan
|
||||||
|
|
||||||
# Programm Startpunkt
|
# Programm Startpunkt
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
display = NeoPixel_64x64()
|
display = NeoPixel_64x64()
|
||||||
|
wlan: Wlan = Wlan( )
|
||||||
|
wlan.connect( ssid="Wokwi-Wlan", password="12345678")
|
||||||
|
|
||||||
# font_checker : Font_Checker = Font_Checker( display)
|
# font_checker : Font_Checker = Font_Checker( display)
|
||||||
# font_checker.fonts_check(pretty=False)
|
# font_checker.fonts_check(pretty=False)
|
||||||
|
|
||||||
# tryout.emojis_check(display)
|
# tryout.emojis_check(display)
|
||||||
emoji_checker : Emoji_Checker = Emoji_Checker(display)
|
# emoji_checker : Emoji_Checker = Emoji_Checker(display)
|
||||||
emoji_checker.check()
|
# emoji_checker.check()
|
||||||
|
|
||||||
# tryout.weather_check(display, test_mode=False)
|
# 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)
|
weather_checker.check(test_mode=False)
|
||||||
|
|
||||||
show_system_load()
|
show_system_load()
|
||||||
|
|||||||
Reference in New Issue
Block a user