Skip to content

Appliances & Unified Schedules

The appliance endpoints let you register devices (HVAC, EV charger, home battery, water heater, solar), push per-device sensor data, set optimization constraints, and pull schedules.


Register a new appliance.

Auth: Required

Terminal window
curl -X POST https://api.hungrymachines.io/api/v1/appliances \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"appliance_type": "ev_charger",
"name": "Tesla Model 3",
"config": {
"battery_capacity_kwh": 75,
"max_charge_rate_kw": 7.2,
"efficiency": 0.9,
"entity_id": "switch.tesla_charger",
"soc_entity_id": "sensor.tesla_battery_level"
}
}'

Response (201):

{
"appliance_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}

Server-side validation is strict — out-of-range or missing required fields return 400 with the offending field name in the detail message.

{
"hvac_type": "central_ac",
"home_size_sqft": 2000,
"entity_id": "climate.living_room"
}
FieldTypeConstraints
hvac_typestring"central_ac", "window_ac", "heat_pump", or "furnace"
home_size_sqftinteger100 – 20 000
entity_idstringRequired. The climate.* entity that exposes current_temperature and accepts set_temperature calls. Used both for sensor input to the thermal model and for applying optimized setpoints.
{
"battery_capacity_kwh": 75,
"max_charge_rate_kw": 7.2,
"efficiency": 0.9,
"entity_id": "switch.tesla_charger",
"soc_entity_id": "sensor.tesla_battery_level"
}
FieldTypeConstraints
battery_capacity_kwhfloat0 – 300
max_charge_rate_kwfloat0 – 50
efficiencyfloat0.5 – 1.0 (charging efficiency factor)
entity_idstringRequired. The switch.* (or equivalent) entity the integration toggles to start/stop charging.
soc_entity_idstring | nullOptional. A sensor.* exposing state-of-charge as 0–100. When set, the readings poller pushes SoC every 5 min so the optimizer’s required-energy calculation has live data.
{
"capacity_kwh": 13.5,
"max_charge_rate_kw": 5.0,
"max_discharge_rate_kw": 5.0,
"entity_id": "switch.powerwall_charge_mode",
"soc_entity_id": "sensor.powerwall_charge"
}
FieldTypeConstraints
capacity_kwhfloat0 – 300
max_charge_rate_kwfloat0 – 50
max_discharge_rate_kwfloat0 – 50
entity_idstringRequired. The switch / button / select entity that toggles charge mode.
soc_entity_idstring | nullOptional. sensor.* for battery SoC %. Same shape as EV.
{
"tank_size_gallons": 50,
"element_watts": 4500,
"insulation_factor": 0.03,
"entity_id": "switch.water_heater",
"temp_entity_id": "sensor.water_heater_temperature"
}
FieldTypeConstraints
tank_size_gallonsinteger0 – 200
element_wattsinteger0 – 10 000
insulation_factorfloat0.01 – 0.05 (heat-loss rate)
entity_idstringRequired. The switch.* (resistive elements) or climate.* (smart tanks) entity the integration toggles each 30-min boundary.
temp_entity_idstring | nullOptional. sensor.* exposing tank temperature in °F. When set, the readings poller pushes tank temp every 5 min.

Solar is forecast-only — no entity_id, no readings to push. The nightly job uses the system size + orientation to build a 48-element generation curve that discounts effective grid pricing for every other appliance during daylight intervals.

{
"system_size_kw": 6.4,
"azimuth_degrees": 180,
"tilt_degrees": 20
}
FieldTypeConstraintsNotes
system_size_kwfloat0 – 100DC nameplate capacity.
azimuth_degreesinteger0 – 360Panel azimuth — 180 (south) is the default for the northern hemisphere.
tilt_degreesinteger0 – 90Panel tilt from horizontal.
StatusDetailCause
400"appliance_type must be one of ('hvac', 'ev_charger', 'home_battery', 'water_heater', 'solar')"Unknown type
400"<field>: <message>"Per-type config validation failure (e.g. "entity_id: String should have at least 3 characters", "home_size_sqft: Input should be less than or equal to 20000")
403"Upgrade required"Free-tier limit (1 HVAC only)

List all registered appliances.

Auth: Required

Terminal window
curl https://api.hungrymachines.io/api/v1/appliances \
-H "Authorization: Bearer YOUR_TOKEN"

Response (200):

[
{
"id": "a1b2c3d4-...",
"user_id": "550e8400-...",
"appliance_type": "ev_charger",
"name": "Tesla Model 3",
"config": {
"battery_capacity_kwh": 75,
"max_charge_rate_kw": 7.2,
"efficiency": 0.9,
"entity_id": "switch.tesla_charger",
"soc_entity_id": "sensor.tesla_battery_level"
},
"is_active": true,
"created_at": "2025-11-15T10:30:00+00:00"
}
]

PUT /api/v1/appliances/{appliance_id}

Section titled “PUT /api/v1/appliances/”

Update an appliance’s name or config.

Auth: Required

Terminal window
curl -X PUT https://api.hungrymachines.io/api/v1/appliances/a1b2c3d4-... \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Tesla Model 3 (Garage)",
"config": {
"battery_capacity_kwh": 75,
"max_charge_rate_kw": 11.5,
"efficiency": 0.9,
"entity_id": "switch.tesla_charger"
}
}'

