NOTICE: All information contained herein is, and remains
the property of TechnoCore Automate.
Updated : 2026-03-16
ObjServiceOpenWeather integrates with the OpenWeatherMap API to retrieve
current weather conditions, 5-day forecasts, air quality data, and
solar-relevant cloud cover predictions. The service stores time-series
snapshots in data_weather for correlation with solar production
(cloud cover vs PV output), health monitoring (temperature, humidity,
air quality), and load shedding planning.
The module lives in factory.service/package.gekkoridge/ as part of
the GekkoRidge home monitoring package.
OpenWeatherMap provides global weather data via a REST API. The free
tier allows 1000 API calls per day across all endpoints. No OAuth
required — authentication is via a simple API key passed as a query
parameter.
Base URL: https://api.openweathermap.org/data/2.5
The service uses three endpoints:
| Endpoint | HTTP | Description | Free Tier |
|---|---|---|---|
/data/2.5/weather |
GET | Current conditions | Yes |
/data/2.5/forecast |
GET | 5-day / 3-hour forecast (40 entries) | Yes |
/data/2.5/air_pollution |
GET | Air quality index + 8 pollutants | Yes |
All responses are JSON. Location is specified by latitude and longitude
coordinates stored in config.yaml.
config.yaml under base.openweather.api_keyImportant: New API keys take up to 2 hours to activate after
creation. During this period, all API calls return HTTP 401
("Invalid API key"). This is normal — wait and retry.
The free tier includes:
Credentials and location are stored under base.openweather (or
overridden per package):
base:
openweather:
api_key: "your_api_key_here"
latitude: -33.94
longitude: 18.51
Sign up for a free API key at
openweathermap.org/api. New keys
take up to 2 hours to activate after creation.
Module-level settings that do not contain secrets:
settings:
base_url: https://api.openweathermap.org/data/2.5
units: metric
location_name: "Norita, Cape Town"
min_record_interval_seconds: 300
| Setting | Default | Description |
|---|---|---|
base_url |
https://api.openweathermap.org/data/2.5 |
API base URL |
units |
metric |
metric (Celsius, m/s), imperial (Fahrenheit, mph), standard (Kelvin) |
location_name |
— | Human-readable label for logs |
min_record_interval_seconds |
300 | Minimum seconds between DB recordings |
Returns current weather conditions at the configured coordinates.
| Field | Type | Description |
|---|---|---|
temperature |
float | Current temperature (°C) |
feels_like |
float | Apparent temperature accounting for wind/humidity (°C) |
temp_min |
float | Daily minimum temperature (°C) |
temp_max |
float | Daily maximum temperature (°C) |
humidity |
int | Relative humidity (%) |
pressure |
int | Atmospheric pressure at sea level (hPa) |
wind_speed |
float | Wind speed (m/s) |
wind_deg |
int | Wind direction (degrees, 0=N, 90=E, 180=S, 270=W) |
wind_gust |
float | Wind gust speed (m/s) |
clouds |
int | Cloud cover (0-100%) |
visibility |
int | Visibility distance (m, max 10000) |
weather_id |
int | OpenWeatherMap condition code |
weather_main |
str | Category: Clear, Clouds, Rain, Drizzle, Thunderstorm, Snow, Mist, Fog |
weather_desc |
str | Detailed description (e.g. "scattered clouds", "light rain") |
weather_icon |
str | Icon code for UI display (e.g. "02d" = few clouds day) |
sunrise |
int | Sunrise time as Unix timestamp |
sunset |
int | Sunset time as Unix timestamp |
location |
str | City/area name returned by API |
timestamp |
int | Data calculation time as Unix timestamp |
Returns 5-day weather forecast in 3-hour intervals. Up to 40 entries
covering 120 hours ahead.
| Field | Type | Description |
|---|---|---|
timestamp |
int | Forecast time as Unix timestamp |
datetime |
str | Forecast time as "YYYY-MM-DD HH:MM:SS" |
temperature |
float | Forecasted temperature (°C) |
feels_like |
float | Forecasted apparent temperature (°C) |
humidity |
int | Forecasted humidity (%) |
pressure |
int | Forecasted pressure (hPa) |
wind_speed |
float | Forecasted wind speed (m/s) |
wind_gust |
float | Forecasted wind gust (m/s) |
clouds |
int | Forecasted cloud cover (%) |
rain_3h |
float | Rain volume in 3-hour period (mm) |
pop |
float | Probability of precipitation (0.0-1.0) |
weather_main |
str | Weather category |
weather_desc |
str | Detailed description |
Returns current air pollution data at the configured coordinates.
| Field | Type | Description | Health Threshold |
|---|---|---|---|
aqi |
int | Air Quality Index (1-5) | 1=Good, 2=Fair, 3=Moderate, 4=Poor, 5=Very Poor |
co |
float | Carbon monoxide (ug/m3) | >15400 = Very Poor |
no |
float | Nitric oxide (ug/m3) | — |
no2 |
float | Nitrogen dioxide (ug/m3) | >400 = Very Poor |
o3 |
float | Ozone (ug/m3) | >240 = Very Poor |
so2 |
float | Sulphur dioxide (ug/m3) | >1064 = Very Poor |
pm2_5 |
float | Fine particles PM2.5 (ug/m3) | >75 = Very Poor |
pm10 |
float | Coarse particles PM10 (ug/m3) | >200 = Very Poor |
nh3 |
float | Ammonia (ug/m3) | — |
timestamp |
int | Data time as Unix timestamp |
Aggregates the 5-day forecast into daily summaries focused on
solar-relevant metrics. Cloud cover is the primary predictor of
PV generation — 0% clouds = full sun, 100% = overcast.
| Field | Type | Description |
|---|---|---|
date |
str | Date (YYYY-MM-DD) |
avg_clouds |
float | Average cloud cover across all 3h samples (%) |
max_clouds |
int | Peak cloud cover during the day (%) |
min_clouds |
int | Minimum cloud cover during the day (%) |
avg_temp |
float | Average temperature (°C) |
rain_mm |
float | Total accumulated rainfall (mm) |
samples |
int | Number of 3-hour forecast samples for this day |
Solar production estimate: As a rough guide, PV output scales
inversely with cloud cover. At 0% clouds expect ~100% rated output;
at 50% clouds expect ~60-70%; at 100% overcast expect ~15-25%.
Temperature also affects panels — efficiency drops ~0.4% per °C
above 25°C for crystalline silicon.
| Method | Description |
|---|---|
_get(path, extra_params, timeout) |
Central GET dispatcher — appends lat/lon/appid/units, handles errors |
_seconds_since_last_reading() |
Checks data_weather for rate-limit guard |
Stores combined weather + air quality snapshots as time-series rows.
Each row captures a complete environmental snapshot at one point in
time. Guid is generated via get_uuid("WEATHER").
| Column | Type | Description |
|---|---|---|
| Guid | VARCHAR(64) PK | get_uuid("WEATHER") |
| Location | VARCHAR(128) | City/area name from API |
| Latitude | DECIMAL(12,8) | GPS latitude |
| Longitude | DECIMAL(12,8) | GPS longitude |
| Temperature | DECIMAL(6,2) | Current temp (°C) |
| FeelsLike | DECIMAL(6,2) | Apparent temp (°C) |
| TempMin | DECIMAL(6,2) | Daily min temp (°C) |
| TempMax | DECIMAL(6,2) | Daily max temp (°C) |
| Humidity | INT | Relative humidity (%) |
| Pressure | INT | Atmospheric pressure (hPa) |
| WindSpeed | DECIMAL(6,2) | Wind speed (m/s) |
| WindDeg | INT | Wind direction (degrees) |
| WindGust | DECIMAL(6,2) | Wind gust (m/s) |
| Clouds | INT | Cloud cover (%) |
| Visibility | INT | Visibility (m) |
| WeatherId | INT | OpenWeatherMap condition code |
| WeatherMain | VARCHAR(32) | Weather category |
| WeatherDesc | VARCHAR(128) | Detailed description |
| WeatherIcon | VARCHAR(16) | Icon code |
| Sunrise | INT | Sunrise Unix timestamp |
| Sunset | INT | Sunset Unix timestamp |
| Aqi | INT | Air Quality Index (1=Good to 5=Very Poor) |
| Co | DECIMAL(10,2) | Carbon monoxide (ug/m3) |
| No | DECIMAL(10,2) | Nitric oxide (ug/m3) |
| No2 | DECIMAL(10,2) | Nitrogen dioxide (ug/m3) |
| O3 | DECIMAL(10,2) | Ozone (ug/m3) |
| So2 | DECIMAL(10,2) | Sulphur dioxide (ug/m3) |
| Pm25 | DECIMAL(10,2) | PM2.5 fine particles (ug/m3) |
| Pm10 | DECIMAL(10,2) | PM10 coarse particles (ug/m3) |
| Nh3 | DECIMAL(10,2) | Ammonia (ug/m3) |
| Package | VARCHAR(255) | Axion package |
| Module | VARCHAR(255) | ObjServiceOpenWeather |
| CreateTime | DATETIME | DB insert timestamp |
Daily weather aggregates for trend analysis.
ReadingDate | MinTemp | MaxTemp | AvgTemp | AvgHumidity |
| AvgClouds | MaxClouds | AvgWindSpeed |
| MaxWindSpeed | AvgAqi | AvgPm25 | ReadingCount
Joins data_weather with data_solar by hour to correlate cloud
cover with actual PV production. This is the key query for building
a solar generation prediction model.
SELECT
w.Clouds, w.Temperature, w.WeatherDesc,
s.PvPowerTotal, s.DailyGeneration,
s.ConsumptionPowerTotal
FROM data_weather w
INNER JOIN data_solar s
ON DATE(w.CreateTime) = DATE(s.CreateTime)
AND HOUR(w.CreateTime) = HOUR(s.CreateTime)
AND s.Package = w.Package
WHERE w.Package = '{package}'
AND w.CreateTime BETWEEN '{start_time}' AND '{end_time}'
Example analysis: "On days with avg clouds < 30%, PV output averaged
5.2 kW. On days with avg clouds > 70%, PV output dropped to 1.8 kW."
record_weather(min_interval=300) fetches current weather and air
quality (2 API calls) and inserts one combined row. The rate-limit
guard checks data_weather.CreateTime before calling the API.
| Interval | Readings/Day | API Calls/Day | Free Tier (1000) |
|---|---|---|---|
| 5 min (300s) | 288 | 576 | OK |
| 10 min (600s) | 144 | 288 | OK |
| 15 min (900s) | 96 | 192 | OK |
| 30 min (1800s) | 48 | 96 | OK |
The process(context) method dispatches to the appropriate method
based on the command key in the workflow context.
| Command | Required Context | Result Key | Description |
|---|---|---|---|
current |
— | _weather_result (JSON) |
Current conditions |
forecast |
— | _weather_result (JSON) |
5-day / 3-hour forecast |
air_quality |
— | _weather_result (JSON) |
Air pollution data |
record |
— | _weather_result |
Record snapshot to database |
solar_forecast |
— | _weather_result (JSON) |
Daily cloud cover aggregates |
Example workflow context:
context = {"command": "current"}
result = svc.process(context)
weather = json.loads(result["_weather_result"])
# Current weather conditions
python ObjServiceOpenWeather.py current
# 5-day / 3-hour forecast
python ObjServiceOpenWeather.py forecast
# Air quality (AQI, PM2.5, PM10, O3)
python ObjServiceOpenWeather.py air
# Solar-relevant daily cloud cover forecast
python ObjServiceOpenWeather.py solar
# Record current snapshot to database
python ObjServiceOpenWeather.py record
| Tier | Limit | Cost |
|---|---|---|
| Free | 1000 calls/day | $0 |
| Startup | 100,000 calls/day | $0 (limited features) |
| Developer | 3,000 calls/min | $40/month |
| Professional | 30,000 calls/min | Custom |
The free tier is sufficient for home monitoring at 5-minute intervals.
All API calls go through _get() which:
requests.exceptions.Timeout and returns empty dictrequests.exceptions.RequestException for network errorsself.debug() with IconConstants.FAILrecord_weather() returns 0 if the API call fails (no DB write)OpenWeatherMap returns HTTP 401 for invalid API keys and HTTP 429
for rate limit exceeded.
The GekkoRidge package correlates weather data with other services:
| Service | Correlation | Query |
|---|---|---|
ObjServiceSolarman |
Cloud cover vs PV production | get_solar_correlation |
ObjServiceLoadshedding |
Weather during outages | Join by hour |
ObjServiceLibreGlucose |
Temperature/humidity vs glucose | Join by hour |
from ObjServiceOpenWeather import ObjServiceApi
svc = ObjServiceApi()
# Current conditions
weather = svc.get_current()
print(
f"{weather['location']}: "
f"{weather['temperature']}°C "
f"({weather['weather_desc']})"
)
print(
f"Clouds: {weather['clouds']}% "
f"Humidity: {weather['humidity']}%"
)
# Air quality
air = svc.get_air_quality()
aqi_labels = {
1: "Good", 2: "Fair", 3: "Moderate",
4: "Poor", 5: "Very Poor",
}
print(
f"AQI: {air['aqi']} "
f"({aqi_labels[air['aqi']]}) "
f"PM2.5: {air['pm2_5']}"
)
# Solar forecast (daily cloud cover)
for day in svc.get_solar_forecast():
print(
f"{day['date']}: "
f"clouds={day['avg_clouds']}% "
f"rain={day['rain_mm']}mm"
)
# Record to database
svc.record_weather()
Updated : 2026-03-16