From 190420e908dde48857719a2753b7b00ffc2a6c8a Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Sat, 7 Feb 2026 17:26:07 -0600 Subject: [PATCH] Added a SCHEMA.md with espress documentation of the JSON output schemas emitted by weatherfeeder. --- SCHEMA.md | 166 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 SCHEMA.md diff --git a/SCHEMA.md b/SCHEMA.md new file mode 100644 index 0000000..428b3d3 --- /dev/null +++ b/SCHEMA.md @@ -0,0 +1,166 @@ +# weatherfeeder Canonical Schemas + +This document expressly defines the JSON payload schemas emitted by weatherfeeder for: + +- `weather.observation.v1` +- `weather.forecast.v1` +- `weather.alert.v1` + +These schemas are defined by the `internal/model` structs and their JSON tags. + +## Shared Conventions + +- Timestamps are JSON strings in RFC3339Nano format. +- Optional fields are omitted when unknown (`omitempty` behavior). +- Numeric measurements are normalized to metric units: + - `*C` = Celsius + - `*Kmh` = kilometers/hour + - `*Pa` = Pascals + - `*Meters` = meters + - `*Mm` = millimeters + - `*Percent` = percent (0-100) +- `conditionCode` is a WMO weather interpretation code (`int`). + - Unknown/unmappable is `-1`. + +## `weather.observation.v1` + +Payload type: `model.WeatherObservation` + +| Field | Type | Required | Notes | +|---|---:|:---:|---| +| `stationId` | string | no | Provider station/location identifier | +| `stationName` | string | no | Human station name | +| `timestamp` | timestamp string | yes | Observation timestamp | +| `conditionCode` | int | yes | WMO code (`-1` unknown) | +| `conditionText` | string | no | Canonical short condition text | +| `isDay` | bool | no | Day/night hint | +| `providerRawDescription` | string | no | Provider-specific evidence text | +| `textDescription` | string | no | Legacy/transitional text description | +| `iconUrl` | string | no | Legacy/transitional icon URL | +| `temperatureC` | number | no | Celsius | +| `dewpointC` | number | no | Celsius | +| `windDirectionDegrees` | number | no | Degrees | +| `windSpeedKmh` | number | no | km/h | +| `windGustKmh` | number | no | km/h | +| `barometricPressurePa` | number | no | Pascals | +| `seaLevelPressurePa` | number | no | Pascals | +| `visibilityMeters` | number | no | Meters | +| `relativeHumidityPercent` | number | no | Percent | +| `apparentTemperatureC` | number | no | Celsius | +| `elevationMeters` | number | no | Meters | +| `rawMessage` | string | no | Provider raw message (for example METAR) | +| `presentWeather` | array | no | Provider-specific structured weather fragments | +| `cloudLayers` | array | no | Cloud layer details | + +### `cloudLayers[]` (`model.CloudLayer`) + +| Field | Type | Required | Notes | +|---|---:|:---:|---| +| `baseMeters` | number | no | Cloud base altitude in meters | +| `amount` | string | no | Provider amount code/text | + +### `presentWeather[]` (`model.PresentWeather`) + +| Field | Type | Required | Notes | +|---|---:|:---:|---| +| `raw` | object | no | Provider-specific object | + +## `weather.forecast.v1` + +Payload type: `model.WeatherForecastRun` + +`product` values are: + +- `"hourly"` +- `"narrative"` +- `"daily"` + +| Field | Type | Required | Notes | +|---|---:|:---:|---| +| `locationId` | string | no | Provider location identifier | +| `locationName` | string | no | Human location name | +| `issuedAt` | timestamp string | yes | Forecast run issue/generated time | +| `updatedAt` | timestamp string | no | Optional later update time | +| `product` | string | yes | One of `hourly`, `narrative`, `daily` | +| `latitude` | number | no | Degrees | +| `longitude` | number | no | Degrees | +| `elevationMeters` | number | no | Meters | +| `periods` | array | yes | Forecast periods, intended chronological order | + +### `periods[]` (`model.WeatherForecastPeriod`) + +| Field | Type | Required | Notes | +|---|---:|:---:|---| +| `startTime` | timestamp string | yes | Start of validity window | +| `endTime` | timestamp string | yes | End of validity window | +| `name` | string | no | Human label (for example "Tonight") | +| `isDay` | bool | no | Day/night hint | +| `conditionCode` | int | yes | WMO code (`-1` unknown) | +| `conditionText` | string | no | Canonical short condition text | +| `providerRawDescription` | string | no | Provider-specific evidence text | +| `textDescription` | string | no | Short narrative text | +| `detailedText` | string | no | Longer narrative text | +| `iconUrl` | string | no | Legacy/transitional icon URL | +| `temperatureC` | number | no | Celsius | +| `temperatureCMin` | number | no | Celsius (aggregated periods) | +| `temperatureCMax` | number | no | Celsius (aggregated periods) | +| `dewpointC` | number | no | Celsius | +| `relativeHumidityPercent` | number | no | Percent | +| `windDirectionDegrees` | number | no | Degrees | +| `windSpeedKmh` | number | no | km/h | +| `windGustKmh` | number | no | km/h | +| `barometricPressurePa` | number | no | Pascals | +| `visibilityMeters` | number | no | Meters | +| `apparentTemperatureC` | number | no | Celsius | +| `cloudCoverPercent` | number | no | Percent | +| `probabilityOfPrecipitationPercent` | number | no | Percent | +| `precipitationAmountMm` | number | no | Liquid-equivalent millimeters | +| `snowfallDepthMm` | number | no | Millimeters | +| `uvIndex` | number | no | Unitless UV index | + +## `weather.alert.v1` + +Payload type: `model.WeatherAlertRun` + +| Field | Type | Required | Notes | +|---|---:|:---:|---| +| `locationId` | string | no | Provider location identifier | +| `locationName` | string | no | Human location name | +| `asOf` | timestamp string | yes | Snapshot timestamp | +| `latitude` | number | no | Degrees | +| `longitude` | number | no | Degrees | +| `alerts` | array | yes | Active alerts in the snapshot | + +### `alerts[]` (`model.WeatherAlert`) + +| Field | Type | Required | Notes | +|---|---:|:---:|---| +| `id` | string | yes | Provider-stable ID, often URL/URI | +| `event` | string | no | Classification/event label | +| `headline` | string | no | Alert headline | +| `severity` | string | no | Example: Extreme/Severe/Moderate/Minor/Unknown | +| `urgency` | string | no | Example: Immediate/Expected/Future/Past/Unknown | +| `certainty` | string | no | Example: Observed/Likely/Possible/Unlikely/Unknown | +| `status` | string | no | Example: Actual/Exercise/Test/System/Unknown | +| `messageType` | string | no | Example: Alert/Update/Cancel | +| `category` | string | no | Example: Met/Geo/Safety/Rescue/Fire/Health/Env/Transport/Infra/CBRNE/Other | +| `response` | string | no | Example: Shelter/Evacuate/Prepare/Execute/Avoid/Monitor/Assess/AllClear/None | +| `description` | string | no | Narrative description | +| `instruction` | string | no | Recommended actions | +| `sent` | timestamp string | no | Provider-dependent | +| `effective` | timestamp string | no | Provider-dependent | +| `onset` | timestamp string | no | Provider-dependent | +| `expires` | timestamp string | no | Provider-dependent | +| `areaDescription` | string | no | Provider area text | +| `senderName` | string | no | Alert sender/source name | +| `references` | array | no | Related alerts | + +### `references[]` (`model.AlertReference`) + +| Field | Type | Required | Notes | +|---|---:|:---:|---| +| `id` | string | no | Related alert ID/URI | +| `identifier` | string | no | Provider identifier when distinct | +| `sender` | string | no | Sender | +| `sent` | timestamp string | no | Timestamp | +