When updating config, send the full per-type object — partial config patches are not supported because the server re-validates the whole shape.

Response (200): Full appliance object (same shape as GET list item).


POST /api/v1/appliances/{appliance_id}/readings

Section titled “POST /api/v1/appliances//readings”

Push sensor data for a specific appliance.

Auth: Required

Terminal window
curl -X POST https://api.hungrymachines.io/api/v1/appliances/a1b2c3d4-.../readings \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"readings": [
{
"timestamp": "2025-11-18T14:30:00+00:00",
"state": "CHARGING",
"value": 45.2,
"power_watts": 7200.0,
"metadata": { "voltage": 240, "current": 30 }
}
]
}'
FieldTypeRequiredDescription
timestampstring (ISO 8601)Yes
statestringYesFree-form label, typically "CHARGING", "IDLE", "ON", "OFF", "DISCHARGING". Not server-validated.
valuefloatYesCharge % (0–100) for EV/battery; tank temperature (60–180 °F) for water heater. Server-validated per appliance type.
power_wattsfloatNoCurrent power draw
metadataobjectNoAny additional data, stored verbatim

Response (201):

{
"accepted": 1
}

Rate limit: 300 readings per user per day (shared across /api/v1/readings and every appliance’s /readings).

StatusDetailCause
400"Value must be 0-100 (percent) for ev_charger"EV/battery value out of range
400"Value must be 60-180 (degrees F) for water_heater"Water heater value out of range
404"Appliance not found"Bad appliance_id, or not owned by the caller
429"Rate limit exceeded: max 300 readings per user per day"Combined daily count exceeded

POST /api/v1/appliances/{appliance_id}/constraints

Section titled “POST /api/v1/appliances//constraints”

Set optimization constraints for a specific appliance. The optimizer uses these on its next run. Constraints are stored verbatim in the appliance’s constraints JSONB column — only the optimizer’s per-type code interprets them, so there’s no strict server-side schema.

Auth: Required

Terminal window
curl -X POST https://api.hungrymachines.io/api/v1/appliances/a1b2c3d4-.../constraints \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"target_charge_pct": 80,
"min_charge_pct": 30,
"deadline_time": "07:00",
"current_charge_pct": 35
}'
{
"target_charge_pct": 90,
"min_charge_pct": 20,
"deadline_time": "23:59"
}
{
"max_temp_f": 140,
"min_temp_f": 110
}

Response (200):

{
"status": "ok",
"constraints": { "..." }
}

GET /api/v1/appliances/{appliance_id}/schedule

Section titled “GET /api/v1/appliances//schedule”

Get the optimized schedule for a single appliance.

Auth: Required

Terminal window
curl https://api.hungrymachines.io/api/v1/appliances/a1b2c3d4-.../schedule \
-H "Authorization: Bearer YOUR_TOKEN"

All schedules use 48 × 30-minute intervals (24 hours).

