Deterministic Weather Monitoring Plugin for NMS_Tools
check_weather.py is an operator‑grade weather monitoring plugin designed for Nagios, Icinga, Thruk, and the broader NMS_Tools suite.
It provides deterministic, timestamp‑aligned weather data using the Open‑Meteo hourly API, with full support for:
- ZIP, city, and lat/long resolution
- Metric and imperial units
- Threshold evaluation
- Nagios‑compliant perfdata
- JSON and verbose output modes
- Wind, gusts, humidity, precipitation, cloud cover
- Condition codes + human‑readable condition text
- Deterministic caching and logging
This tool is built for reliability, clarity, and long‑term maintainability.
check_weather.py uses a deterministic, backend‑driven icon mapping based on the Weather Icons project by Erik Flowers:
- Project: https://erikflowers.github.io/weather-icons/
- License: SIL OFL 1.1 (fonts), MIT (CSS)
- Icon files used: SVG variants (e.g.,
wi-day-sunny.svg,wi-night-cloudy.svg)
Icons are selected entirely in the backend using:
- WMO weather codes from Open‑Meteo
- Sunrise/sunset timestamps to determine day vs. night
- A deterministic mapping table that resolves each WMO code to:
- a normalized condition text (
context) - a specific icon filename (
icon)
- a normalized condition text (
- The UI does not perform any weather logic.
- The UI only renders the icon filename provided by the backend.
- All modes (current, hourly, weekly) use the same mapping.
- Icons are guaranteed to be consistent across all output formats.
{
"context": "Clear sky",
"icon": "wi-day-sunny.svg"
}Nighttime icons are selected using the local sunrise/sunset times returned by Open‑Meteo. The backend supports expressive “alt” night icons (e.g., wi-night-alt-showers.svg) for improved clarity.
check_weather.py uses a deterministic mapping from WMO weather codes to:
- a normalized condition text (
context) - a specific icon filename (
icon)
This ensures consistent behavior across current, hourly, and weekly modes.
| WMO Code | Meaning | Context Text | Icon Filename |
|---|---|---|---|
| 0 | Clear sky | Clear sky | wi-day-sunny.svg / wi-night-clear.svg |
| 1 | Mainly clear | Mainly clear | wi-day-sunny.svg / wi-night-clear.svg |
| 2 | Partly cloudy | Partly cloudy | wi-day-cloudy.svg / wi-night-alt-cloudy.svg |
| 3 | Overcast | Overcast | wi-cloudy.svg / wi-night-cloudy.svg |
| 45, 48 | Fog / Depositing rime fog | Fog | wi-fog.svg / wi-night-fog.svg |
| 51–55 | Drizzle (light–dense) | Drizzle | wi-sprinkle.svg / wi-night-alt-sprinkle.svg |
| 61–65 | Rain (light–heavy) | Rain | wi-rain.svg / wi-night-alt-rain.svg |
| 80–82 | Rain showers (light–violent) | Rain showers | wi-showers.svg / wi-night-alt-showers.svg |
| 71–75 | Snow (light–heavy) | Snow | wi-snow.svg / wi-night-alt-snow.svg |
| 85–86 | Snow showers | Snow showers | wi-snow.svg / wi-night-alt-snow.svg |
| 95 | Thunderstorm | Thunderstorm | wi-thunderstorm.svg / wi-night-alt-thunderstorm.svg |
| 96–99 | Thunderstorm w/ hail | Thunderstorm (hail) | wi-hail.svg / wi-night-alt-hail.svg |
The backend determines day/night using:
sunrisesunset- the timestamp of the forecast entry
This ensures correct icon selection even for:
- hourly forecasts crossing midnight
- weekly forecasts with sunrise/sunset per day
- verbose and JSON modes
The Weather Icons project includes hundreds of icons, but check_weather uses a minimal, deterministic subset to avoid ambiguity and ensure consistent UI rendering.
Weather icons are not a UI concern — they are a data normalization concern.
The backend selects icons because:
-
WMO codes require interpretation
The UI should not need to understand meteorology or WMO classification. -
Day/night logic requires sunrise/sunset timestamps
Only the backend has access to:- the location’s timezone
- the day’s sunrise/sunset
- the forecast timestamp
-
Consistency across modes
Current, hourly, and weekly modes all use the same mapping table. -
Deterministic output
The UI receives:{ "context": "Clear sky", "icon": "wi-day-sunny.svg" } -
Separation of concerns
- Backend: meteorology, normalization, mapping
- UI: display only
The Weather Icons project was chosen because:
- It is open‑source (MIT + SIL OFL 1.1)
- It provides a complete set of day/night variants
- It is widely used and stable
- It matches the operator‑grade aesthetic of NMS_Tools
- SVG format ensures crisp rendering at any size
Open‑Meteo does not provide icons.
Other providers (e.g., OpenWeatherMap) do, but:
- They are raster PNGs
- They are low‑resolution
- They are provider‑specific
- They do not include night variants
- They cannot be redistributed freely
Weather Icons solves all of these problems.
Because that leads to:
- duplicated logic
- inconsistent behavior
- mismatched day/night handling
- drift between UI and backend
- increased maintenance burden
The backend is the single source of truth.
(Updated 2026‑05)
The check_weather tool uses a hybrid classifier to determine how each Weather Icons SVG should be recolored.
Classification is now handled entirely by analyzer.py and merges filename semantics with geometry analysis.
Weather Icons encode most semantics directly in the filename.
The exporter extracts:
sun/moon(day/night)cloudrain(rain, showers, sprinkle, mix)snow(snow, sleet, hail)thunder(storm, lightning)fog(fog, haze)wind
This is the authoritative source for precipitation and special effects.
SVG paths are parsed and curves are approximated as line segments.
Geometry is used only to detect:
sunmooncloud
Night icons suppress sun geometry to avoid false positives.
Final groups are:
final_groups = filename_groups ∪ geometry_base_shape
Examples:
wi-day-rain.svg → ['sun', 'rain'] wi-night-snow.svg → ['moon', 'snow'] wi-cloudy.svg → ['cloud'] wi-night-fog.svg → ['moon', 'fog']
Recoloring is performed by recolor.py.
Each <path> element is assigned to one or more groups and recolored using the palette defined in the engine.
After processing all icons, the exporter prints and logs:
- total icons
- group counts
- coverage percentages
- groups‑per‑icon histogram
- day/night breakdown
This summary acts as a regression surface to detect classification drift.
The legacy classifier.py has been removed.
All classification is now handled by analyzer.py.
A small HTML/JS/CSS demo is included in this directory (weather_demo.html,
weather.js, weather_demo.css). These files are not part of the NMS_Tools
suite and are not used by any monitoring scripts. They provide a visual
demonstration of how check_weather.py output can be rendered in a browser
using the same JSON schema. The demo is provided for testing and experimentation.
Hourly mode now begins at the next hour ≥ local time, not at midnight.
The backend slices the raw Open‑Meteo hourly arrays before flattening, ensuring:
- Always 24 hours of future data
- No stale hours
- No midnight anchoring
- Deterministic alignment across all hourly fields
Weekly mode now always begins at today, even if the provider returns yesterday.
The backend slices the raw daily arrays before enrichment.
- Always 7 days
- Never includes yesterday
- Fully enriched (context, icon, wind max, units)
All modes now include normalized, deterministic fields:
context— human‑readable condition texticon— backend‑selected icon filenamewind_mph_max/wind_kph_max— weekly max wind- Unit‑converted fields for temperature, wind, visibility, pressure, precipitation
Verbose mode uses these enriched fields directly.
- Temperature
- Wind speed
- Wind gusts
- Humidity
- Precipitation
- Cloud cover
- Weather condition codes + text
- Rolling 24‑hour forecast window (next hour → +24h)
- ZIP code
- City + optional state
- Latitude/longitude
- Temperature (hot/cold)
- Wind
- Gust
- Humidity
- Precipitation
- Cloud cover
- Nagios
- JSON
- Verbose
- Quiet
- Timestamp alignment
- Rounded numeric values
- Clean error handling
- Predictable caching
- Operator‑grade logging
- Rolling hourly slicing and weekly day‑slicing performed in fetch layer
- Backend‑normalized condition text and icon filenames
Requires:
- Python 3.8+
- requests
Install:
pip install requests./check_weather.py --location "Saint John, KS"./check_weather.py --location "Saint John, KS" --units imperial./check_weather.py --location 67576 -j./check_weather.py --location "Saint John, KS" -vHourly mode now returns the next 24 hours starting from the next hour ≥ local time.
./check_weather.py --location 67576 -v -HWeekly mode now always returns 7 days beginning at the current local date.
./check_weather.py --location 67576 -W -v./check_weather.py --location 67576 \
--warning-wind 25 \
--critical-wind 35 \
--warning-gust 40 \
--critical-gust 50./check_weather.py --location 67576 --show-location-details./check_weather.py --location 67576 --log-dir ~/Logscheck_weather.py uses three distinct provider components:
- Open‑Meteo
- Selected via --provider (validated enum)
- Base URL: [https://api.open-meteo.com/v1/forecast]
- Used for all weather data retrieval.
- Zippopotam.us
- URL pattern: [https://api.zippopotam.us//]
- Open‑Meteo Geocoding
- URL pattern: [https://geocoding-api.open-meteo.com/v1/search?name=]
The resolved location block includes:
- Provider name
- Provider URL
- Latitude / longitude
- Resolved city, state, country
These appear in both JSON (resolved_location) and verbose (--show-location-details) output.
--provider {open-meteo}The provider flag is validated but currently non‑functional.
check_weather always uses:
- Zippopotam.us for ZIP → lat/lon
- Open‑Meteo Geocoding for city/state
- Open‑Meteo for weather data
The [--provider] value is logged for operator visibility but does not change execution behavior. Reserved for future multi‑provider support.
--include-gusts Include wind gusts even without thresholds
--include-precip Include precipitation fields
--include-clouds Include cloud cover fields These flags control which fields appear in:
- Verbose output
- JSON output
- Perfdata
--force-cache Force reading from cache even if API is available
--ignore-cache Ignore cache entirely
--ignore-ttl Ignore TTL when reading cache
--cache-info Display cache metadata Logging is disabled in Nagios mode. Nagios mode is the default output mode, and plugins must remain side‑effect‑free. Logging only activates when using --verbose, --json, or --quiet.
Enable logging:
--log-dir /path/to/logsLog entries include:
- [START] metadata banner
- [WEATHER] location + weather blocks
- [RESULT] final Nagios state + message
- [END] termination marker
Rotation controlled by:
--log-max-mb <size>OK: Weather normal: 56.66°F, 20.26 mph | temp=56.66;; wind=20.26;; humidity=31.00;; cloud=54.00;;Location Resolution Details:
Input: 67576
Location Provider: zippopotam.us
Location Provider URL: https://api.zippopotam.us/US/67576
Weather Provider: open-meteo
Weather Provider URL: https://api.open-meteo.com/v1/forecast
Resolved Name: Saint John, Kansas, US
Latitude: 38.0309
Longitude: -98.7647
Weather API URL: https://api.open-meteo.com/v1/forecast?latitude=...
Status: OK
Message: Weather normal: 56.66°F, 20.26 mph
Location: Saint John, Kansas 67576, US
Temperature: 56.66°F (13.70°C)
Wind Speed: 20.26 mph (32.60 kph)
Humidity: 31.00%
Condition: Partly cloudy
Source: Live API{
"status": "OK",
"message": "Weather normal: 56.66°F, 20.26 mph",
"location": "Saint John, Kansas 67576, US",
"data": {
"time": "2026-04-11T09:45",
"temperature_f": 56.66,
"wind_mph": 20.26,
"humidity": 31,
"cloudcover": 54,
"condition_text": "Partly cloudy",
"source": "Live API"
},
"resolved_location": {
"input": "67576",
"weather_provider": "open-meteo",
"weather_provider_url": "https://api.open-meteo.com/v1/forecast",
"location_provider": "zippopotam.us",
"location_provider_url": "https://api.zippopotam.us/US/67576",
"city": "Saint John",
"state": "Kansas",
"country": "US",
"latitude": 38.0309,
"longitude": -98.7647
},
"runtime_ms": 763.0
}{
"status": "OK",
"message": "Hourly forecast retrieved",
"location": "Saint John, Kansas 67576, US",
"data": {
"mode": "hourly",
"hours": [
{
"time": "2026-04-27T11:00",
"temperature_f": 54.68,
"wind_mph": 14.42,
"humidity": 84,
"cloudcover": 100,
"precip_in": 0.0,
"context": "Overcast",
"icon": "wi-cloudy.svg"
},
...
],
"units": "imperial",
"source": "Live API"
}
}{
"status": "OK",
"message": "Weekly forecast retrieved",
"location": "Saint John, Kansas 67576, US",
"data": {
"mode": "weekly",
"days": [
{
"date": "2026-04-27",
"temp_max_f": 66.02,
"temp_min_f": 53.06,
"wind_mph_max": 17.15,
"precip_in": 0.0,
"context": "Fog",
"icon": "wi-night-fog.svg"
},
...
],
"units": "imperial",
"source": "Live API"
}
}The following features are planned for the next release:
- Verbose mode will display icon filenames next to condition text.
- New
--debugflag will expose backend decision details (slice indices, sunrise/sunset logic, WMO mappings). - New
--self-testmode will validate slicing, enrichment, and mapping without hitting the API. - Minutely precipitation support (
--minutely) for short‑term rain alerts. - Alerts mode (
--alerts) using Open‑Meteo NWS alert feed for US locations.
- Uses Open‑Meteo hourly API for deterministic, timestamp‑aligned data
- Uses Zippopotam.us + Open‑Meteo Geocoding for location resolution
- All numeric values rounded to 2 decimals
- Designed for graphing (PNP4Nagios, Grafana, etc.)
- Logging, caching, and condition‑text support fully implemented