A native iOS replacement for the SmartLife app, scoped to a single device — the MPets SMARTO M500 automatic litter box (Tuya model ALB201). Built because SmartLife is buggy and slow, and the device's protocol turned out to be reverse-engineerable.
Status: early development. The Tuya v3.4 local protocol is fully ported to Swift and proven against the live device. The iOS UI is next.
$ swift run pawkit-cli
connecting to 192.168.1.99:6668…
✓ connected, session key established
→ reading status…
DP map (18 keys):
DP 5 = int(5) # delay_clean_time
DP 6 = int(3871) # cat_weight (g)
DP 7 = int(1) # excretion_times_day
DP 16 = bool(true) # light
DP 22 = int(2) # fault bitmap
DP 24 = string("standby")
...
Decoded state:
status: standby
cat weight: 3871 g (3.871 kg)
delay clean: 5 min
light: true child_lock: false button_sound: true kitten_mode: false
faults: cover_box_not_in_place
End-to-end read and write (pawkit-cli --toggle-light flips DP 16 off→on through the local protocol).
Hybrid local + cloud, via a single LitterBoxClient protocol with two implementations:
PawkitCore (Swift Package)
├── TuyaProtocol.swift wire constants, frame type, command enum
├── TuyaCipher.swift AES-128-ECB + HMAC-SHA256 + session-key derivation
├── TuyaMessageCodec.swift pack/unpack v3.4 frames (the bit/byte layer)
├── TuyaError.swift
├── Models.swift TuyaDPValue, LitterBoxState, fault bitmap, sleep config
└── LocalClient.swift NWConnection async client w/ status / setDP / events
The local protocol handles 95% of operations. A cloud client (planned) will cover the few settings that don't expose locally — delay_clean_time writes propagate cloud→device with a 1–3 min lag, factory_reset is cloud-only, and schedules are cloud-stored.
The full reverse-engineered datapoint reference — including the fault bitmap, the 5-byte schedule packing format, and the cat-session push-event encoding — is in dp_map.md. 18 of ~19 visible DPs are confirmed, plus 2 push-event DPs.
Requires macOS 13+ / iOS 16+ and Swift 5.9.
cd PawkitCore
swift test # cipher + codec unit tests
swift build # builds the library + CLI
.build/debug/pawkit-cli # connect, read, decode
.build/debug/pawkit-cli --toggle-light # full read+write smoke testPawkit doesn't implement Tuya BLE pairing — that's a much larger protocol, and a one-time setup. The flow is:
- Pair the device once via SmartLife (standard Tuya account binding).
- Create a Smart Home project at iot.tuya.com, authorize IoT Core, Authorization Token Management, and Smart Home Basic Service.
- Link your SmartLife account to the project (Devices → Link Tuya App Account).
- Run
bootstrap.pyonce to fetch yourdevIdandlocalKeyinto.env. - From there, pawkit talks directly to the device on TCP port 6668 — no SmartLife needed for daily use.
If you ever factory-reset the device, you'll need SmartLife again to re-pair (the localKey rotates). For day-to-day operation it stays out of the way.
tinytuya— the open-source Python library whose protocol implementation made this port possible. Pawkit's Swift v3.4 layer is faithful to tinytuya's wire format.- The countless Home Assistant /
localtuyacontributors who've documented Tuya quirks over the years.
Personal project, no formal license yet. If you want to use this as a reference for your own Tuya device, fork freely.