An Android app that controls a Tapo L535 bulb from a home screen widget and from Quick Settings, talking to it directly over the LAN with KLAP. No cloud round trip.
Power on the left, −/+ on the right, and no text anywhere: the background fills from
the left to show the current brightness. Tapping the empty middle opens the app.
Repeated taps are throttled and debounced, so a burst moves one step and sends one request rather than queueing a round trip per tap.
The tile uses a ToggleRangeTemplate, so it can be dragged sideways to dim. App widgets
are built from RemoteViews, which has no SeekBar, so this is the only place in
Android where a bulb gets a real slider.
Brightness, colour temperature, and a hue by saturation field, plus colour presets. The UI is in Korean.
Turn on third-party access. Tapo app → Me → Third-Party Services → Third-Party Compatibility. With it off the bulb answers with the TPAP scheme instead of KLAP, and TPAP is TLS-based PAKE with no public implementation, so nothing can reach it locally.
Your TP-Link account must be registered with an email address.
A phone-number-only account cannot authenticate over local KLAP. This is not documented
anywhere I could find, and it costs a lot of time to diagnose, so here is what was ruled
out on a L535E(KR), firmware speaking KLAP lv=2:
- Every spelling of the number was rejected:
01012345678,+821012345678,821012345678,+82-1012345678, with and without separators. - The bulb was factory reset and re-registered to that account. It still rejected the password that had just been set during registration.
python-kasafails identically with the same credentials, so this is not specific to this implementation.- Blank credentials and TP-Link's built-in setup credentials are all rejected.
- The Kasa cloud refuses phone numbers outright with
Username format error. Tapo cloud login succeeds, but its passthrough answers-20571 Device is offlineeven while the Tapo app controls the bulb remotely, so that route is closed too. - Registering the same bulb to an email account worked on the first attempt.
python-kasa and every other implementation derive the auth hash as
sha256(sha1(username) + sha1(password)) assuming the username is an email. Whatever a
phone-number account stores on the device is something else, and it is not discoverable
from outside. If you registered with a phone number, create an email-based TP-Link ID and
move the bulb to it.
One dead end worth naming: the owner value a device advertises in its discovery
response is not md5(username). python-kasa computes a value of that shape but
never compares it, and it does not match in practice. Do not use it to check whether an
account owns a device.
gradlew.bat :app:assembleDebug
gradlew.bat :app:testDebugUnitTest
The KLAP port is checked byte for byte against python-kasa output. Regenerate the
reference values with tools/klap_vectors.py.
Run these from a PC on the same network when the bulb will not answer. Credentials are prompted for, never passed as arguments.
python tools/sweep.py 192.168.0 find addresses answering KLAP on the LAN
python tools/probe_handshake1.py <ip> check the endpoint and port
python tools/try_login.py try each credential spelling, plus blank and defaults
python tools/kasa_connect.py connect with python-kasa itself

