Skip to content

Repository files navigation

LANtern

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.

Home screen widget

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.

Home screen widget filled to 41 percent, with a bulb icon on the left and minus and plus on the right

Repeated taps are throttled and debounced, so a burst moves one step and sends one request rather than queueing a round trip per tap.

Quick Settings

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.

App

Brightness, colour temperature, and a hue by saturation field, plus colour presets. The UI is in Korean.

App screen showing power, colour presets, a brightness slider, and a white and colour picker

Before it will work

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-kasa fails 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 offline even 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.

Build

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.

Diagnostics

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

About

Control a Tapo L535 bulb locally from an Android widget and Quick Settings. KLAP v2 ported to Kotlin, no cloud round trip.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages