Skip to main content

Critical Quirks

These are the most common implementation mistakes. Each one can cause silent, hard-to-debug failures.
#QuirkConsequence if wrong
1Always include explicit port in server URLs (e.g., :8000)WoWLAN fails — server push cannot wake sleeping device
2object_revision and object_timestamp must appear before object_key in JSONDevice silently ignores the update or fails to parse it
3expires in entry key response must be a JSON number, not a stringDevice rejects response — never displays entry code
4Never include value field in PUT responsesDevice overwrites its own local state with server data
5Never provision credentials via 401 responsesDevice enters credential loop — cycles between defaults and assigned credentials indefinitely
6Use manual_eco_all, not away, for direct eco controlaway triggers delayed, schedule-preconditioning-interruptible eco instead of immediate
7manual_eco_timestamp is Unix seconds, not millisecondsDevice rejects eco change (600s window validation fails)
8Always push complete schedules — no partial setpoint updatesDevice replaces entire schedule; partial push wipes unincluded days
9Monday = 0, not Sunday in schedule day keysSchedule shifted by one day
10All temperatures are Celsius — never Fahrenheit in dataDevice displays wrong values; schedule setpoints off by ~32°F
11Connection hold time must be shorter than X-nl-suspend-time-maxDevice’s fallback timer fires first — unpredictable behavior
12Don’t set target_change_pending: true again after device cleared itCreates update loop — display keeps cycling
13Don’t push stale target_temperature values on every subscribeOverrides schedule-derived setpoints with old cloud values
14Don’t push device-only fields (e.g., current_temperature)Device overwrites your value in the next PUT
15Check can_heat/can_cool before pushing HVAC modeDevice silently falls back to supported mode — confusing UI state
16Include user and structure buckets on every subscribe for paired devicesDevice loses pairing state after reboot
17Wait ≥15 seconds between schedule pushesSecond push lands in debounce window and is silently discarded

Timing Reference

TimerDurationSourceNotes
X-nl-suspend-time-max300s (recommended)Server-set headerDevice safety-net wake timer; must be ≤350s
Connection hold time290s (= suspend - 10)Server behaviorMust be shorter than X-nl-suspend-time-max
Device closing window5sDevice-internalAfter receiving body data; resets on each chunk
Batch window≤3sServer behaviorTime between chunks on same connection
Eco timestamp window±600sDevice-internalmanual_eco_timestamp must match device clock ±10 min
Schedule debounce15sDevice-internalSliding window; last push wins
Immediate timeout7sDevice-internalApplies if response lacks Transfer-Encoding: chunked
Idle connection timeout~360sDevice-internalConnection considered dead; device resubscribes silently
X-nl-defer-device-window15–30s (recommended)Server-set headerDelay for device PUT batching
X-nl-disable-defer-window60s (after push)Server-set headerSuppress defer delay temporarily

Battery Voltage Thresholds

VoltageEffect
3.8V+Normal operation
3.7V+25 seconds added to sleep duration
3.65VLow battery flag set
3.6VWiFi disabled — device goes offline
3.5V+225 seconds added to sleep duration
When battery drops below 3.6V, the device goes completely offline and reconnects only after voltage recovers above 3.8V. Recommendation: Queue updates for offline devices and deliver them on the next subscribe after reconnect.

URL Port Requirement

Always include an explicit port in server URLs. Even for standard ports.
WRONG:  http://your-server/nest/transport
CORRECT: http://your-server:8000/nest/transport
Without an explicit port, the device’s URL parser fails to extract the port for TCP keepalive offload (WoWLAN). Result: the server can push data but the device does not wake — it stays asleep until its safety-net timer fires. The NLE server automatically appends the explicit port via api_origin_with_port when generating URLs in the entry response.

Entry Key expires Field

The expires field in the passphrase response must be a JSON number, not a string:
// CORRECT
{"value": "A3XR7M2", "expires": 1707234600000}

// WRONG — device silently rejects
{"value": "A3XR7M2", "expires": "1707234600000"}
The expiration must also be at least 30 minutes in the future from the device’s perspective.

Session ID Behavior

The device’s session field in subscribe requests is not a unique connection identifier. The device reuses the same session value across all subscribe requests during its operational lifetime. Do not use session to track or de-duplicate subscriptions. Generate your own server-side subscription ID for each connection.

Overlapping Subscriptions

When a device wakes early, it may send a new subscribe request before the previous connection closes. Your server may briefly hold two active subscriptions for the same device. This is normal. Push data to all active subscriptions for a device. Remove a subscription only when its specific connection closes. Never close a subscription because a newer one arrived.