Skip to main content

pypopur

Experimental — a reverse-engineered Python client for the Popur S7 smart litter box, built from static analysis of the official Android app (v2.0.0 / ThingClips SDK). Tested against a single real device; expect rough edges.

PyPI License: MIT Tests

Features

  • LAN control — Tuya protocol 3.5 session-key negotiation, DP read/write over TCP:6668, auto-protocol probing (3.1–3.5), heartbeat keepalive, stale-session recovery
  • Cloud APIs — full ThingClips/Popur app surface: auth, homes, devices, DP shadow, pets, pet records, firmware, timers, device management
  • MQTT events — real-time DP deltas and alert events over TLS to the app's broker, with reconnect/resubscribe handling
  • Hybrid transport — LAN-first with automatic cloud fallback; slow reconciliation tier for settings DPs and pet data the LAN omits
  • Discovery — passive UDP announcements plus active host location via ARP MAC match and port-6668 subnet scan
  • DP codecs — all firmware-4 packed payloads decoded (settings, calibration, radar config, cat weight, visit records)
  • Pet tracking — household pet profiles, per-visit weight/duration records decoded from the app's own format
  • Home Assistant integration — full entity coverage (sensors, switches, buttons, numbers, selects) in custom_components/popur/

Install

pip install pypopur

Requires Python 3.11+.

Quick start — local control

import asyncio
from pypopur import LocalTuyaTransport, PopurClient

async def main():
    transport = LocalTuyaTransport(
        device_id="your-device-id",
        host="192.168.1.x",
        local_key="your-local-key",
    )
    client = PopurClient(transport)

    async with client:
        snapshot = await client.refresh()
        print(snapshot.machine_status, snapshot.cat_present, snapshot.bin_full)
        await client.start_cleaning()

asyncio.run(main())

Quick start — cloud account

import asyncio
from pypopur.mobile import MobileAppProfile, PopurAccount, ThingMobileApi

async def main():
    api = ThingMobileApi(
        MobileAppProfile.bundled_popur_app2(),
        install_id="a-stable-id-for-this-install",
    )
    account = PopurAccount(api)
    await account.login("you@example.com", "your-password")

    home_id = (await account.homes())[0]["gid"]
    devices = await account.home_devices(home_id)

    for dev in devices:
        print(dev.name, dev.device_id, dev.local_key)

asyncio.run(main())

Quick start — pets

pets = await account.pets(home_id)
for pet in pets:
    print(pet.name, pet.pet_type, pet.weight)

page = await account.pet_records(home_id, page_size=50)
for record in page.records:
    usage = record.toilet_usage()
    if usage:
        print(record.pet_id, usage.weight_grams, "g", usage.duration_seconds, "s")

Quick start — MQTT events

events = account.connect_events(
    devices={dev.device_id: dev.local_key},
    on_event=lambda e: print(e.dev_id, e.dps),
    on_connect=lambda: print("connected"),
)
await events.connect()

Architecture

┌─────────────────────────────────────────────────┐
│                  PopurClient                     │
│         typed controls + DeviceSnapshot          │
├─────────────────────────────────────────────────┤
│              FallbackTransport                   │
│   LAN primary (3.5) ──fail──► Cloud fallback    │
├──────────┬──────────────────┬───────────────────┤
│ LAN TCP  │  MQTT TLS        │  Cloud HTTPS      │
│ :6668    │  smart/mb/in/    │  a1.tuyaus.com    │
│ DP r/w   │  DP deltas       │  shadow, pets,    │
│ ~23 DPs  │  alerts          │  mgmt APIs        │
└──────────┴──────────────────┴───────────────────┘
  • LAN — fast path for reads/writes; ~23 DPs in status responses
  • MQTT — real-time push (protocol-4 deltas, protocol-56 alerts); deltas only, no snapshots
  • Cloud — settings DPs LAN omits (102 etc.), pet profiles/records, firmware, timers, device management

Home Assistant

The integration lives in custom_components/popur/ — install via HACS custom repository or copy the directory to your HA custom_components/. See custom_components/popur/README.md for details.

Status

  • Tested against: one Popur S7 (Wi-Fi 3.0.30 / MCU 4.3.0), firmware-4 protocol family
  • LAN protocol: verified live — handshake, DP read/write, heartbeat, reconnect
  • Cloud APIs: signature-exact to the app; read paths verified live, write paths untested
  • MQTT: verified live — connect, subscribe, decode protocol-4/56 frames, reconnect
  • Test suite: 769 tests, all passing

Limitations

  • Only tested against one device and one firmware version — other S7s may differ
  • App-layer write APIs (pets, device management) are signature-verified but never executed live
  • The standalone LAN transport doesn't fetch the thing model — no schema validation on writes
  • MQTT is event-driven (deltas only) — it can't replace polling for state seeding or reconciliation
  • Settings DPs and pet data require the cloud — they don't exist on the LAN channel

Development

python -m pytest tests/          # 769 tests
ruff check src tests             # lint

License

MIT — see LICENSE.

Disclaimer

This is an unofficial, reverse-engineered client. It is not affiliated with or endorsed by Popur. Use at your own risk — the author is not responsible for bricked devices, angry cats, or unexpected litter box behavior.

Metadata

Release files for pypopur 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pypopur 0.1.1
File Size Uploaded
pypopur-0.1.1.tar.gz 311.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pypopur 0.1.1
File Interpreter ABI Platform
pypopur-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 550.2 kB

Release files / pypopur-0.1.1.tar.gz

Download URL pypopur-0.1.1.tar.gz
Size 311.8 kB
Tags Source
SHA-256 checksum
How to use checksums
e1315c3088a833a98b9e757e200bd0736214adcfc63eebf4cf2f89a8b7c67898
BLAKE2b-256 checksum
How to use checksums
28e70001c61493056d387a404f7f2c902115181067caf21bb4fa75c9c012033f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 19, 2026.

Transparency log

Release files / pypopur-0.1.1-py3-none-any.whl

Download URL pypopur-0.1.1-py3-none-any.whl
Size 238.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ae30bda1e51b3777a74100d881f804a7aa9175ca8b7adbe5eacb68c10e3f8ee8
BLAKE2b-256 checksum
How to use checksums
9f2bdfe40afa229f49765998cd321b8820abc44b3a72a8640582f39806a78953
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 19, 2026.

Transparency log

Release history Release notifications | RSS feed

0.1.2

2 release files

This release

0.1.1 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page