Python • APIs • Weather.gov • CLI Menus

Tiny Terminal Weather App

This project turns the weather logic from my larger Flask seed tracking app into a standalone Python terminal tool. The app can get a forecast from Weather.gov, print the next few forecast periods, save a default location, and run from an interactive menu.

The first version was a simple command-line script. The upgraded version is more advanced: it works like a small terminal application with menu options, saved settings, one-time lookups, direct command-line mode, and better input validation.

Project context: this is a small API project, but it connects several real skills: Python scripting, public APIs, JSON parsing, command-line arguments, environment variables, saved settings, and turning a one-off script into a reusable tool.
Interactive menu Choose saved location, ZIP lookup, coordinates, or settings.
Weather.gov API Use /points to find the correct forecast endpoint.
JSON forecast periods Read temperature, wind, summary, and details.
Terminal output Print a clean forecast from a small Python app.
The project starts as an API script, then grows into a reusable terminal app.

What I built

I built a Python program called weather.py. It can be used two ways: as an interactive menu app or as a direct command-line tool.

Interactive

Menu mode

Running python3 weather.py opens a menu where I can save a location, fetch the forecast, change the number of periods, or run one-time lookups.

CLI

Direct mode

Running the script with --zip or --coords skips the menu and prints the forecast immediately.

Settings

Saved location

The app saves a location and forecast-period count in weather_settings.json, making it feel more like a real terminal utility.

Why this version is more advanced

The first version proved that the API request worked. This version adds application behavior around that working core.

01

Stores settings

The app can remember a saved location instead of requiring a ZIP code every time.

02

Supports two workflows

It works both as an interactive menu app and as a direct command-line tool.

03

Validates input

The script checks ZIP codes, coordinate ranges, and forecast-period counts.

04

Handles errors

HTTP errors, network errors, missing data, and bad input are caught and printed cleanly.

How it connects to my seed app

This project is not random. It came from a real feature in my larger Flask seed tracking app. The larger app uses weather data for garden planning. This project pulls out the core weather API pattern and turns it into a smaller standalone learning project.

Seed app pattern Tiny terminal version Why it matters
fetch_json() Reusable helper for API requests. Keeps headers, timeout, and JSON parsing in one place.
User-Agent Set by WEATHER_USER_AGENT or a default string. Identifies the app when making Weather.gov requests.
ZIP lookup Uses a ZIP code to find latitude and longitude. Makes the app easier to run without manually entering coordinates.
Weather.gov /points Finds the correct forecast URL for a location. The first API call gives the app the next API endpoint to follow.
Forecast periods Prints the next few periods in the terminal. Turns nested JSON into useful readable output.

How the app works

The app follows a clean request chain. This is the important part of the project.

01

Get location

The user enters a ZIP code, coordinates, or uses a saved location.

02

Resolve coordinates

If a ZIP code is used, the script turns it into latitude and longitude.

03

Ask Weather.gov

The script calls Weather.gov /points to find the forecast URL.

04

Print forecast

The script fetches forecast periods and prints them in a readable format.

Main API lesson: some APIs return links to other endpoints. The first request may not contain the final data. It may tell the program where to request the final data next.

Direct mode still works

Even with the menu, the script still works as a normal command-line tool. That matters because direct commands are easier to automate later.

# Open the interactive menu
python3 weather.py

# One-time ZIP code forecast
python3 weather.py --zip 11234

# One-time coordinate forecast
python3 weather.py --coords 40.7128 -74.0060

# Print more forecast periods
python3 weather.py --zip 11234 --count 5

Settings file

The menu version creates a small JSON settings file so the app can remember the saved location and forecast-period count.

weather_settings.json

A saved location might look like this:

{
  "location": {
    "type": "zip",
    "zip_code": "11234",
    "latitude": "40.6215",
    "longitude": "-73.9349",
    "label": "Brooklyn, NY"
  },
  "count": 3
}
Git note: if I do not want my saved location committed, I should add weather_settings.json to .gitignore.
echo "weather_settings.json" >> .gitignore

AI-assisted build note

This version was built with AI assistance, but it was not created from nothing. I already had working weather logic in my Flask seed app, including the request helper, Weather.gov flow, ZIP-code idea, and forecast parsing pattern.

What I provided

Existing code and project goals

I provided the weather logic from my seed app and explained the goal: make a tiny standalone terminal weather project that could run with a ZIP code, coordinates, and an interactive menu.

What AI helped with

Refactoring into a CLI app

