Weather
If your Home Assistant has a weather.* entity wired up (most installs do), push the hourly forecast once daily so the optimizer plans against your local weather rather than a generic fallback. The Hungry Machines app does this for you automatically; custom integrations should set up a daily rest_command.
Each push is stored for the next optimization run and retained as history that later helps fill outdoor-temperature gaps. If no fresh push is available when the overnight run happens (older than 24 hours, or never pushed), a fallback weather source is used for that account.
POST /api/v1/weather
Section titled “POST /api/v1/weather”Auth: Required (Bearer JWT)
Required vs. optional
Section titled “Required vs. optional”Only forecast.hourly_temps_f (24–72 values) is required. forecast.forecast_date and the humidity, wind, cloud-cover, precipitation and solar-irradiance arrays are optional — send the ones your weather source exposes.
Example
Section titled “Example”curl -X POST https://api.hungrymachines.io/api/v1/weather \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "forecast": { "hourly_temps_f": [54.2, 53.8, "...(24-72 values)"], "hourly_humidity": [62, 64, "..."], "hourly_wind_mph": [3.1, 2.8, "..."] } }'Request body
Section titled “Request body”{ "forecast": { "hourly_temps_f": [54.2, 53.8, 53.5, "..."], "hourly_humidity": [62, 64, 65, "..."], "hourly_wind_mph": [3.1, 2.8, 2.5, "..."] }}| Field | Type | Required | Validation |
|---|---|---|---|
forecast.hourly_temps_f | array[float] | Yes | 24–72 hourly values, each in [-50, 150] °F. Element 0 is local midnight (hour 0) of forecast_date in the account’s timezone; the array walks forward one hour per element. |
forecast.forecast_date | string | No | YYYY-MM-DD: the local calendar date that element 0 covers. Defaults to today in the account’s timezone. Validated as an ISO date; a malformed value is a 400. |
forecast.hourly_humidity | array[float] | null | No | Same length as hourly_temps_f. Each value in [0, 100] percent. Omit or send null to skip. |
forecast.hourly_wind_mph | array[float] | null | No | Same length as hourly_temps_f. Each value in [0, 200] mph. Omit or send null to skip. |
forecast.hourly_cloud_coverage_pct | array[float] | null | No | Same length as hourly_temps_f. Each value in [0, 100] percent. Omit or send null to skip. |
forecast.hourly_precipitation_mm | array[float] | null | No | Same length as hourly_temps_f. Each value in [0, 300] mm. Omit or send null to skip. |
forecast.hourly_solar_irradiance_w | array[float] | null | No | Same length as hourly_temps_f. Each value in [0, 1500] W/m². Omit or send null to skip. |
Response (201)
Section titled “Response (201)”{ "accepted_hours": 48, "pushed_at": "2025-11-18T03:30:00+00:00"}| Field | Type | Notes |
|---|---|---|
accepted_hours | integer | Number of hourly entries stored from hourly_temps_f |
pushed_at | string | UTC ISO 8601 timestamp when the row was saved |
Errors
Section titled “Errors”| Status | Detail | Cause |
|---|---|---|
| 400 | "<name> must contain between 24 and 72 hourly values" | Array length out of bounds. <name> is the field path, e.g. forecast.hourly_temps_f. |
| 400 | "<name>[<i>]=<v> is outside the allowed range [<min>, <max>]" | Value out of range |
| 400 | "<name>[<i>] is not a finite number" | Non-finite value |
| 400 | "<name> length must match hourly_temps_f" | An optional array’s length disagrees with hourly_temps_f |
| 400 | "forecast.forecast_date must be an ISO date (YYYY-MM-DD)" | forecast_date is not a valid ISO date |
| 401 | "Not authenticated" | Missing or invalid token |
| 422 | array detail | forecast or forecast.hourly_temps_f missing, or an array element that is not a number. See Errors, Limits & Stability. |
| 503 | "Database unavailable" | Database not reachable |
| 503 | "Failed to store weather forecast" | DB write failed |
When to push
Section titled “When to push”Push once per day, before the account’s overnight optimization run. Any push from the last 24 hours counts as fresh, so missing a single day simply falls back to a server-side weather source (or the most recent prior push).
A typical Home Assistant rest_command + automation:
rest_command: hm_push_weather: url: "https://api.hungrymachines.io/api/v1/weather" method: POST headers: Authorization: "Bearer !secret hm_access_token" Content-Type: "application/json" payload: > { "forecast": { "hourly_temps_f": {{ state_attr('weather.home', 'forecast') | map(attribute='temperature') | list | tojson }} } }
automation: - alias: "HM — push weather forecast at 03:30 UTC" trigger: - platform: time at: "03:30:00" action: - service: rest_command.hm_push_weatherAdjust the entity (weather.home) and forecast attribute path to match what your weather integration exposes. A typical HA weather integration returns forecast as a list of dicts with a temperature field; some integrations use templow/temperature per day instead — verify in Developer Tools → States first.