Skip to content
FarmHome+

FarmHome+ Device API (v1)

The device API is how a FarmHome+ hub or storage module (Fridge, Freezer, Pantry Dry, Pantry Root, MedCold, MedCabinet) sends data to a household. The simulator in scripts/device-simulator.mjs and real firmware use the same API.

Base URL: https://farm-home.nl/api/v1/device

1. Authentication

Each device has its own API key, shaped like fhd_<deviceId>_<secret>.

2. Pairing

A. Code pairing (recommended for modules with a display or app)

```json

{ "device_type": "fridge", "hardware_serial": "FH-FR-000123", "firmware_version": "1.0.0", "simulated": false }

`

The response is 201:

```json

{ "pairing_code": "ABCD-2345", "pairing_token": "<secret>", "expires_at": "…", "poll_interval_sec": 5 }

`

B. Provisioned key (technicians, DIY modules, simulator)

The owner opens Devices → Pair a module → Provision a key and gets a key shown once. Tick This is a simulator for test data: everything that device sends is then labelled Simulated in the app.

3. Envelope

Every ingest call (heartbeat, telemetry, events, ack, and the metadata of captures) uses the same JSON envelope:

fieldrequirednotes
event_idyesUnique per event, 8–80 characters [A-Za-z0-9._:-]. A repeat returns {"status":"duplicate"} and is not stored twice.
observed_atrecommendedISO 8601 UTC from the device clock. If it is missing, the server receive time is used (timestamp_quality: server_received). If it is more than 10 minutes old, it is marked late. It is rejected if it is in the future or more than 30 days old.
sequence_numberoptionalA monotonic integer.
schema_versionoptionalDefaults to "1".
firmware_versionoptional
device_idoptionalIf present, it must match the key.
simulatedoptionalSet true for any synthetic data.
payloadyesAn object whose shape depends on the endpoint.

Errors use the shape { "error": "<human message>", "code": "<CODE>" }. Codes:

4. Endpoints

POST /heartbeat

Payload: { "battery_pct": 87, "signal_quality": 72, "fault_codes": [] }. A device is shown as Online only if it sent a heartbeat or data in the last 5 minutes. After that it shows Delayed, and after 30 minutes it is Offline and an alert is raised.

POST /telemetry

{ "event_id": "t-…", "observed_at": "2026-11-11T10:00:00Z", "payload": { "readings": [
  { "sensor_type": "temperature_c", "value": 4.2 },
  { "sensor_type": "humidity_pct", "value": 61 },
  { "sensor_type": "door_open", "value": false }
] } }

Allowed sensors are temperature_c (−50…80), humidity_pct (0…100), door_open (boolean), light_lux and battery_pct. You can send 1–50 readings per call. Values outside the physical range are stored as fault and ignored by the rules.

The temperature rules depend on the location the device is assigned to. These are configured ranges for reminders, not food-safety judgements:

locationrange
fridge0–7 °C
freezer≤ −15 °C
MedCold2–8 °C

POST /events

The body is the envelope plus event_type, which is one of:

The payload must be under 4 KB. item_added and item_removed are logged as observations only. They never change the inventory by themselves.

POST /captures (multipart/form-data)

The response is 202 { "status": "accepted", "capture_id": "…", "analysis": "queued" }.

GET /config

Returns the device's name, its assigned location, camera_enabled, the capture policy (no continuous video), and the heartbeat and telemetry intervals (60 s and 300 s). It also returns server_time for clock sync.

POST /ack

The envelope with payload.ref. It acknowledges a local alert, for example when the buzzer was silenced.

5. curl examples

KEY="fhd_…"   # shown once in the app
BASE=https://farm-home.nl/api/v1/device

curl -s -X POST $BASE/heartbeat -H "Authorization: Bearer $KEY" -H 'content-type: application/json' \
  -d '{"event_id":"hb-'$(date +%s)'","simulated":true,"payload":{"battery_pct":90}}'

curl -s -X POST $BASE/captures -H "Authorization: Bearer $KEY" \
  -F image=@fridge.jpg -F 'metadata={"event_id":"cap-'$(date +%s)'","simulated":true,"payload":{"trigger":"user"}}'

6. Simulator

scripts/device-simulator.mjs is a simulator. It always sends simulated: true, and its data is labelled Simulated in the app.

FARMHOME_DEVICE_KEY=fhd_… node scripts/device-simulator.mjs all --image ./fridge.jpg
node scripts/device-simulator.mjs pair --type fridge   # code-pairing flow, prints the code to enter in the app

7. Security notes