AI helped simplify the larger Flask pattern into a smaller script, add the menu, structure the functions, keep direct command-line mode, and make the project easier to explain as a LaunchShell writeup.

How I would describe it: this is an AI-assisted refactor and learning project. I used my existing app logic as the base, used AI to help turn it into a cleaner standalone terminal tool, then ran and tested the result locally.

Important code pieces

The full script is longer because of the menu and error handling. These are the most important pieces.

Reusable API helper

This keeps the Weather.gov headers and JSON parsing in one place.

def fetch_json(url):
    request_headers = {
        "Accept": "application/geo+json, application/json",
        "User-Agent": DEFAULT_USER_AGENT,
    }

    req = urllib.request.Request(url, headers=request_headers)

    with urllib.request.urlopen(req, timeout=15) as response:
        return json.loads(response.read().decode("utf-8"))

Forecast URL lookup

Weather.gov /points returns the correct forecast endpoint for the location.

def fetch_forecast_url(latitude, longitude):
    points_url = f"https://api.weather.gov/points/{latitude},{longitude}"
    data = fetch_json(points_url)

    properties = data.get("properties", {})
    forecast_url = properties.get("forecast")

    if not forecast_url:
        raise ValueError("Weather.gov did not return a forecast URL.")

    return forecast_url

Saved settings

JSON makes the saved location simple and readable.

def load_settings():
    if not os.path.exists(SETTINGS_PATH):
        return {}

    with open(SETTINGS_PATH, "r", encoding="utf-8") as file:
        return json.load(file)


def save_settings(settings):
    with open(SETTINGS_PATH, "w", encoding="utf-8") as file:
        json.dump(settings, file, indent=2)
        file.write("\n")

Menu loop

The app keeps showing the menu until the user chooses quit.

def interactive_menu():
    settings = load_settings()

    while True:
        print_header()
        print_menu(settings)

        choice = input("Select Option > ").strip().lower()

        if choice == "1":
            show_forecast_for_location(settings["location"], settings.get("count", 3))
        elif choice == "q":
            print("Goodbye.")
            break

Tested output

I tested the script with ZIP code 11234. The exact forecast changes over time, but this confirmed the API request chain worked.

python3 weather.py --zip 11234

Weather forecast for New York, NY
=================================

Overnight
---------
Temp: 54°F
Wind: 5 mph W
Short: Mostly Clear
Details: Mostly clear, with a low around 54. West wind around 5 mph.

Saturday
--------
Temp: 77°F
Wind: 3 to 17 mph SW
Short: Sunny
Details: Sunny. High near 77, with temperatures falling to around 72 in the afternoon. Southwest wind 3 to 17 mph.

Saturday Night
--------------
Temp: 62°F
Wind: 8 to 17 mph SW
Short: Partly Cloudy
Details: Partly cloudy. Low around 62, with temperatures rising to around 65 overnight. Southwest wind 8 to 17 mph.
Note: Weather.gov may return a broader label such as New York, NY even when the ZIP code is inside Brooklyn. The script can prefer the ZIP lookup label if I want the displayed location to be more specific.

Project files

The finished project is still small.

tiny-terminal-weather/
├── weather.py
├── README.md
├── .gitignore
└── weather_settings.json   # generated locally, optional/private

What I learned

This project is useful because it starts with one API request and grows into a real command-line tool.

APIs

Follow the endpoint chain

The first Weather.gov request gives the app a forecast URL. The app then follows that URL to get the actual forecast periods.

Python

Small scripts can mature

A simple one-shot script can become a menu app with saved settings, validation, error handling, and direct command-line support.

Workflow

AI can help refactor

AI assistance was useful for turning existing working code into a smaller teaching project, but the important part was understanding, testing, and documenting the result.

Future improvements

The current version works, but there are natural upgrades that would make it even more useful.

Simple upgrades

Make it nicer

  • Add a compact --short output mode.
  • Add hourly forecast support.
  • Cache the forecast for a short time to avoid repeated requests.
  • Show the saved location at startup with clearer formatting.
Project upgrades

Connect it to other systems

  • Run it on a Raspberry Pi dashboard.
  • Use it in a cron job for daily terminal weather summaries.
  • Generate a static JSON file for a website.
  • Reuse the logic inside a Flask route later.
Related guide: this project pairs with the API guide because it turns the idea of APIs into a working terminal tool.

Final idea

This project shows how a small script becomes a real tool. The first version proved the Weather.gov API worked. The advanced version adds an interactive menu, saved settings, direct command-line mode, input validation, and cleaner error handling. It is still small, but it now feels like a reusable Linux terminal utility.