Skip to content

Commit cb75d81

Browse files
authored
Merge pull request #510 from Psykotik/main
[Add] Ping and Weather custom sensor + ColoredFlat Theme
2 parents 2355937 + 9d6a00d commit cb75d81

14 files changed

+765
-9
lines changed

Diff for: config.yaml

+18-6
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
1-
---
21
config:
32
# Configuration values to set up basic communication
43
# Set your COM port e.g. COM3 for Windows, /dev/ttyACM0 for Linux...
54
# Use AUTO for COM port auto-discovery (may not work on every setup)
65
# COM_PORT: "/dev/ttyACM0"
76
# COM_PORT: "COM3"
8-
COM_PORT: "AUTO"
7+
COM_PORT: AUTO
98

109
# Theme to use (located in res/themes)
1110
# Use the name of the folder as value
12-
THEME: 3.5inchTheme2
11+
THEME: ColoredFlat
1312

1413
# Hardware sensors reading
1514
# Choose the appropriate method for reading your hardware sensors:
@@ -23,8 +22,21 @@ config:
2322
# Linux/MacOS interfaces are named "eth0", "wlan0", "wlp1s0", "enp2s0"...
2423
# For Windows use the interfaces pretty name: "Ethernet 2", "Wi-Fi", ...
2524
# Leave the fields empty if the card does not exist on your setup
26-
ETH: "" # Ethernet Card
27-
WLO: "" # Wi-Fi Card
25+
ETH: Ethernet # Ethernet Card
26+
WLO: Wi-Fi # Wi-Fi Card
27+
28+
# Used to calculate your ping.
29+
PING: 8.8.8.8
30+
31+
# For weather, you can use for example https://www.latlong.net/
32+
LATITUDE: 45.75
33+
LONGITUDE: 4.85
34+
# OpenWeatherMap API KEY. Can be obtained by creating a free account on https://home.openweathermap.org/users/sign_up. You need to subscreibe to the 3.0 OneCallAPI that has 1000 free daily calls
35+
API_KEY: abcdef
36+
# Units used to display temperatures (metric - °C, imperial - °F, standard - °K)
37+
WEATHER_UNITS: metric
38+
# Language is used by the API. Find more here https://openweathermap.org/api/one-call-3#multi
39+
LANGUAGE: fr
2840

2941
display:
3042
# Display revision:
@@ -35,7 +47,7 @@ display:
3547
# - SIMU for 3.5" simulated LCD (image written in screencap.png)
3648
# - SIMU5 for 5" simulated LCD
3749
# To identify your smart screen: https://github.com/mathoudebine/turing-smart-screen-python/wiki/Hardware-revisions
38-
REVISION: A
50+
REVISION: C
3951

4052
# Display Brightness
4153
# Set this as the desired %, 0 being completely dark and 100 being max brightness

Diff for: library/scheduler.py

+6
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,12 @@ def CustomStats():
161161
stats.Custom.stats()
162162

163163

164+
@async_job("Weather_Stats")
165+
@schedule(timedelta(seconds=config.THEME_DATA['STATS'].get('WEATHER', {}).get("INTERVAL", 0)).total_seconds())
166+
def WeatherStats():
167+
# logger.debug("Refresh Weather data")
168+
stats.Weather.stats()
169+
164170
@async_job("Queue_Handler")
165171
@schedule(timedelta(milliseconds=1).total_seconds())
166172
def QueueHandler():

Diff for: library/sensors/sensors_custom.py

+36-1
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,12 @@
2121

2222
import math
2323
import platform
24+
import requests
2425
from abc import ABC, abstractmethod
2526
from typing import List
27+
from ping3 import ping, verbose_ping
28+
from datetime import datetime
29+
import library.config as config
2630

2731

2832
# Custom data classes must be implemented in this file, inherit the CustomDataSource and implement its 2 methods
@@ -47,6 +51,37 @@ def last_values(self) -> List[float]:
4751
# If you do not want to draw a line graph or if your custom data has no numeric values, keep this function empty
4852
pass
4953

