final dashboard adjustments

This commit is contained in:
giusber2005 2025-08-02 06:14:14 +02:00
parent 81475b8080
commit 770247a175
14 changed files with 139 additions and 35 deletions

BIN
.DS_Store vendored

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -6,7 +6,8 @@ import os
from datetime import datetime, date
from plant_model import PlantGrowthModel
from data_handler import DataHandler
from tkcalendar import DateEntry
from tkcalendar import DateEntry, Calendar
from plant_meteo import HappyMeteo
class PlantGrowthDashboard:
def __init__(self, root):
@ -15,7 +16,11 @@ class PlantGrowthDashboard:
self.root.geometry("1000x800") # More square dimensions
self.root.configure(bg='#f0f0f0')
image = Image.open("public/transparentLogo.png")
image = Image.open("public/logoTransparent.png")
desired_size = (128, 128)
image = image.resize(desired_size, Image.Resampling.LANCZOS)
# Convert to PhotoImage
icon = ImageTk.PhotoImage(image)
# Set as window icon
@ -24,6 +29,7 @@ class PlantGrowthDashboard:
# Initialize components
self.plant_model = PlantGrowthModel()
self.data_handler = DataHandler()
self.happyMeteo = HappyMeteo()
# Variables - fixed plant type
self.current_plant = "tomato" # Fixed plant type
@ -143,6 +149,8 @@ class PlantGrowthDashboard:
command=lambda x, p=param: self.on_param_change(p))
scale.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(4, 4))
setattr(self, f"{param}_scale", scale)
# Value label
value_label = ttk.Label(param_frame, text=f"{self.env_params[param].get():.1f}", width=5, font=('Arial', 8))
value_label.pack(side=tk.RIGHT)
@ -163,28 +171,33 @@ class PlantGrowthDashboard:
spacer.grid(row=row, column=0, columnspan=2, pady=10)
# Add this after the parameters section
ttk.Label(control_frame, text="Final date of growth:",
ttk.Label(control_frame, text="Final date of growth (choose a date):",
font=('Arial', 9, 'bold')).grid(row=row, column=0, columnspan=2, sticky=tk.W, pady=(10, 4))
row += 1
# Compact date entry with calendar popup
# To get the selected dat simply call self.date_entry.get_date()
self.date_entry = DateEntry(control_frame,
width=12,
background='darkblue',
foreground='white',
borderwidth=2,
font=('Arial', 8),
date_pattern='dd/mm/yyyy',
state='readonly', # Add this
cursor='hand2') # Add this
self.date_entry.grid(row=row, column=0, columnspan=2, sticky=tk.W, pady=2)
# To get the selected dat simply call self.calendar.get_date()
# self.date_entry
self.calendar = Calendar(control_frame, selectmode='day',
year=2025, month=8, day=1,
font=('Arial', 8))
self.calendar.grid(row=row, column=0, columnspan=2, pady=2)
row += 1
control_frame.columnconfigure(0, weight=1)
control_frame.columnconfigure(1, weight=1)
def disable_parameter(self, param):
scale = getattr(self, f"{param}_scale", None)
if scale:
scale.configure(state='disabled')
def enable_parameter(self, param):
scale = getattr(self, f"{param}_scale", None)
if scale:
scale.configure(state='normal')
def setup_visualization_panel(self, parent):
viz_frame = ttk.LabelFrame(parent, text="Plant Visualization", padding="6")
viz_frame.grid(row=1, column=1, sticky=(tk.W, tk.E, tk.N, tk.S), padx=3)
@ -325,14 +338,18 @@ class PlantGrowthDashboard:
if current_mode == "controlled":
print("Switched to Controlled mode")
# Enable all parameter controls
self.enable_parameter("humidity")
self.enable_parameter("brightness")
self.enable_parameter("temperature")
# No need to call the meteo api
elif current_mode == "open":
print("Switched to Open mode")
# Disable most parameter controls (temp, humidity, light)
# Call the meteo api to retrieve all the parameters, set variable meteo_values to retrieve when submitiing all
# Inside the retrieving of all the data check if the mode is one, select which data to use
self.disable_parameter("humidity")
self.disable_parameter("brightness")
self.disable_parameter("temperature")
def on_param_change(self, param):
value = self.env_params[param].get()
@ -354,7 +371,7 @@ class PlantGrowthDashboard:
self.ambient_mode.set("controlled")
self.date_entry.set_date(date.today())
self.calendar.set_date(date.today())
def update_parameter_label(self, param, value):
"""Update the value label for a specific parameter"""
@ -387,10 +404,23 @@ class PlantGrowthDashboard:
def submit_plant_data(self):
"""Submit plant information and photo"""
try:
# Get current parameters
start_date = datetime.now().date()
end_date = self.calendar.get_date()
time_lapse = end_date - start_date
days_difference = time_lapse.days
params = {param: var.get() for param, var in self.env_params.items()}
params['plant_type'] = self.current_plant
params['ambient_mode'] = self.ambient_mode.get()
current_mode = self.ambient_mode.get()
if current_mode == "open":
happy_data = self.happyMeteo.openMeteoCall(days_difference)
excluded_params = {"humidity", "temperature", "brightness"}
params = {param: var.get() for param, var in self.env_params.items()
if param not in excluded_params}
# Create submission data
submission_data = {
@ -399,7 +429,7 @@ class PlantGrowthDashboard:
'baseline_image_path': self.baseline_image_path,
'plant_info': self.plant_info_text.get(1.0, tk.END),
'start date': datetime.now().date().isoformat(),
'end_date': self.date_entry.get_date().isoformat()
'end_date': self.calendar.get_date().isoformat()
}
#Remove plant_info_text

View file

@ -0,0 +1,83 @@
import openmeteo_requests
import pandas as pd
import requests_cache
from retry_requests import retry
import geocoder
class HappyMeteo:
def __init__(self):
# Setup the Open-Meteo API client with cache and retry on error
cache_session = requests_cache.CachedSession('.cache', expire_after = 3600)
retry_session = retry(cache_session, retries = 5, backoff_factor = 0.2)
self.openmeteo = openmeteo_requests.Client(session = retry_session)
def get_current_location(self):
"""Get current location using IP geolocation"""
try:
g = geocoder.ip('me')
if g.ok:
latitude = g.latlng[0]
longitude = g.latlng[1]
print(f"Latitude: {latitude}")
print(f"Longitude: {longitude}")
print(f"Address: {g.address}")
return latitude, longitude
else:
print("Could not determine location")
return None, None
except Exception as e:
print(f"Error getting location: {e}")
return None, None
def openMeteoCall(self, timeLapse):
lat, lon = self.get_current_location()
# Make sure all required weather variables are listed here
# The order of variables in hourly or daily is important to assign them correctly below
url = "https://api.open-meteo.com/v1/forecast"
params = {
"latitude": lat,
"longitude": lon,
"daily": ["weather_code", "temperature_2m_mean", "rain_sum", "showers_sum", "precipitation_sum", "daylight_duration", "relative_humidity_2m_mean"],
"timezone": "auto",
"forecast_days": timeLapse
}
responses = self.openmeteo.weather_api(url, params=params)
# Process first location. Add a for-loop for multiple locations or weather models
response = responses[0]
print(f"Coordinates: {response.Latitude()}°N {response.Longitude()}°E")
print(f"Elevation: {response.Elevation()} m asl")
print(f"Timezone: {response.Timezone()}{response.TimezoneAbbreviation()}")
print(f"Timezone difference to GMT+0: {response.UtcOffsetSeconds()}s")
# Process daily data. The order of variables needs to be the same as requested.
daily = response.Daily()
daily_weather_code = daily.Variables(0).ValuesAsNumpy()
daily_temperature_2m_mean = daily.Variables(1).ValuesAsNumpy()
daily_rain_sum = daily.Variables(2).ValuesAsNumpy()
daily_showers_sum = daily.Variables(3).ValuesAsNumpy()
daily_precipitation_sum = daily.Variables(4).ValuesAsNumpy()
daily_daylight_duration = daily.Variables(5).ValuesAsNumpy()
daily_relative_humidity_2m_mean = daily.Variables(6).ValuesAsNumpy()
daily_data = {"date": pd.date_range(
start = pd.to_datetime(daily.Time(), unit = "s", utc = True),
end = pd.to_datetime(daily.TimeEnd(), unit = "s", utc = True),
freq = pd.Timedelta(seconds = daily.Interval()),
inclusive = "left"
)}
daily_data["weather_code"] = daily_weather_code
daily_data["temperature_2m_mean"] = daily_temperature_2m_mean
daily_data["rain_sum"] = daily_rain_sum
daily_data["showers_sum"] = daily_showers_sum
daily_data["precipitation_sum"] = daily_precipitation_sum
daily_data["daylight_duration"] = daily_daylight_duration
daily_data["relative_humidity_2m_mean"] = daily_relative_humidity_2m_mean
daily_dataframe = pd.DataFrame(data = daily_data)
print("\nDaily data\n", daily_dataframe)

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

View file

@ -30,6 +30,10 @@ memory-profiler>=0.60.0
tkcalendar
openmeteo-requests
requests-cache
retry-requests
geocoder
# Note: tkinter comes pre-installed with most Python distributions
# If tkinter is not available, install it using your system package manager:
# Ubuntu/Debian: sudo apt-get install python3-tk

View file

@ -1,18 +1,5 @@
ROADMAP
-differences between the three modes:
Open mode:
-the temp, humidity, light, water parameters are setted by the meteo api, the rest is setted by the user
-all the parameters are controlled by the user
SemiControlled mode:
-the user choose how to set the parameters
-all parameters free
Controlled mode:
-all the values are set by the user
-the user choose which parameters are free and which are controlled by the meteo api
-make the calendar widget working
-format the data that will came to the user
-final updates of README.md, hackathon page, forgejo, github page, small design adjustments.