{
"appliance_id": "...",
"appliance_type": "hvac",
"name": "Living Room AC",
"date": "2025-11-18",
"schedule": {
"intervals": [0, 1, 2, "...", 47],
"high_temps": [74.0, 74.0, "...(48 values)"],
"low_temps": [70.0, 70.0, "...(48 values)"],
"setpoint_temps": [72.0, 71.5, "...(48 values)"],
"temp_trajectory": [72.1, 71.6, "...(48 values)"]
},
"savings_pct": 18.5,
"source": "optimization",
"entities": { "entity_id": "climate.living_room" }
}

Apply setpoint_temps[idx] to the climate entity at each 30-min boundary, regardless of HVAC mode. high_temps and low_temps are the display-only comfort band used for charting; the thermostat does not see them. Both setpoint_temps and temp_trajectory are null on defaults rows — fall back to (high_temps[idx] + low_temps[idx]) / 2.

{
"appliance_id": "...",
"appliance_type": "ev_charger",
"name": "Tesla Model 3",
"date": "2025-11-18",
"schedule": {
"intervals": [false, false, "...", true, true, "...", false],
"value_trajectory": [35.0, 35.0, "...", 57.5, 80.0, "...", 80.0],
"unit": "percent"
},
"savings_pct": 32.1,
"source": "optimization",
"entities": { "entity_id": "switch.tesla_charger", "soc_entity_id": "sensor.tesla_battery_level" }
}

intervals: boolean array — true = charger on, false = charger off. value_trajectory: predicted charge level (0–100 %) at each interval.

Same shape as EV charger: intervals (boolean), value_trajectory (percent), unit: "percent", plus an entities block.

{
"appliance_id": "...",
"appliance_type": "water_heater",
"name": "Basement Water Heater",
"date": "2025-11-18",
"schedule": {
"intervals": [true, true, false, "...", false, true, "..."],
"temp_trajectory": [120.0, 128.5, 135.2, "...", 118.3, 126.7, "..."],
"unit": "fahrenheit"
},
"savings_pct": 22.0,
"source": "optimization",
"entities": { "entity_id": "switch.water_heater", "temp_entity_id": "sensor.water_heater_temperature" }
}

intervals: boolean array — true = element on, false = element off. temp_trajectory: predicted tank temperature at each interval.

Every per-appliance schedule entry carries an entities sub-object echoing the HA entity ids the integration needs to apply the schedule. Only fields the appliance config actually set are included — entity_id for everything except solar, plus soc_entity_id (EV / battery) or temp_entity_id (water heater) when set. Solar appliances have no entities block.


Unified view of all appliance schedules in a single response.

Auth: Required

Terminal window
curl https://api.hungrymachines.io/api/v1/schedules \
-H "Authorization: Bearer YOUR_TOKEN"

Response (200):

{
"date": "2025-11-18",
"appliances": [
{
"appliance_id": "...",
"appliance_type": "hvac",
"name": "Living Room AC",
"schedule": {
"intervals": [0, 1, "...", 47],
"high_temps": [74.0, "..."],
"low_temps": [70.0, "..."],
"setpoint_temps": [72.0, "..."],
"temp_trajectory": [72.1, "..."]
},
"savings_pct": 18.5,
"source": "optimization",
"entities": { "entity_id": "climate.living_room" }
},
{
"appliance_id": "...",
"appliance_type": "ev_charger",
"name": "Tesla Model 3",
"schedule": {
"intervals": [false, "...", true, "...", false],
"value_trajectory": [30.0, "...", 80.0],
"unit": "percent"
},
"savings_pct": 32.1,
"source": "optimization",
"entities": { "entity_id": "switch.tesla_charger" }
}
]
}

Each entry in the appliances array uses the per-type schedule shape described above. Use this endpoint when you need the full picture for a multi-device home.

To force a synchronous recompute (e.g. immediately after the user saves a new constraint) and return the same shape, use POST /api/v1/schedule/recompute.

TypeSchedule fieldsUnit
hvacintervals (int[48]), high_temps (float[48]), low_temps (float[48]), setpoint_temps (float[48] | null), temp_trajectory (float[48] | null)Fahrenheit
ev_chargerintervals (bool[48]), value_trajectory (float[48]), unitpercent (0–100)
home_batteryintervals (bool[48]), value_trajectory (float[48]), unitpercent (0–100)
water_heaterintervals (bool[48]), temp_trajectory (float[48]), unitfahrenheit
solarNo schedule — solar contributes to other appliances’ pricing curve but emits no per-interval commands.