54+
# Custom data class to measure ping to google.fr
55+
class Ping(CustomDataSource):
56+
# This list is used to store the last 500 values to display a line graph
57+
last_val = [math.nan] * 500 # By default, it is filed with math.nan values to indicate there is no data stored
58+
59+
def as_numeric(self) -> float:
60+
# Measure the ping
61+
try:
62+
result = ping(config.CONFIG_DATA['config'].get('PING', "8.8.8.8"))*1000
63+
if result is not None:
64+
# Store the value to the history list that will be used for line graph
65+
self.last_val.append(result)
66+
# Also remove the oldest value from history list
67+
self.last_val.pop(0)
68+
return result
69+
else:
70+
self.last_val.append(9999)
71+
self.last_val.pop(0)
72+
return 9999 # Return 0 if ping fails
73+
except Exception as e:
74+
self.last_val.append(9999)
75+
self.last_val.pop(0)
76+
return 9999
77+
78+
def as_string(self) -> str:
79+
# Format the ping result as a string
80+
return f'{self.as_numeric():4.0f} ms'
81+
82+
def last_values(self) -> List[float]:
83+
# Since there is no historical data for ping, return an empty list
84+
return self.last_val
5085

5186
# Example for a custom data class that has numeric and text values
5287
class ExampleCustomNumericData(CustomDataSource):
@@ -95,4 +130,4 @@ def as_string(self) -> str:
95130

96131
def last_values(self) -> List[float]:
97132
# If a custom data class only has text values, it won't be possible to display line graph
98-
pass
133+
pass

Diff for: library/stats.py

+75
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
import babel.dates
3232
from psutil._common import bytes2human
33+
import requests
3334

3435
import library.config as config
3536
from library.display import display
@@ -767,3 +768,77 @@ def stats():
767768
theme_data = config.THEME_DATA['STATS']['CUSTOM'][custom_stat].get("LINE_GRAPH", None)
768769
if theme_data is not None and last_values is not None:
769770
display_themed_line_graph(theme_data=theme_data, values=last_values)
771+
772+
class Weather:
773+
@staticmethod
774+
def stats():
775+
WEATHER_UNITS = {'metric': '°C', 'imperial': '°F', 'standard': '°K'}
776+
777+
weather_theme_data = config.THEME_DATA['STATS'].get('WEATHER', {})
778+
wtemperature_theme_data = weather_theme_data.get('TEMPERATURE', {}).get('TEXT', {})
779+
wfelt_theme_data = weather_theme_data.get('TEMPERATURE_FELT', {}).get('TEXT', {})
780+
wupdatetime_theme_data = weather_theme_data.get('UPDATE_TIME', {}).get('TEXT', {})
781+
wdescription_theme_data = weather_theme_data.get('WEATHER_DESCRIPTION', {}).get('TEXT', {})
782+
whumidity_theme_data = weather_theme_data.get('HUMIDITY', {}).get('TEXT', {})
783+
784+
# Retrieve information used to center description, if needed
785+
center_description_length = 40
786+
if 'CENTER_LENGTH' in wdescription_theme_data:
787+
center_description_length = wdescription_theme_data['CENTER_LENGTH']
788+
789+
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
790+
791+
if activate:
792+
if HW_SENSORS in ["STATIC", "STUB"]:
793+
temp = "17.5°C"
794+
feel = "(17.2°C)"
795+
desc = "Cloudy"
796+
time = "@15:33"
797+
humidity = "45%"
798+
if wdescription_theme_data['CENTER_LENGTH']:
799+
desc = "x"*center_description_length
800+
else:
801+
# API Parameters
802+
lat = config.CONFIG_DATA['config'].get('LATITUDE', "")
803+
lon = config.CONFIG_DATA['config'].get('LONGITUDE', "")
804+
api_key = config.CONFIG_DATA['config'].get('API_KEY', "")
805+
units = config.CONFIG_DATA['config'].get('WEATHER_UNITS', "metric")
806+
lang = config.CONFIG_DATA['config'].get('LANGUAGE', "en")
807+
deg = WEATHER_UNITS.get(units, '°?')
808+
if api_key:
809+
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}'
810+
try:
811+
response = requests.get(url)
812+
if response.status_code == 200:
813+
try:
814+
data = response.json()
815+
temp = f"{data['current']['temp']:.1f}{deg}"
816+
feel = f"({data['current']['feels_like']:.1f}{deg})"
817+
desc = data['current']['weather'][0]['description'].capitalize()
818+
if wdescription_theme_data['CENTER_LENGTH']:
819+
desc = desc.center(center_description_length)
820+
humidity = f"{data['current']['humidity']:.0f}%"
821+
now = datetime.datetime.now()
822+
time = f"@{now.hour:02d}:{now.minute:02d}"
823+
except Exception as e:
824+
logger.error(str(e))
825+
desc = "Error fetching weather"
826+
else:
827+
print(f"Failed to fetch weather data. Status code: {response.status_code}")
828+
print(f"Response content: {response.content}")
829+
logger.error(response.text)
830+
desc = response.json().get('message')
831+
except:
832+
desc = "Connection error"
833+
834+
if activate:
835+
# Display Temperature
836+
display_themed_value(theme_data=wtemperature_theme_data, value=temp)
837+
# Display Temperature Felt
838+
display_themed_value(theme_data=wfelt_theme_data, value=feel)
839+
# Display Update Time
840+
display_themed_value(theme_data=wupdatetime_theme_data, value=time)
841+
# Display Humidity
842+
display_themed_value(theme_data=whumidity_theme_data, value=humidity)
843+
# Display Weather Description
844+
display_themed_value(theme_data=wdescription_theme_data, value=desc)

