Easy-to-manage touch screens
Free, open-source touch screens. Guided USB setup, a drag-and-drop editor and local control. Made for Home Assistant.
How beautiful will tonight's sunset be? Get a score, reasons, and the best time to watch.
LifestylePython · TypeScriptGitHub @zaurusmanOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Sunset beauty predictor — physics-based scoring with weather data
Translation pending
Taken from the repository README
Loading the full README. The saved excerpt is shown below.
Sunset Predictor combines a physics-informed scoring engine with an optional ML calibration layer to predict how beautiful a sunset will be for any location and date. It returns a beauty score (0–100), a category (Poor → Epic), a confidence level, natural-language explanations, the best viewing window, and a 7-day forecast.
The beauty score is a daily quality metric — it answers "will today's sunset be beautiful?", not "are you watching at the exact right moment?".
Weather is sampled at the hour nearest to local sunset. Four components are weighted:Read the full README ↗How good will tonight's sunset be?
Sunset Predictor combines a physics-informed scoring engine with an optional ML calibration layer to predict how beautiful a sunset will be for any location and date. It returns a beauty score (0–100), a category (Poor → Epic), a confidence level, natural-language explanations, the best viewing window, and a 7-day forecast.
sunset-predictor/
├── backend/ Python 3.11 + FastAPI — scoring engine, ML, REST API
│ ├── app/
│ │ ├── api/ Endpoints: /health, /predict, /forecast, /model/info
│ │ ├── core/ Config (pydantic-settings) + logging
│ │ ├── models/ ML model wrapper + artifact registry
│ │ ├── services/ astronomy, weather, scoring, explanation, prediction
│ │ ├── schemas/ Pydantic request/response models
│ │ └── utils/ Math helpers, TTL cache, time utils
│ ├── scripts/
│ │ ├── build_reddit_dataset.py Fetch r/sunset + join weather → labeled CSV
│ │ └── train_model.py Train HistGradientBoostingRegressor
│ └── tests/ pytest suite
└── frontend/ Next.js 15 + TypeScript + Tailwind CSS
└── src/
├── app/ Home page + /forecast route
├── components/ ScoreDial, ForecastChart, LocationSearch, …
└── lib/ Typed API client + utilities
The beauty score is a daily quality metric — it answers "will today's sunset be beautiful?", not "are you watching at the exact right moment?".
Weather is sampled at the hour nearest to local sunset. Four components are weighted:
| Component | Weight | What it measures |
|---|---|---|
| Cloud Quality | 40% | High/mid cloud distribution (catching light) vs low clouds (blocking sun) |
| Atmosphere | 30% | Visibility, aerosol optical depth, clarity |
| Moisture | 20% | Precipitation and humidity penalties |
| Horizon | 10% | Permanent obstruction (buildings, mountains) |
The physics score is optionally calibrated by a trained ML model (HistGradientBoostingRegressor) using the blending formula:
final_score = 0.4 × physics_score + 0.6 × ml_prediction
The ML model is trained on Reddit engagement data (r/sunset upvotes as a proxy label) joined with historical weather. It is completely optional — the app runs in physics-only mode if no model is trained.
cd sunset-predictor/backend
# Create virtual environment
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
# Install dependencies
pip install -r requirements.txt
# Copy env file
cp .env.example .env
# Edit .env if needed (all defaults work without changes)
# Start the API server
uvicorn app.main:app --reload --port 8000
The API is now running at http://localhost:8000. Auto-generated docs: http://localhost:8000/docs
cd sunset-predictor/frontend
# Install dependencies
npm install
# Copy env file
cp .env.example .env.local
# NEXT_PUBLIC_API_URL defaults to http://localhost:8000
# Start dev server
npm run dev
Open http://localhost:3000.
cd sunset-predictor
# First run: copy env files
cp backend/.env.example backend/.env
cp frontend/.env.example frontend/.env
# Build and start
docker-compose up --build
# Stop
docker-compose down
Fetches top posts from r/sunset, joins each post's timestamp with historical weather from Open-Meteo, and saves a labeled CSV for training.
cd sunset-predictor/backend
# Basic (uses public Reddit JSON API, no credentials needed)
python scripts/build_reddit_dataset.py \
--latitude 37.7749 \
--longitude -122.4194 \
--limit 300 \
--out data/reddit_dataset.csv
# More options
python scripts/build_reddit_dataset.py \
--latitude 51.5074 \
--longitude -0.1278 \
--subreddit sunset \
--limit 500 \
--time-filter month \ # hour|day|week|month|year|all
--label-method percentile \ # percentile|log1p|zscore
--image-only \
--out data/reddit_dataset.csv
# With Reddit API credentials (higher rate limits)
# Set REDDIT_CLIENT_ID, REDDIT_CLIENT_SECRET, REDDIT_USER_AGENT in .env
Note: Reddit upvotes are a proxy for engagement, not objective beauty. The label represents "posts that the community engaged with most", which correlates but is not identical to beauty.
cd sunset-predictor/backend
python scripts/train_model.py \
--input data/reddit_dataset.csv \
--output-dir trained_models/ \
--blend-alpha 0.4
Saves:
trained_models/calibration_model.joblib — the trained modeltrained_models/model_metadata.json — RMSE, Spearman r, feature importancesRestart the backend after training to load the new model, or the running server will pick it up on the next restart.
curl http://localhost:8000/health
curl -X POST http://localhost:8000/predict \
-H "Content-Type: application/json" \
-d '{"latitude": 37.7749, "longitude": -122.4194}'
curl -X POST http://localhost:8000/predict \
-H "Content-Type: application/json" \
-d '{
"latitude": 37.7749,
"longitude": -122.4194,
"weather_override": {
"cloud_high": 45,
"cloud_low": 5,
"cloud_total": 52,
"visibility_m": 25000,
"precipitation_mm": 0,
"relative_humidity": 52
}
}'
curl -X POST http://localhost:8000/forecast \
-H "Content-Type: application/json" \
-d '{"latitude": 37.7749, "longitude": -122.4194, "days": 7}'
curl http://localhost:8000/model/info
{
"beauty_score_0_100": 72.4,
"category": "Great",
"confidence_0_100": 68.2,
"reasons": [
"High clouds look very promising for catching vivid colour.",
"Clear air and good visibility will help colours pop.",
"No rain near sunset — conditions are clean."
],
"sunset_time": "2024-06-21T19:45:00Z",
"best_viewing_window_start": "2024-06-21T19:35:00Z",
"best_viewing_window_end": "2024-06-21T20:10:00Z",
"algorithm_version": "1.0.0",
"ml_model_used": false,
"physics_component_breakdown": {
"cloud_quality_score": 78.3,
"atmosphere_score": 74.1,
"moisture_score": 96.0,
"horizon_score": 89.5,
"weighted_physics_score": 72.4
}
}
cd sunset-predictor/backend
pytest tests/ -v
Tests cover:
aerosol_is_estimated flag in the response indicates this.POST /rate stores each rating with its raw weather inputs — in Postgres when DATABASE_URL is set, else backend/data/ratings.jsonl. backend/scripts/ratings.py exports them and builds a training table; see its docstring.| Layer | Technologies |
|---|---|
| Backend | Python 3.11, FastAPI, Pydantic v2, Uvicorn, httpx |
| ML / Data | scikit-learn, XGBoost, pandas, numpy, joblib |
| Astronomy | astral |
| Weather | Open-Meteo (free, no key required) |
| Frontend | Next.js 15, TypeScript, Tailwind CSS, Recharts, Lucide |
| Infra | Docker, docker-compose |
Afterglow can be added to the Home Screen and ping users ~4 h before an Epic (≥ 80) sunset at places they belled. iOS requires the app to be installed to the Home Screen (iOS 16.4+).
Setup:
DATABASE_URL on Render.python backend/scripts/generate_vapid_keys.py → set VAPID_PUBLIC_KEY,
VAPID_PRIVATE_KEY (and optionally VAPID_SUBJECT) on Render.ALERTS_SECRET → set it on Render.ALERTS_API_URL (the Render URL) and ALERTS_SECRET.Cost: one prediction per 0.1° cell per day, only for belled places, at ~4 h before local sunset — independent of subscriber count. Alert checks never start a climatology build.