1. Introduction
Our product is a sensing bed with 10,752 pressure sensors, 42 independently actuatable vertical blocks, and 4 recliners (head-end and leg-end, on both sides of the bed, so that two users can be controlled independently). On top of the raw sensor grid we trained a stack of models: posture classification, keypoint detection, Pressure ID (the biometric identifier described in our earlier post), sleep stage classification, and a BCG-derived physiological event detector that runs on a piezoelectric sensor under the mattress. Together these form the semantic state of the user on the bed. The question this post addresses is how we went from controlling that bed with a rule-based engine, to controlling a whole room of devices with a language model, and why the intermediate steps failed.
2. Starting point: the rule-based engine
Given the posture class , keypoint vector over 9 body points (head, shoulders, hips, knees, ankles), and user identity , the initial controller was a deterministic function:
where ‑bed is the action space of the bed, namely the joint configuration of 42 blocks. In practice was a lookup table with roughly 30 handwritten rules of the form “if posture is left-side and user is Kaushik and hip keypoint is above threshold, lower block (3,4) by 2 mm.” This worked. The bed alone is a closed system with a small, well-defined action space, and rules are the correct engineering choice when is small and the mapping from state to action is well-understood.
3. First extension: adding smart home devices
The natural extension was to control the room, not just the mattress. We added lights and an Apple TV. The controller became:
where is the sleep stage (a subset of ) and is the time of day. More rules were added. A representative one: if the user transitions from to while the TV is running, the lights are on, and the head-end of the bed is reclined up, then pause the TV, dim the lights, and drive the bed to its flat sleeping position. The rule count grew from 30 to roughly 80 in two weeks of development.
This still worked, but a pattern was becoming visible. The joint action space was growing multiplicatively while our engineering effort was growing additively.
4. Where the rule-based approach collapses
Consider a more realistic deployment: the bedroom contains the bed, a ceiling light, a bedside lamp, a smart TV, a thermostat, an air purifier, a humidifier, and smart blinds. A rough count of the action space of each device:
- Bed: 42 blocks with 250 height levels each (1 mm precision), plus 4 recliners with continuous angle, giving a naive configuration space of order 250⁴². In practice the reachable set is much smaller, but still far too large for anyone to write if-cases over manually.
- Ceiling light: on/off, 100 brightness levels, color temperature. ≈ 2 × 10² × 10² = 2 × 10⁴.
- Bedside lamp: similar structure, ≈ 10⁴.
- Smart TV: on/off, volume (100), input source (10), app (20+), playback state (5). Actions are also nested: within an app there are further actions.
- Thermostat: continuous setpoint, 4 modes, schedule override. Effectively continuous.
- Air purifier: on/off, 5 fan speeds, auto mode, timer.
- Humidifier: on/off, target humidity (continuous), mist level (3).
- Smart blinds: open percentage (continuous).
Let denote the action space of device . The joint action space is the Cartesian product:
and even after pruning to a realistic subset, is of order 10¹⁰ or more. Writing if-cases over this space is not a matter of more engineering effort; it is structurally infeasible, because most of the rules would have to encode interactions between devices that no single engineer has ground truth for. Should the humidifier run harder if the thermostat is set low and the user is supine? Should the blinds close when the TV starts playing in the evening, or only in certain rooms? These are not technical questions, they are preference and context questions, and there is no stable answer to hard-code.
The problem is not just combinatorial. It is semantic. Even enumerating the action space of each device requires us to read its documentation and encode it. When a user adds a new device we have not seen, the controller cannot act on it.
5. Why language models are the right tool here
Large language models have seen, in their training data, the documentation of essentially every consumer smart home device. They know that a Philips Hue bulb has color temperature, that a Dyson fan oscillates, that an Apple TV has a sleep timer. This is not a claim about their reasoning ability; it is a claim about their priors over device action spaces. They are, by construction, a compressed representation of the action space of the smart home.
Framed as a policy, the LLM controller is:
where the state variables are:
- : posture class.
- : keypoints over 9 body points (head, shoulders, hips, knees, ankles). The set is deliberately small today and will grow (wrists, elbows, neck, spine midpoints) as the downstream detector matures.
- : user identity from Pressure ID.
- : output of the sleep stage classifier, taken as a subset rather than a single element because the labels are not mutually exclusive. A user can be simultaneously sleeping and snoring, or sleeping and moving. The classifier is only invoked when a user is detected on the bed; if the bed is empty, is not fetched and the LLM policy is not called in the first place.
- : recent physiological event vector from BCG, for example a rolling summary of the form “HR spike of +18 bpm in the last 90s” or “respiratory rate dip from 16 to 11 in the last 3 minutes.” This is not raw BCG, it is an event-level summary in natural language (or a structured record that serializes cleanly to natural language).
The input is serialized into natural language and the output is a structured action set (typically JSON) that our orchestrator dispatches to each device.
Why the LLM does not see raw pressure
This is the single most important design decision in the system and the one most often questioned, usually by ML engineers who want to collapse everything into one big model. The answer is that the LLM does not see, and should not see, the raw pressure grid or the raw BCG waveform. Raw pressure and raw BCG are outside the LLM's training distribution and carry no semantic meaning to it. A pressure value of 312 at sensor (47, 83) is not a token the model has useful priors over.
Instead we feed learned semantic compressions of each raw signal: posture class and keypoints (from pressure), sleep stage (from pressure and BCG jointly), and event-level physiological summaries like “HR spike of +18 bpm in the last 90s” (from BCG). Every one of these is a token sequence the LLM's priors cover. This is the same pattern as feeding a vision-language model image embeddings rather than raw pixels, or feeding an audio model spectrograms rather than waveforms. The LLM is a reasoner over semantic state; we are responsible for producing that semantic state.
The separation of labor is:
- The upstream models compress high-dimensional, noisy, physically meaningful sensor data into a small semantic vocabulary.
- The LLM reasons over that semantic vocabulary, combines it with user context, and emits structured actions.
Neither layer is replaceable by the other. The LLM cannot read a pressure grid; the posture classifier cannot decide that the blinds should close.
6. What this gives us
Joint control rather than independent control. The rule-based engine controlled each device with its own if-cases. The LLM reasons over the full device set at once. If the user is supine, past 22:00, has indicated a preference for cool sleeping, and the thermostat is at 24°C, the LLM can simultaneously lower the thermostat, dim the ceiling light, close the blinds, and raise the humidifier setpoint, as one coherent decision. No rule writer has to anticipate this combination.
Initial policy for free, refinement later. With the rule-based engine we were hand-authoring the mapping one entry at a time. With learned controllers (behavior cloning, classical RL) we would be training that same mapping from data. With the LLM, the initial policy is elicited from pretraining priors rather than trained from scratch, because the LLM has seen device documentation, smart home tutorials, and controller code. That gives us a functional on day one, without a labeled dataset. It does not close the door on learning: logged (state, action, outcome) triples from deployed units can later be used to refine the policy through supervised finetuning or reinforcement learning against a grounded reward signal. The V1 story is that we get a reasonable for free. The V2 story is that we improve against measured outcomes. Both are true; the first is the prerequisite for the second.
Contextual state the rules could not express. User preferences (“I like a warm room”), medical history (“lower back surgery in 2023”), and longitudinal context (“user has slept poorly the last three nights”) enter the prompt as natural language. Encoding the same information as rule conditions would require a dedicated schema per attribute and a dedicated rule per combination.
7. What this costs us
The honest list of disadvantages we have observed so far:
Compute and latency. A forward pass through a multi-billion parameter model per decision is expensive in both VRAM and wall clock time compared to an if-case. Inference latency is on the order of seconds, which is acceptable for sleep-timescale decisions but not for fast reactive control.
Non-determinism. The same state can produce different actions across runs. For a bed controller this is tolerable, for a safety-relevant actuation it is not. Safety is enforced after the LLM, not inside it. The LLM emits a JSON action plan, which is parsed and passed through a constraint layer before it reaches the motors. That layer enforces block-level limits (maximum absolute block height, maximum change in height per actuation cycle, maximum simultaneous velocity across blocks), recliner angle limits, and pinch-zone checks where adjacent blocks cannot diverge beyond a safe gradient. Any action violating these constraints is clipped or rejected before dispatch, regardless of what the LLM proposed.
Prompt engineering as a hidden complexity tax. The “rules” did not disappear; they migrated into the system prompt and the serialization format. This is a real cost that is easy to underreport.
8. Further work
Three directions are active:
Orchestration. How the LLM's structured output is dispatched, retried, and reconciled with device state is nontrivial. A device can be offline, an action can fail mid-execution, and the LLM's view of the world can drift from reality. Robust orchestration patterns are an open engineering problem.
Online finetuning at low parameter count. We want the smallest model that retains the priors we need. The goal is not the highest-capacity model; it is the lowest-capacity model that still knows what a humidifier is. We are exploring distillation and LoRA-based online finetuning to push throughput up without losing device coverage.
Pressure as a native modality. The current design compresses pressure into posture and keypoints before the LLM sees it. This is a lossy compression. The long-term goal is a foundation model that reads pressure grids the way CLIP reads images and Whisper reads audio, as a first-class modality with its own tokenizer and its own pretraining objective. Reaching that goal means collecting pressure data at a scale comparable to what image and audio foundation models were trained on, which is a multi-year project in its own right.
9. Closing
The arc of this system is not unusual. A small, well-defined control problem is best solved by rules. A medium control problem with learned structure is best solved by a small supervised model. A large, open-ended control problem over a combinatorial space of devices we cannot fully enumerate is, for now, best solved by a language model acting as a reasoner over a semantic state we ourselves produced from pressure and BCG. The LLM did not replace our models; it sits on top of them. Posture, keypoints, Pressure ID, sleep stage, and BCG event summaries are what make the LLM's job tractable, and the LLM is what makes the joint control problem tractable. The layers compose, and each one earned its place.
engineering\ computational
- 1 — Introduction
- 2 — Rule-based engine
- 3 — First extension
- 4 — Where rules collapse
- 5 — Why LLMs
- 6 — What this gives us
- 7 — What this costs
- 8 — Further work
- 9 — Closing
Apr 2026Kaushik Kalva