Diff for: main.py

+1
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ def on_win32_wm_event(hWnd, msg, wParam, lParam):
209209
scheduler.NetStats()
210210
scheduler.DateStats()
211211
scheduler.CustomStats()
212+
scheduler.WeatherStats()
212213
scheduler.QueueHandler()
213214

214215
if tray_icon and platform.system() == "Darwin": # macOS-specific

Diff for: requirements.txt

+9-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Python packages requirements
2-
Pillow~=10.3.0 # Image generation
2+
Pillow~=10.2.0 # Image generation
33
pyserial~=3.5 # Serial link to communicate with the display
44
PyYAML~=6.0.1 # For themes files
55
psutil~=5.9.8 # CPU / disk / network metrics
@@ -8,9 +8,10 @@ babel~=2.14.0 # Date/time formatting
88
ruamel.yaml~=0.18.6 # For configuration editor
99
sv-ttk~=2.6.0 # Tk Sun Valley theme for configuration editor
1010

11+
1112
# Efficient image serialization
1213
numpy~=1.24.4; python_version < "3.9" # For Python 3.8 max.
13-
numpy~=1.26.4; python_version >= "3.9" # For Python 3.9+
14+
numpy~=1.26.0; python_version >= "3.9" # For Python 3.9+
1415

1516
# For Nvidia GPU on all platforms
1617
GPUtil~=1.4.0; python_version < "3.12"
@@ -27,3 +28,9 @@ pyadl~=0.1; sys_platform=="win32"
2728
# Following packages are for LibreHardwareMonitor integration on Windows
2829
pythonnet~=3.0.3; sys_platform=="win32"
2930
pywin32>=306; sys_platform=="win32"
31+
32+
# for weather
33+
requests
34+
35+
# for ping
36+
ping3~=4.0.4

Diff for: res/themes/ColoredFlat/background.png

209 KB
Loading

Diff for: res/themes/ColoredFlat/background.xcf

675 KB
Binary file not shown.

Diff for: res/themes/ColoredFlat/preview.png

237 KB
Loading

Diff for: res/themes/ColoredFlat/telecharger-arrow.png

3.61 KB
Loading

0 commit comments

Comments
 (0)