| """ |
| Streamlit app: SolarWine — Smart Shading for Vineyard Solar Panels. |
| 5 tabs: Overview, Photosynthesis & Data, Forecasting, Shading Simulator, Documentation. |
| """ |
|
|
| from __future__ import annotations |
|
|
| import sys |
| from pathlib import Path |
|
|
| PROJECT_ROOT = Path(__file__).resolve().parent |
| if str(PROJECT_ROOT) not in sys.path: |
| sys.path.insert(0, str(PROJECT_ROOT)) |
|
|
| try: |
| from dotenv import load_dotenv |
| load_dotenv(PROJECT_ROOT / ".env") |
| except ImportError: |
| pass |
|
|
| import os |
| import streamlit as st |
|
|
| |
| |
| for key, value in st.secrets.items(): |
| if isinstance(value, str) and key not in os.environ: |
| os.environ[key] = value |
|
|
| from ui.bootstrap import img_to_base64, now_israel |
|
|
| |
| |
| |
|
|
| st.set_page_config(page_title="SolarWine - Smart Shading for Vineyards", layout="wide") |
|
|
| |
| |
| |
| _NEW_FRONTEND_URL = os.environ.get("SOLARWINE_FRONTEND_URL", "") |
| if _NEW_FRONTEND_URL: |
| st.info( |
| f"A new version of the SolarWine dashboard is available at " |
| f"[{_NEW_FRONTEND_URL}]({_NEW_FRONTEND_URL}). " |
| f"This Streamlit app will be retired soon.", |
| icon="🔄", |
| ) |
|
|
| |
| |
| |
|
|
| _BRAND_GREEN = "#00BD3E" |
| _BRAND_DARK = "#1A1A1A" |
|
|
| st.markdown(f""" |
| <style> |
| /* SolarWine brand overrides — async font load (non-blocking) */ |
| @font-face {{ |
| font-family: 'Open Sans'; |
| font-style: normal; |
| font-weight: 300 400 600 700; |
| font-display: swap; |
| src: url('https://fonts.gstatic.com/s/opensans/v40/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTS-mu0SC55I.woff2') format('woff2'); |
| }} |
| |
| html, body, [class*="css"] {{ |
| font-family: 'Open Sans', sans-serif; |
| }} |
| |
| /* Header bar */ |
| header[data-testid="stHeader"] {{ |
| background-color: {_BRAND_DARK}; |
| }} |
| |
| /* Metric value color */ |
| [data-testid="stMetricValue"] {{ |
| color: {_BRAND_GREEN}; |
| }} |
| |
| /* Tab labels */ |
| button[data-baseweb="tab"] {{ |
| font-weight: 600; |
| }} |
| |
| /* Sidebar background */ |
| section[data-testid="stSidebar"] {{ |
| background-color: {_BRAND_DARK}; |
| }} |
| section[data-testid="stSidebar"] * {{ |
| color: #FFFFFF !important; |
| }} |
| section[data-testid="stSidebar"] button {{ |
| background-color: {_BRAND_GREEN} !important; |
| color: #FFFFFF !important; |
| border: none !important; |
| border-radius: 6px !important; |
| }} |
| section[data-testid="stSidebar"] button:hover {{ |
| background-color: #00a035 !important; |
| }} |
| |
| /* Hero banner */ |
| .hero-banner {{ |
| background: linear-gradient(135deg, {_BRAND_DARK} 0%, #2d3a2d 100%); |
| padding: 2rem 2.5rem; |
| border-radius: 12px; |
| margin-bottom: 1.5rem; |
| display: flex; |
| align-items: center; |
| gap: 2rem; |
| }} |
| .hero-banner img {{ |
| max-height: 48px; |
| }} |
| .hero-text {{ |
| color: #FFFFFF; |
| }} |
| .hero-text h1 {{ |
| margin: 0 0 0.3rem 0; |
| font-size: 1.8rem; |
| font-weight: 700; |
| color: #FFFFFF; |
| }} |
| .hero-text p {{ |
| margin: 0; |
| font-size: 1.05rem; |
| color: #B8D4B8; |
| }} |
| </style> |
| """, unsafe_allow_html=True) |
|
|
| |
| |
| |
|
|
| _ASSETS = PROJECT_ROOT / "assets" |
| _logo_path = _ASSETS / "logo.png" |
|
|
| if _logo_path.exists(): |
| _logo_b64 = img_to_base64(_logo_path) |
| st.markdown(f""" |
| <div class="hero-banner"> |
| <img src="data:image/png;base64,{_logo_b64}" alt="SolarWine logo"> |
| <div class="hero-text"> |
| <h1>Smart Shading for Vineyard Solar Panels</h1> |
| <p>Empowering growers · Harvesting sunshine</p> |
| </div> |
| </div> |
| """, unsafe_allow_html=True) |
| else: |
| st.title("SolarWine — Smart Shading for Vineyard Solar Panels") |
|
|
| |
| |
| |
|
|
| sidebar = st.sidebar |
| if _logo_path.exists(): |
| sidebar.image(str(_logo_path), width=180) |
|
|
| _now = now_israel() |
| sidebar.caption("**Site:** Yeruham, Israel") |
| sidebar.markdown(f"**Date:** {_now.strftime('%Y-%m-%d')}") |
| sidebar.markdown(f"**Time (local):** {_now.strftime('%H:%M')}") |
| st.session_state.current_time_israel = _now |
|
|
| |
| |
| |
|
|
| _PAGES = [ |
| "System Status", |
| "Overview", |
| "Photosynthesis & Data", |
| "Forecasting", |
| "Shading Simulator", |
| "Documentation", |
| ] |
|
|
| _selected = sidebar.radio("Navigate", _PAGES, label_visibility="collapsed") |
|
|
| if _selected == "System Status": |
| from ui.tab_system_status import render_tab_system_status |
| render_tab_system_status() |
| elif _selected == "Overview": |
| from ui.tab_overview import render_tab_overview |
| render_tab_overview() |
| elif _selected == "Photosynthesis & Data": |
| from ui.tab_data import render_tab_data |
| render_tab_data() |
| elif _selected == "Forecasting": |
| from ui.tab_forecast import render_tab_forecast |
| render_tab_forecast() |
| elif _selected == "Shading Simulator": |
| from ui.tab_shading import render_tab_shading |
| render_tab_shading() |
| elif _selected == "Documentation": |
| from ui.tab_docs import render_tab_docs |
| render_tab_docs() |
|
|