-
-
Status Active
-
Say “Alexa, I’m leaving.” Capture the presence line on Away and the lights off. Caption: Leaving saves the room and keeps the lights off.
-
Say “Alexa, match the lights to the weather.” Capture the reply plus the cards sitting on the sky color.
-
Caption: "YOUR_LOCATION " is overcast, from this browser's location. living-room is a cool gray.
-
Loop on kitchen, which keeps the record of past.
Inspiration
Smart-home commands are easy to send and easy to lose. A routine tells a bulb to change, then moves on. If the bulb misses the packet, the room is wrong and nothing notices.
We wanted the opposite for Kasa lights on our own Wi-Fi. Say the intention once, the way you would say it to Alexa+: "cycle the kitchen color every five seconds." After that, a loop on the LAN should set the bulb, read it back, and try again until the light matches. Alexa+ is the voice. The loop is the product.
What it does
Light Agent turns one plain-English intention into a verified control loop for TP-Link Kasa bulbs.
You can turn a light on or off, set brightness, or set color. You can also schedule that action on an interval. cycleColor steps the hue by $$45^\circ$$ on every tick. Each tick applies the action, reads the bulb with getState, and retries up to 3 times. The wait before retry $$n$$ is
$$t_n = 200 \times 2^{\,n-1} \text{ ms}$$
so 200 ms and then 400 ms. Three misses mark the task error. Later ticks never call the model.
npm run setupdiscovers bulbs on the LAN, asks for a friendly name, and saves them tolights.json.MockConnectorruns the same loop with no hardware.KasaConnectordrives real KL/LB bulbs throughtplink-smarthome-api.
How we built it
TypeScript on Node, run directly with tsx. Four pieces stay separate so each one can be tested on its own:
LightConnectoris the contract.MockConnectorkeeps state in memory.KasaConnectormaps a friendly id to a bulb host.LightAgentis a Claude tool-use loop. It compiles a sentence intopower,brightness,color, orschedule_recurring_task, then stops.TaskSchedulerowns the interval, the verify step, and the backoff. It emits a status line after every tick.- Tools in
src/agent/tools.tsare the only bridge from the model to the connector and the scheduler.
The mock path was exercised end to end: tool dispatch, hue steps, a successful verify, and the retry-to-error path. npm run typecheck is clean.
Challenges we ran into
- CommonJS vs. ESM.
tplink-smarthome-apiis CommonJS with getter-based named exports.import { Client }typechecks and throws at runtime under Node's ESM loader. A default import, then destructuring, fixed it. - Discovery on segmented networks. Discovery is a UDP broadcast. On a segmented subnet the default broadcast finds nothing, so setup accepts
KASA_BROADCASTand a longer scan. - Type narrowing. The library returns
Bulb | Plug, so a device has to be narrowed beforelighting.setLightState. - No hardware test yet. The Kasa connector is written and not yet run on physical bulbs. The mock connector is what made verify and retry testable without a LAN.
- In-memory loops. Loops live in memory, so the process has to stay open for ticks to fire.
- Alexa+ onboarding. Device onboarding is partner-gated, and a classic skill is the wrong surface. The eligible path is a self-hosted MCP server on spec 2025-11-25 over Streamable HTTP, with a page that is a real MCP client.
Accomplishments that we're proud of
- The model is used once, as a compiler. The runtime after that is deterministic: apply, read back, retry, with no token cost per tick.
- The hardware boundary held. The agent and the scheduler never mention Kasa. A new bulb brand is another
LightConnector. - The failure path is real, not a log we hope is true. A connector that disagrees with the intended state is retried, then marked
errorafter three attempts.
What we learned
A natural-language light switch is a demo. A closed loop is the product. Verification has to be a read of the bulb, and the schedule has to survive as state the next session can list.
We also learned the Alexa+ contract: judges meet the project through MCP (initialize, tools/list, tools/call), voice, and cards. The partner console being closed does not change the loop underneath.
What's next for Light Agent
- Put the loop behind a Streamable HTTP MCP server and an Alexa+ page: microphone, spoken confirmation, and cards for color, loop status, and each verify or retry.
- Persist tasks to
tasks.jsonand restore them on startup. - Run the same sentences against real Kasa bulbs on the LAN.
- Add more connectors behind the same interface.
Built With
- ai
- alexa+
- anthropic
- claude
- css
- html5
- javascript
- model-context-protocol
- node.js
- open-meteo
- openstreetmap
- tp-link-kasa
- typescript
- web-speech-api
- zod

Log in or sign up for Devpost to join the conversation.