diff --git a/config.yaml b/config.yaml index ef8c7d18..5575247b 100644 --- a/config.yaml +++ b/config.yaml @@ -33,6 +33,21 @@ config: # Value must be 'controller/fan' e.g. 'nct6798/fan2'. Use configuration wizard for help in selection CPU_FAN: AUTO + # Address used for ping sensor. Can be internal/external IP (e.g. 8.8.8.8 or 192.168.0.1) or hostname (google.com) + PING: 8.8.8.8 + + # Weather data with OpenWeatherMap API. Only useful if you want to use a theme that displays it + # Location from which to display the weather. Use for example https://www.latlong.net/ to get latitude/longitude + WEATHER_LATITUDE: 45.75 + WEATHER_LONGITUDE: 4.85 + # OpenWeatherMap API KEY. Can be obtained by creating a free account on https://home.openweathermap.org/users/sign_up. + # You need to subscribe to the 3.0 OneCallAPI that has 1000 free daily calls + WEATHER_API_KEY: "" + # Units used to display temperatures (metric - °C, imperial - °F, standard - °K) + WEATHER_UNITS: metric + # Language is used by the API. Find more here https://openweathermap.org/api/one-call-3#multi + WEATHER_LANGUAGE: en + display: # Display revision: # - A for Turing 3.5" and UsbPCMonitor 3.5"/5" diff --git a/library/lcd/lcd_comm.py b/library/lcd/lcd_comm.py index 5a79408b..10f14b17 100644 --- a/library/lcd/lcd_comm.py +++ b/library/lcd/lcd_comm.py @@ -235,6 +235,10 @@ def DisplayText( assert len(text) > 0, 'Text must not be empty' assert font_size > 0, "Font size must be > 0" + # If only width is specified, assume height based on font size (one-line text) + if width > 0 and height == 0: + height = font_size + if background_image is None: # A text bitmap is created with max width/height by default : text with solid background text_image = Image.new( diff --git a/library/scheduler.py b/library/scheduler.py index 4d03c62a..175aa6e8 100644 --- a/library/scheduler.py +++ b/library/scheduler.py @@ -121,7 +121,7 @@ def CPUFanSpeed(): @async_job("GPU_Stats") -@schedule(timedelta(seconds=config.THEME_DATA['STATS']['GPU'].get("INTERVAL", 0)).total_seconds()) +@schedule(timedelta(seconds=config.THEME_DATA['STATS'].get('GPU', {}).get("INTERVAL", 0)).total_seconds()) def GpuStats(): """ Refresh the GPU Stats """ # logger.debug("Refresh GPU Stats") @@ -129,45 +129,61 @@ def GpuStats(): @async_job("Memory_Stats") -@schedule(timedelta(seconds=config.THEME_DATA['STATS']['MEMORY'].get("INTERVAL", 0)).total_seconds()) +@schedule(timedelta(seconds=config.THEME_DATA['STATS'].get('MEMORY', {}).get("INTERVAL", 0)).total_seconds()) def MemoryStats(): # logger.debug("Refresh memory stats") stats.Memory.stats() @async_job("Disk_Stats") -@schedule(timedelta(seconds=config.THEME_DATA['STATS']['DISK'].get("INTERVAL", 0)).total_seconds()) +@schedule(timedelta(seconds=config.THEME_DATA['STATS'].get('DISK', {}).get("INTERVAL", 0)).total_seconds()) def DiskStats(): # logger.debug("Refresh disk stats") stats.Disk.stats() @async_job("Net_Stats") -@schedule(timedelta(seconds=config.THEME_DATA['STATS']['NET'].get("INTERVAL", 0)).total_seconds()) +@schedule(timedelta(seconds=config.THEME_DATA['STATS'].get('NET', {}).get("INTERVAL", 0)).total_seconds()) def NetStats(): # logger.debug("Refresh net stats") stats.Net.stats() @async_job("Date_Stats") -@schedule(timedelta(seconds=config.THEME_DATA['STATS']['DATE'].get("INTERVAL", 0)).total_seconds()) +@schedule(timedelta(seconds=config.THEME_DATA['STATS'].get('DATE', {}).get("INTERVAL", 0)).total_seconds()) def DateStats(): # logger.debug("Refresh date stats") stats.Date.stats() + @async_job("SystemUptime_Stats") -@schedule(timedelta(seconds=config.THEME_DATA['STATS']['UPTIME'].get("INTERVAL", 0)).total_seconds()) +@schedule(timedelta(seconds=config.THEME_DATA['STATS'].get('UPTIME', {}).get("INTERVAL", 0)).total_seconds()) def SystemUptimeStats(): # logger.debug("Refresh system uptime stats") stats.SystemUptime.stats() + @async_job("Custom_Stats") -@schedule(timedelta(seconds=config.THEME_DATA['STATS']['CUSTOM'].get("INTERVAL", 0)).total_seconds()) +@schedule(timedelta(seconds=config.THEME_DATA['STATS'].get('CUSTOM', {}).get("INTERVAL", 0)).total_seconds()) def CustomStats(): # print("Refresh custom stats") stats.Custom.stats() +@async_job("Weather_Stats") +@schedule(timedelta(seconds=config.THEME_DATA['STATS'].get('WEATHER', {}).get("INTERVAL", 0)).total_seconds()) +def WeatherStats(): + # logger.debug("Refresh Weather data") + stats.Weather.stats() + + +@async_job("Ping_Stats") +@schedule(timedelta(seconds=config.THEME_DATA['STATS'].get('PING', {}).get("INTERVAL", 0)).total_seconds()) +def PingStats(): + # logger.debug("Refresh Ping data") + stats.Ping.stats() + + @async_job("Queue_Handler") @schedule(timedelta(milliseconds=1).total_seconds()) def QueueHandler(): diff --git a/library/stats.py b/library/stats.py index d904be74..d70413f9 100644 --- a/library/stats.py +++ b/library/stats.py @@ -29,6 +29,8 @@ from typing import List import babel.dates +import requests +from ping3 import ping from psutil._common import bytes2human from uptime import uptime @@ -42,6 +44,7 @@ WLO_CARD = config.CONFIG_DATA["config"].get("WLO", "") HW_SENSORS = config.CONFIG_DATA["config"].get("HW_SENSORS", "AUTO") CPU_FAN = config.CONFIG_DATA["config"].get("CPU_FAN", "AUTO") +PING_DEST = config.CONFIG_DATA["config"].get("PING", "127.0.0.1") if HW_SENSORS == "PYTHON": if platform.system() == 'Windows': @@ -824,3 +827,104 @@ def stats(): theme_data = config.THEME_DATA['STATS']['CUSTOM'][custom_stat].get("LINE_GRAPH", None) if theme_data is not None and last_values is not None: display_themed_line_graph(theme_data=theme_data, values=last_values) + + +class Weather: + @staticmethod + def stats(): + WEATHER_UNITS = {'metric': '°C', 'imperial': '°F', 'standard': '°K'} + + weather_theme_data = config.THEME_DATA['STATS'].get('WEATHER', {}) + wtemperature_theme_data = weather_theme_data.get('TEMPERATURE', {}).get('TEXT', {}) + wfelt_theme_data = weather_theme_data.get('TEMPERATURE_FELT', {}).get('TEXT', {}) + wupdatetime_theme_data = weather_theme_data.get('UPDATE_TIME', {}).get('TEXT', {}) + wdescription_theme_data = weather_theme_data.get('WEATHER_DESCRIPTION', {}).get('TEXT', {}) + whumidity_theme_data = weather_theme_data.get('HUMIDITY', {}).get('TEXT', {}) + + activate = True if wtemperature_theme_data.get("SHOW") or wfelt_theme_data.get( + "SHOW") or wupdatetime_theme_data.get("SHOW") or wdescription_theme_data.get( + "SHOW") or whumidity_theme_data.get("SHOW") else False + + if activate: + temp = None + feel = None + time = None + humidity = None + if HW_SENSORS in ["STATIC", "STUB"]: + temp = "17.5°C" + feel = "(17.2°C)" + desc = "Cloudy" + time = "@15:33" + humidity = "45%" + else: + # API Parameters + lat = config.CONFIG_DATA['config'].get('WEATHER_LATITUDE', "") + lon = config.CONFIG_DATA['config'].get('WEATHER_LONGITUDE', "") + api_key = config.CONFIG_DATA['config'].get('WEATHER_API_KEY', "") + units = config.CONFIG_DATA['config'].get('WEATHER_UNITS', "metric") + lang = config.CONFIG_DATA['config'].get('WEATHER_LANGUAGE', "en") + deg = WEATHER_UNITS.get(units, '°?') + if api_key: + url = f'https://api.openweathermap.org/data/3.0/onecall?lat={lat}&lon={lon}&exclude=minutely,hourly,daily,alerts&appid={api_key}&units={units}&lang={lang}' + try: + response = requests.get(url) + if response.status_code == 200: + data = response.json() + temp = f"{data['current']['temp']:.1f}{deg}" + feel = f"({data['current']['feels_like']:.1f}{deg})" + desc = data['current']['weather'][0]['description'].capitalize() + humidity = f"{data['current']['humidity']:.0f}%" + now = datetime.datetime.now() + time = f"@{now.hour:02d}:{now.minute:02d}" + else: + logger.error(f"Error {response.status_code} fetching OpenWeatherMap API:") + # logger.error(f"Response content: {response.content}") + # logger.error(response.text) + desc = response.json().get('message') + except Exception as e: + logger.error(f"Error fetching OpenWeatherMap API: {str(e)}") + desc = "Error fetching OpenWeatherMap API" + else: + logger.warning("No OpenWeatherMap API key provided in config.yaml") + desc = "No OpenWeatherMap API key" + + if activate: + # Display Temperature + display_themed_value(theme_data=wtemperature_theme_data, value=temp) + # Display Temperature Felt + display_themed_value(theme_data=wfelt_theme_data, value=feel) + # Display Update Time + display_themed_value(theme_data=wupdatetime_theme_data, value=time) + # Display Humidity + display_themed_value(theme_data=whumidity_theme_data, value=humidity) + # Display Weather Description (or error message) + display_themed_value(theme_data=wdescription_theme_data, value=desc) + + +class Ping: + last_values_ping = [] + + @classmethod + def stats(cls): + theme_data = config.THEME_DATA['STATS']['PING'] + + delay = ping(dest_addr=PING_DEST, unit="ms") + + save_last_value(delay, cls.last_values_ping, + theme_data['LINE_GRAPH'].get("HISTORY_SIZE", DEFAULT_HISTORY_SIZE)) + # logger.debug(f"Ping delay: {delay}ms") + + display_themed_progress_bar(theme_data['GRAPH'], delay) + display_themed_radial_bar( + theme_data=theme_data['RADIAL'], + value=int(delay), + unit="ms", + min_size=6 + ) + display_themed_value( + theme_data=theme_data['TEXT'], + value=int(delay), + unit="ms", + min_size=6 + ) + display_themed_line_graph(theme_data['LINE_GRAPH'], cls.last_values_ping) diff --git a/main.py b/main.py index 66c920d3..fbe0cf0f 100755 --- a/main.py +++ b/main.py @@ -214,6 +214,8 @@ def on_win32_wm_event(hWnd, msg, wParam, lParam): scheduler.DateStats() scheduler.SystemUptimeStats() scheduler.CustomStats() + scheduler.WeatherStats() + scheduler.PingStats() scheduler.QueueHandler() if tray_icon and platform.system() == "Darwin": # macOS-specific diff --git a/requirements.txt b/requirements.txt index b32ba96a..27b2d88f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,6 +8,8 @@ ruamel.yaml~=0.18.6 # For configuration editor sv-ttk~=2.6.0 # Tk Sun Valley theme for configuration editor tkinter-tooltip~=3.1.2 # Tooltips for configuration editor uptime~=3.0.1 # For System Uptime +requests~=2.32.3 # HTTP library +ping3~=4.0.8 # ICMP ping implementation using raw socket # Image generation Pillow~=10.4.0; python_version < "3.9" # For Python 3.8 max. diff --git a/res/themes/ColoredFlat/background.png b/res/themes/ColoredFlat/background.png new file mode 100644 index 00000000..2e6b4ed1 Binary files /dev/null and b/res/themes/ColoredFlat/background.png differ diff --git a/res/themes/ColoredFlat/background.xcf b/res/themes/ColoredFlat/background.xcf new file mode 100644 index 00000000..e8b0ba5f Binary files /dev/null and b/res/themes/ColoredFlat/background.xcf differ diff --git a/res/themes/ColoredFlat/preview.png b/res/themes/ColoredFlat/preview.png new file mode 100644 index 00000000..de9411c7 Binary files /dev/null and b/res/themes/ColoredFlat/preview.png differ diff --git a/res/themes/ColoredFlat/telecharger-arrow.png b/res/themes/ColoredFlat/telecharger-arrow.png new file mode 100644 index 00000000..691717a4 Binary files /dev/null and b/res/themes/ColoredFlat/telecharger-arrow.png differ diff --git a/res/themes/ColoredFlat/theme.yaml b/res/themes/ColoredFlat/theme.yaml new file mode 100644 index 00000000..ca56f5e3 --- /dev/null +++ b/res/themes/ColoredFlat/theme.yaml @@ -0,0 +1,567 @@ +--- +author: "@Psykotik" + +display: + DISPLAY_SIZE: 5" + DISPLAY_ORIENTATION: portrait + DISPLAY_RGB_LED: 255, 0, 0 + +static_images: + BACKGROUND: + PATH: background.png + X: 0 + Y: 0 + WIDTH: 480 + HEIGHT: 800 + +static_text: + RAM: + TEXT: /32Gb + X: 120 + Y: 348 + FONT: jetbrains-mono/JetBrainsMono-Bold.ttf + FONT_SIZE: 22 + FONT_COLOR: 247, 227, 227 + BACKGROUND_IMAGE: background.png + +STATS: + CPU: + PERCENTAGE: + INTERVAL: 1 + TEXT: + SHOW: true + SHOW_UNIT: true + X: 110 + Y: 127 + FONT: jetbrains-mono/JetBrainsMono-Bold.ttf + FONT_SIZE: 35 + FONT_COLOR: 247, 227, 227 + BACKGROUND_IMAGE: background.png + GRAPH: + SHOW: false + X: 40 + Y: 210 + WIDTH: 160 + HEIGHT: 15 + MIN_VALUE: 0 + MAX_VALUE: 100 + BAR_COLOR: 247, 227, 227 + BAR_OUTLINE: true + BACKGROUND_IMAGE: background.png + LINE_GRAPH: + SHOW: true + X: 30 + Y: 160 + WIDTH: 180 + HEIGHT: 70 + MIN_VALUE: 0 + MAX_VALUE: 100 + HISTORY_SIZE: 60 + AUTOSCALE: false + LINE_COLOR: 247, 227, 227 + AXIS: true + AXIS_COLOR: 247, 227, 227 + BACKGROUND_IMAGE: background.png + FREQUENCY: + INTERVAL: 1 + TEXT: + SHOW: true + SHOW_UNIT: True + X: 40 + Y: 250 + FONT: roboto/Roboto-Bold.ttf + FONT_SIZE: 15 + FONT_COLOR: 247, 227, 227 + BACKGROUND_IMAGE: background.png + LOAD: + INTERVAL: 1 + ONE: + TEXT: + SHOW: false + SHOW_UNIT: false + X: 110 + Y: 106 + FONT: roboto/Roboto-Bold.ttf + FONT_SIZE: 13 + FONT_COLOR: 200, 200, 200 + BACKGROUND_IMAGE: background.png + FIVE: + TEXT: + SHOW: false + SHOW_UNIT: false + X: 183 + Y: 106 + FONT: roboto/Roboto-Bold.ttf + FONT_SIZE: 13 + FONT_COLOR: 200, 200, 200 + BACKGROUND_IMAGE: background.png + FIFTEEN: + TEXT: + SHOW: False + SHOW_UNIT: false + X: 265 + Y: 106 + FONT: roboto/Roboto-Bold.ttf + FONT_SIZE: 13 + FONT_COLOR: 200, 200, 200 + BACKGROUND_IMAGE: background.png + TEMPERATURE: + INTERVAL: 1 + TEXT: + SHOW: true + SHOW_UNIT: True + X: 110 + Y: 240 + FONT: jetbrains-mono/JetBrainsMono-Bold.ttf + FONT_SIZE: 30 + FONT_COLOR: 247, 227, 227 + BACKGROUND_IMAGE: background.png + GPU: + INTERVAL: 1 + PERCENTAGE: + TEXT: + SHOW: true + SHOW_UNIT: True + X: 345 + Y: 127 + FONT: jetbrains-mono/JetBrainsMono-Bold.ttf + FONT_SIZE: 35 + FONT_COLOR: 247, 227, 227 + BACKGROUND_IMAGE: background.png + GRAPH: + SHOW: false + X: 280 + Y: 210 + WIDTH: 160 + HEIGHT: 15 + MIN_VALUE: 0 + MAX_VALUE: 100 + BAR_COLOR: 247, 227, 227 + BAR_OUTLINE: true + BACKGROUND_IMAGE: background.png + LINE_GRAPH: + SHOW: true + X: 270 + Y: 160 + WIDTH: 180 + HEIGHT: 70 + MIN_VALUE: 0 + MAX_VALUE: 100 + HISTORY_SIZE: 60 + AUTOSCALE: false + LINE_COLOR: 247, 227, 227 + AXIS: True + AXIS_COLOR: 247, 227, 227 + BACKGROUND_IMAGE: background.png + MEMORY: + GRAPH: + SHOW: true + X: 280 + Y: 260 + WIDTH: 70 + HEIGHT: 10 + MIN_VALUE: 0 + MAX_VALUE: 100 + BAR_COLOR: 247, 227, 227 + BAR_OUTLINE: true + BACKGROUND_IMAGE: background.png + RADIAL: + SHOW: false + X: 310 + Y: 255 + RADIUS: 20 + WIDTH: 5 + MIN_VALUE: 0 + MAX_VALUE: 100 + ANGLE_START: 110 + ANGLE_END: 70 + ANGLE_STEPS: 1 + ANGLE_SEP: 25 + CLOCKWISE: True + BAR_COLOR: 247, 227, 227 + SHOW_TEXT: True + SHOW_UNIT: True + FONT: roboto/Roboto-Bold.ttf + FONT_SIZE: 15 + FONT_COLOR: 200, 200, 200 + BACKGROUND_IMAGE: background.png + TEXT: + SHOW: true + SHOW_UNIT: True + X: 280 + Y: 245 + FONT: jetbrains-mono/JetBrainsMono-Bold.ttf + FONT_SIZE: 15 + FONT_COLOR: 247, 227, 227 + BACKGROUND_IMAGE: background.png + TEMPERATURE: + TEXT: + SHOW: true + SHOW_UNIT: True + X: 350 + Y: 240 + FONT: jetbrains-mono/JetBrainsMono-Bold.ttf + FONT_SIZE: 30 + FONT_COLOR: 247, 227, 227 + BACKGROUND_IMAGE: background.png + MEMORY: + INTERVAL: 1 + SWAP: + GRAPH: + SHOW: false + X: 115 + Y: 285 + WIDTH: 178 + HEIGHT: 13 + MIN_VALUE: 0 + MAX_VALUE: 100 + BAR_COLOR: 255, 0, 0 + BAR_OUTLINE: False + BACKGROUND_IMAGE: background.png + RADIAL: + SHOW: False + X: 141 + Y: 275 + RADIUS: 28 + WIDTH: 8 + MIN_VALUE: 0 + MAX_VALUE: 100 + ANGLE_START: 110 + ANGLE_END: 70 + ANGLE_STEPS: 1 + ANGLE_SEP: 25 + CLOCKWISE: True + BAR_COLOR: 255, 0, 0 + SHOW_TEXT: True + SHOW_UNIT: True + FONT: roboto/Roboto-Bold.ttf + FONT_SIZE: 13 + FONT_COLOR: 200, 200, 200 + BACKGROUND_IMAGE: background.png + VIRTUAL: + GRAPH: + SHOW: false + X: 40 + Y: 420 + WIDTH: 160 + HEIGHT: 15 + MIN_VALUE: 0 + MAX_VALUE: 100 + BAR_COLOR: 247, 227, 227 + BAR_OUTLINE: true + BACKGROUND_IMAGE: background.png + LINE_GRAPH: + SHOW: True + X: 30 + Y: 380 + WIDTH: 180 + HEIGHT: 70 + MIN_VALUE: 0 + MAX_VALUE: 100 + HISTORY_SIZE: 200 + AUTOSCALE: False + LINE_COLOR: 247, 227, 227 + AXIS: true + AXIS_COLOR: 247, 227, 227 + BACKGROUND_IMAGE: background.png + USED: + SHOW: True + SHOW_UNIT: false + X: 55 + Y: 350 + FONT: jetbrains-mono/JetBrainsMono-Bold.ttf + FONT_SIZE: 22 + FONT_COLOR: 247, 227, 227 + BACKGROUND_IMAGE: background.png + FREE: + SHOW: FALSE + SHOW_UNIT: True + X: 182 + Y: 129 + FONT: jetbrains-mono/JetBrainsMono-Bold.ttf + FONT_SIZE: 23 + FONT_COLOR: 255, 255, 255 + BACKGROUND_IMAGE: background.png + PERCENT_TEXT: + SHOW: True + SHOW_UNIT: True + X: 110 + Y: 310 + FONT: jetbrains-mono/JetBrainsMono-Bold.ttf + FONT_SIZE: 35 + FONT_COLOR: 247, 227, 227 + BACKGROUND_IMAGE: background.png + DISK: + INTERVAL: 10 + USED: + GRAPH: + SHOW: true + X: 280 + Y: 400 + WIDTH: 160 + HEIGHT: 15 + MIN_VALUE: 0 + MAX_VALUE: 100 + BAR_COLOR: 247, 227, 227 + BAR_OUTLINE: true + BACKGROUND_IMAGE: background.png + RADIAL: + SHOW: false + X: 360 + Y: 415 + RADIUS: 35 + WIDTH: 6 + MIN_VALUE: 0 + MAX_VALUE: 100 + ANGLE_START: 120 + ANGLE_END: 70 + ANGLE_STEPS: 1 + ANGLE_SEP: 25 + CLOCKWISE: True + BAR_COLOR: 247, 227, 227 + SHOW_TEXT: True + SHOW_UNIT: True + FONT: roboto/Roboto-Bold.ttf + FONT_SIZE: 25 + FONT_COLOR: 247, 227, 227 + BACKGROUND_IMAGE: background.png + TEXT: + SHOW: false + SHOW_UNIT: True + X: 204 + Y: 405 + FONT: jetbrains-mono/JetBrainsMono-Bold.ttf + FONT_SIZE: 23 + FONT_COLOR: 247, 227, 227 + BACKGROUND_IMAGE: background.png + PERCENT_TEXT: + SHOW: true + SHOW_UNIT: True + X: 305 + Y: 360 + FONT: jetbrains-mono/JetBrainsMono-Bold.ttf + FONT_SIZE: 40 + FONT_COLOR: 247, 227, 227 + BACKGROUND_IMAGE: background.png + TOTAL: + TEXT: + SHOW: False + SHOW_UNIT: True + X: 204 + Y: 375 + FONT: jetbrains-mono/JetBrainsMono-Bold.ttf + FONT_SIZE: 23 + FONT_COLOR: 255, 255, 255 + BACKGROUND_IMAGE: background.png + FREE: + TEXT: + SHOW: False + SHOW_UNIT: True + X: 204 + Y: 435 + FONT: jetbrains-mono/JetBrainsMono-Bold.ttf + FONT_SIZE: 23 + FONT_COLOR: 255, 255, 255 + BACKGROUND_IMAGE: background.png + NET: + INTERVAL: 1 + WLO: + UPLOAD: + TEXT: + SHOW: false + X: 327 + Y: 590 + FONT: jetbrains-mono/JetBrainsMono-Bold.ttf + FONT_SIZE: 22 + FONT_COLOR: 247, 227, 227 + BACKGROUND_IMAGE: background.png + UPLOADED: + TEXT: + SHOW: false + X: 380 + Y: 620 + FONT: jetbrains-mono/JetBrainsMono-Bold.ttf + FONT_SIZE: 17 + FONT_COLOR: 247, 227, 227 + BACKGROUND_IMAGE: background.png + DOWNLOAD: + TEXT: + SHOW: false + X: 87 + Y: 590 + FONT: jetbrains-mono/JetBrainsMono-Bold.ttf + FONT_SIZE: 22 + FONT_COLOR: 247, 227, 227 + BACKGROUND_IMAGE: background.png + DOWNLOADED: + TEXT: + SHOW: false + X: 140 + Y: 620 + FONT: jetbrains-mono/JetBrainsMono-Bold.ttf + FONT_SIZE: 17 + FONT_COLOR: 247, 227, 227 + BACKGROUND_IMAGE: background.png + ETH: + UPLOAD: + TEXT: + SHOW: true + X: 315 + Y: 540 + FONT: jetbrains-mono/JetBrainsMono-Bold.ttf + FONT_SIZE: 22 + FONT_COLOR: 247, 227, 227 + BACKGROUND_IMAGE: background.png + LINE_GRAPH: + SHOW: true + X: 260 + Y: 600 + WIDTH: 180 + HEIGHT: 60 + MIN_VALUE: 0 + MAX_VALUE: 500000 + HISTORY_SIZE: 120 + AUTOSCALE: False + LINE_COLOR: 247, 227, 227 + AXIS: True + AXIS_COLOR: 247, 227, 227 + BACKGROUND_IMAGE: background.png + UPLOADED: + TEXT: + SHOW: true + X: 370 + Y: 570 + FONT: jetbrains-mono/JetBrainsMono-Bold.ttf + FONT_SIZE: 17 + FONT_COLOR: 247, 227, 227 + BACKGROUND_IMAGE: background.png + DOWNLOAD: + TEXT: + SHOW: true + X: 80 + Y: 540 + FONT: jetbrains-mono/JetBrainsMono-Bold.ttf + FONT_SIZE: 22 + FONT_COLOR: 247, 227, 227 + BACKGROUND_IMAGE: background.png + LINE_GRAPH: + SHOW: true + X: 30 + Y: 600 + WIDTH: 180 + HEIGHT: 60 + MIN_VALUE: 0 + MAX_VALUE: 1500000 + HISTORY_SIZE: 120 + AUTOSCALE: False + LINE_COLOR: 247, 227, 227 + AXIS: True + AXIS_COLOR: 247, 227, 227 + BACKGROUND_IMAGE: background.png + DOWNLOADED: + TEXT: + SHOW: true + X: 135 + Y: 570 + FONT: jetbrains-mono/JetBrainsMono-Bold.ttf + FONT_SIZE: 17 + FONT_COLOR: 247, 227, 227 + BACKGROUND_IMAGE: background.png + DATE: + # For time display, it is recommended not to change the interval: keep to 1 + INTERVAL: 1 + DAY: # Format (Y/M/D ordering, month/day translations...) will match your computer locale + TEXT: + FORMAT: medium # short (2/20/23) / medium (Feb 20, 2023) / long (February 20, 2023) / full (Monday, February 20, 2023) + SHOW: True + X: 275 + Y: 25 + FONT: roboto/Roboto-Bold.ttf + FONT_SIZE: 30 + FONT_COLOR: 247, 227, 227 + BACKGROUND_IMAGE: background.png + HOUR: # Format (12/24h, timezone translations) will match your computer locale + TEXT: + FORMAT: medium # short (6:48 PM) / medium (6:48:53 PM) / long (6:48:53 PM UTC) / full (6:48:53 PM Coordinated Universal Time) + SHOW: True + X: 55 + Y: 25 + FONT: roboto/Roboto-Bold.ttf + FONT_SIZE: 30 + FONT_COLOR: 247, 227, 227 + BACKGROUND_IMAGE: background.png + WEATHER: + # For optimal use, if you don't want to trigger the free threshold daily call (1000 calls), the interval should be 90 MINIMUM (not really useful as the API didn't update that quickly) + INTERVAL: 300 + TEMPERATURE: + TEXT: + SHOW: True + X: 27 + Y: 70 + FONT: roboto-mono/RobotoMono-Bold.ttf + FONT_SIZE: 22 + FONT_COLOR: 247, 227, 227 + BACKGROUND_IMAGE: background.png + TEMPERATURE_FELT: + TEXT: + SHOW: True + X: 115 + Y: 68 + FONT: roboto-mono/RobotoMono-Bold.ttf + FONT_SIZE: 22 + FONT_COLOR: 247, 227, 227 + BACKGROUND_IMAGE: background.png + UPDATE_TIME: + TEXT: + SHOW: True + X: 370 + Y: 69 + FONT: roboto-mono/RobotoMono-Bold.ttf + FONT_SIZE: 22 + FONT_COLOR: 247, 227, 227 + BACKGROUND_IMAGE: background.png + HUMIDITY: + TEXT: + SHOW: True + X: 290 + Y: 69 + FONT: roboto-mono/RobotoMono-Bold.ttf + FONT_SIZE: 22 + FONT_COLOR: 247, 227, 227 + BACKGROUND_IMAGE: background.png + WEATHER_DESCRIPTION: + TEXT: + SHOW: True + X: 0 + Y: 95 + WIDTH: 480 + FONT: roboto-mono/RobotoMono-Bold.ttf + FONT_SIZE: 18 + FONT_COLOR: 247, 227, 227 + BACKGROUND_IMAGE: background.png + ANCHOR: mb # Text is centered + PING: + INTERVAL: 1 + TEXT: + SHOW: True + X: 325 + Y: 495 + FONT: jetbrains-mono/JetBrainsMono-Bold.ttf + FONT_SIZE: 25 + FONT_COLOR: 247, 227, 227 + BACKGROUND_IMAGE: background.png + ALIGN: right + LINE_GRAPH: + SHOW: true + X: 30 + Y: 680 + WIDTH: 415 + HEIGHT: 70 + MIN_VALUE: 0 + MAX_VALUE: 250 + HISTORY_SIZE: 120 + AUTOSCALE: false + LINE_COLOR: 247, 227, 227 + AXIS: True + AXIS_COLOR: 247, 227, 227 + BACKGROUND_IMAGE: background.png diff --git a/res/themes/ColoredFlat/wallpaperv2.png b/res/themes/ColoredFlat/wallpaperv2.png new file mode 100644 index 00000000..bf2d7581 Binary files /dev/null and b/res/themes/ColoredFlat/wallpaperv2.png differ diff --git a/res/themes/default.yaml b/res/themes/default.yaml index d2c5cc6d..a915bcb6 100644 --- a/res/themes/default.yaml +++ b/res/themes/default.yaml @@ -217,5 +217,32 @@ STATS: FORMATTED: TEXT: SHOW: False + WEATHER: + INTERVAL: 0 + TEMPERATURE: + TEXT: + SHOW: False + TEMPERATURE_FELT: + TEXT: + SHOW: False + UPDATE_TIME: + TEXT: + SHOW: False + HUMIDITY: + TEXT: + SHOW: False + WEATHER_DESCRIPTION: + TEXT: + SHOW: False + PING: + INTERVAL: 0 + GRAPH: + SHOW: False + RADIAL: + SHOW: False + LINE_GRAPH: + SHOW: False + TEXT: + SHOW: False CUSTOM: INTERVAL: 0 diff --git a/res/themes/theme_example.yaml b/res/themes/theme_example.yaml index d843c3b6..c34df53a 100644 --- a/res/themes/theme_example.yaml +++ b/res/themes/theme_example.yaml @@ -1465,4 +1465,121 @@ STATS: # BACKGROUND_COLOR: 132, 154, 165 BACKGROUND_IMAGE: background.png ALIGN: left # left / center / right - ANCHOR: lt # Check https://pillow.readthedocs.io/en/stable/handbook/text-anchors.html \ No newline at end of file + ANCHOR: lt # Check https://pillow.readthedocs.io/en/stable/handbook/text-anchors.html + WEATHER: + # For optimal use, if you want to stay on the free plan (1000 calls), the interval should be 90 MINIMUM (not really useful as the API didn't update that quickly) + INTERVAL: 300 + TEMPERATURE: + TEXT: + SHOW: True + X: 27 + Y: 70 + FONT: roboto-mono/RobotoMono-Bold.ttf + FONT_SIZE: 22 + FONT_COLOR: 200, 200, 200 + BACKGROUND_IMAGE: background.png + TEMPERATURE_FELT: + TEXT: + SHOW: True + X: 115 + Y: 68 + FONT: roboto-mono/RobotoMono-Bold.ttf + FONT_SIZE: 22 + FONT_COLOR: 200, 200, 200 + BACKGROUND_IMAGE: background.png + UPDATE_TIME: + TEXT: + SHOW: True + X: 370 + Y: 69 + FONT: roboto-mono/RobotoMono-Bold.ttf + FONT_SIZE: 22 + FONT_COLOR: 200, 200, 200 + BACKGROUND_IMAGE: background.png + HUMIDITY: + TEXT: + SHOW: True + X: 290 + Y: 69 + FONT: roboto-mono/RobotoMono-Bold.ttf + FONT_SIZE: 22 + FONT_COLOR: 200, 200, 200 + BACKGROUND_IMAGE: background.png + WEATHER_DESCRIPTION: + TEXT: + SHOW: True + X: 20 + Y: 95 + FONT: roboto-mono/RobotoMono-Bold.ttf + FONT_SIZE: 18 + FONT_COLOR: 200, 200, 200 + BACKGROUND_IMAGE: background.png + PING: + INTERVAL: 10 + GRAPH: + SHOW: False + X: 115 + Y: 357 + WIDTH: 178 + HEIGHT: 13 + MIN_VALUE: 0 + MAX_VALUE: 100 + BAR_COLOR: 255, 0, 0 + BAR_OUTLINE: False + # BACKGROUND_COLOR: 0, 0, 0 + BACKGROUND_IMAGE: background.png + RADIAL: + SHOW: False + X: 141 + Y: 275 + RADIUS: 28 + WIDTH: 8 + MIN_VALUE: 0 + MAX_VALUE: 100 + ANGLE_START: 110 + ANGLE_END: 70 + ANGLE_STEPS: 1 + ANGLE_SEP: 25 + CLOCKWISE: True + BAR_COLOR: 255, 0, 0 + SHOW_TEXT: True + SHOW_UNIT: True + FONT: roboto-mono/RobotoMono-Bold.ttf + FONT_SIZE: 13 + FONT_COLOR: 200, 200, 200 + # BACKGROUND_COLOR: 0, 0, 0 + BACKGROUND_IMAGE: background.png + LINE_GRAPH: + SHOW: False + X: 300 + Y: 220 + WIDTH: 133 + HEIGHT: 70 + MIN_VALUE: 30 + MAX_VALUE: 120 + HISTORY_SIZE: 10 + AUTOSCALE: True + LINE_COLOR: 61, 184, 225 + LINE_WIDTH: 2 + AXIS: True + AXIS_COLOR: 255, 135, 0 + # BACKGROUND_COLOR: 0, 0, 0 + BACKGROUND_IMAGE: background.png + TEXT: + SHOW: False + SHOW_UNIT: True + X: 204 + Y: 405 + # Text sensors may vary in size and create "ghosting" effects where old value stay displayed under the new one. + # To avoid this use one of these 2 methods (or both): + # - either use a monospaced font (fonts with "mono" in name, see res/fonts/ for available fonts) + # - or force a static width/height for the text field. Be sure to have enough space for the longest value that can be displayed (e.g. "100%" for a percentage) + # WIDTH: 200 # Uncomment to force a static width + # HEIGHT: 50 # Uncomment to force static height + FONT: jetbrains-mono/JetBrainsMono-Bold.ttf + FONT_SIZE: 23 + FONT_COLOR: 255, 255, 255 + # BACKGROUND_COLOR: 132, 154, 165 + BACKGROUND_IMAGE: background.png + ALIGN: left # left / center / right + ANCHOR: lt # Check https://pillow.readthedocs.io/en/stable/handbook/text-anchors.html diff --git a/theme-editor.py b/theme-editor.py index e0a63266..5ca59443 100755 --- a/theme-editor.py +++ b/theme-editor.py @@ -47,7 +47,6 @@ except: os._exit(0) - if len(sys.argv) != 2: print("Usage :") print(" theme-editor.py theme-name") @@ -125,6 +124,10 @@ def refresh_theme(): stats.SystemUptime.stats() if config.THEME_DATA['STATS']['CUSTOM'].get("INTERVAL", 0) > 0: stats.Custom.stats() + if config.THEME_DATA['STATS']['WEATHER'].get("INTERVAL", 0) > 0: + stats.Weather.stats() + if config.THEME_DATA['STATS']['PING'].get("INTERVAL", 0) > 0: + stats.Ping.stats() if __name__ == "__main__":