pypopur 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. pypopur-0.1.0/LICENSE +21 -0
  2. pypopur-0.1.0/PKG-INFO +183 -0
  3. pypopur-0.1.0/README.md +152 -0
  4. pypopur-0.1.0/pyproject.toml +60 -0
  5. pypopur-0.1.0/setup.cfg +4 -0
  6. pypopur-0.1.0/src/pypopur/__init__.py +308 -0
  7. pypopur-0.1.0/src/pypopur/app/__init__.py +78 -0
  8. pypopur-0.1.0/src/pypopur/app/dp101.py +524 -0
  9. pypopur-0.1.0/src/pypopur/app/dp_string.py +401 -0
  10. pypopur-0.1.0/src/pypopur/app/helper.py +625 -0
  11. pypopur-0.1.0/src/pypopur/app/manager.py +776 -0
  12. pypopur-0.1.0/src/pypopur/app/repository.py +585 -0
  13. pypopur-0.1.0/src/pypopur/app/resolver.py +171 -0
  14. pypopur-0.1.0/src/pypopur/bootstrap.py +22 -0
  15. pypopur-0.1.0/src/pypopur/client.py +321 -0
  16. pypopur-0.1.0/src/pypopur/cloud.py +449 -0
  17. pypopur-0.1.0/src/pypopur/codec.py +86 -0
  18. pypopur-0.1.0/src/pypopur/discovery.py +374 -0
  19. pypopur-0.1.0/src/pypopur/dps.py +860 -0
  20. pypopur-0.1.0/src/pypopur/events.py +408 -0
  21. pypopur-0.1.0/src/pypopur/exceptions.py +37 -0
  22. pypopur-0.1.0/src/pypopur/local.py +477 -0
  23. pypopur-0.1.0/src/pypopur/mobile.py +2647 -0
  24. pypopur-0.1.0/src/pypopur/models.py +331 -0
  25. pypopur-0.1.0/src/pypopur/pipeline.py +389 -0
  26. pypopur-0.1.0/src/pypopur/popur_app2_material.py +12 -0
  27. pypopur-0.1.0/src/pypopur/py.typed +1 -0
  28. pypopur-0.1.0/src/pypopur/reads.py +420 -0
  29. pypopur-0.1.0/src/pypopur/reference.py +314 -0
  30. pypopur-0.1.0/src/pypopur/sdk/__init__.py +50 -0
  31. pypopur-0.1.0/src/pypopur/sdk/_fastjson.py +58 -0
  32. pypopur-0.1.0/src/pypopur/sdk/_java.py +107 -0
  33. pypopur-0.1.0/src/pypopur/sdk/atop.py +724 -0
  34. pypopur-0.1.0/src/pypopur/sdk/comm_pipeline.py +1038 -0
  35. pypopur-0.1.0/src/pypopur/sdk/crypto.py +166 -0
  36. pypopur-0.1.0/src/pypopur/sdk/dedup.py +114 -0
  37. pypopur-0.1.0/src/pypopur/sdk/device_cache.py +909 -0
  38. pypopur-0.1.0/src/pypopur/sdk/device_id.py +122 -0
  39. pypopur-0.1.0/src/pypopur/sdk/discovery.py +597 -0
  40. pypopur-0.1.0/src/pypopur/sdk/hexutil.py +138 -0
  41. pypopur-0.1.0/src/pypopur/sdk/lan_control.py +1321 -0
  42. pypopur-0.1.0/src/pypopur/sdk/lan_framing.py +218 -0
  43. pypopur-0.1.0/src/pypopur/sdk/lan_session.py +1989 -0
  44. pypopur-0.1.0/src/pypopur/sdk/lan_socket.py +957 -0
  45. pypopur-0.1.0/src/pypopur/sdk/low_power.py +431 -0
  46. pypopur-0.1.0/src/pypopur/sdk/mqtt_client.py +486 -0
  47. pypopur-0.1.0/src/pypopur/sdk/mqtt_framing.py +404 -0
  48. pypopur-0.1.0/src/pypopur/sdk/mqtt_session.py +451 -0
  49. pypopur-0.1.0/src/pypopur/sdk/mqtt_sign.py +115 -0
  50. pypopur-0.1.0/src/pypopur/sdk/sando.py +71 -0
  51. pypopur-0.1.0/src/pypopur/sdk/schema.py +223 -0
  52. pypopur-0.1.0/src/pypopur/sdk/security.py +224 -0
  53. pypopur-0.1.0/src/pypopur/sdk/thing_model.py +1229 -0
  54. pypopur-0.1.0/src/pypopur/sdk/timestamp.py +106 -0
  55. pypopur-0.1.0/src/pypopur/sdk/validation.py +293 -0
  56. pypopur-0.1.0/src/pypopur/transport.py +125 -0
  57. pypopur-0.1.0/src/pypopur/writes.py +127 -0
  58. pypopur-0.1.0/src/pypopur.egg-info/PKG-INFO +183 -0
  59. pypopur-0.1.0/src/pypopur.egg-info/SOURCES.txt +85 -0
  60. pypopur-0.1.0/src/pypopur.egg-info/dependency_links.txt +1 -0
  61. pypopur-0.1.0/src/pypopur.egg-info/requires.txt +6 -0
  62. pypopur-0.1.0/src/pypopur.egg-info/top_level.txt +1 -0
  63. pypopur-0.1.0/tests/test_app_helper.py +389 -0
  64. pypopur-0.1.0/tests/test_app_layer.py +1035 -0
  65. pypopur-0.1.0/tests/test_atop.py +418 -0
  66. pypopur-0.1.0/tests/test_bootstrap.py +29 -0
  67. pypopur-0.1.0/tests/test_client.py +271 -0
  68. pypopur-0.1.0/tests/test_cloud.py +153 -0
  69. pypopur-0.1.0/tests/test_cloud_channel.py +168 -0
  70. pypopur-0.1.0/tests/test_codecs.py +332 -0
  71. pypopur-0.1.0/tests/test_discovery.py +519 -0
  72. pypopur-0.1.0/tests/test_events.py +537 -0
  73. pypopur-0.1.0/tests/test_fallback_transport.py +143 -0
  74. pypopur-0.1.0/tests/test_hardening.py +248 -0
  75. pypopur-0.1.0/tests/test_lan_session.py +938 -0
  76. pypopur-0.1.0/tests/test_lan_socket.py +424 -0
  77. pypopur-0.1.0/tests/test_local.py +278 -0
  78. pypopur-0.1.0/tests/test_low_power.py +301 -0
  79. pypopur-0.1.0/tests/test_mobile.py +1417 -0
  80. pypopur-0.1.0/tests/test_mqtt_client.py +339 -0
  81. pypopur-0.1.0/tests/test_native_vectors.py +126 -0
  82. pypopur-0.1.0/tests/test_pipeline.py +402 -0
  83. pypopur-0.1.0/tests/test_reads.py +218 -0
  84. pypopur-0.1.0/tests/test_reference.py +58 -0
  85. pypopur-0.1.0/tests/test_sdk.py +1854 -0
  86. pypopur-0.1.0/tests/test_thing_model.py +701 -0
  87. pypopur-0.1.0/tests/test_writes.py +598 -0
pypopur-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 BookCatKid
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
pypopur-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,183 @@
1
+ Metadata-Version: 2.4
2
+ Name: pypopur
3
+ Version: 0.1.0
4
+ Summary: Async Python client for the Popur S7 smart litter box — LAN control, cloud APIs, MQTT events, and pet tracking
5
+ Author: BookCatKid
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/BookCatKid/pypopur
8
+ Project-URL: Repository, https://github.com/BookCatKid/pypopur
9
+ Project-URL: Issues, https://github.com/BookCatKid/pypopur/issues
10
+ Project-URL: Documentation, https://github.com/BookCatKid/pypopur#readme
11
+ Project-URL: Changelog, https://github.com/BookCatKid/pypopur/releases
12
+ Keywords: popur,s7,litter-box,tuya,thingclips,home-assistant,iot,smart-home
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Home Automation
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Classifier: Typing :: Typed
22
+ Requires-Python: >=3.11
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Requires-Dist: cryptography>=43
26
+ Requires-Dist: tinytuya>=1.15
27
+ Provides-Extra: test
28
+ Requires-Dist: pytest>=8; extra == "test"
29
+ Requires-Dist: pytest-asyncio>=0.24; extra == "test"
30
+ Dynamic: license-file
31
+
32
+ # pypopur
33
+
34
+ **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.
35
+
36
+ [![PyPI](https://img.shields.io/pypi/v/pypopur)](https://pypi.org/project/pypopur/)
37
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
38
+ [![Tests](https://github.com/BookCatKid/pypopur/actions/workflows/ci.yml/badge.svg)](https://github.com/BookCatKid/pypopur/actions/workflows/ci.yml)
39
+
40
+ ## Features
41
+
42
+ - **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
43
+ - **Cloud APIs** — full ThingClips/Popur app surface: auth, homes, devices, DP shadow, pets, pet records, firmware, timers, device management
44
+ - **MQTT events** — real-time DP deltas and alert events over TLS to the app's broker, with reconnect/resubscribe handling
45
+ - **Hybrid transport** — LAN-first with automatic cloud fallback; slow reconciliation tier for settings DPs and pet data the LAN omits
46
+ - **Discovery** — passive UDP announcements plus active host location via ARP MAC match and port-6668 subnet scan
47
+ - **DP codecs** — all firmware-4 packed payloads decoded (settings, calibration, radar config, cat weight, visit records)
48
+ - **Pet tracking** — household pet profiles, per-visit weight/duration records decoded from the app's own format
49
+ - **Home Assistant integration** — full entity coverage (sensors, switches, buttons, numbers, selects) in `custom_components/popur/`
50
+
51
+ ## Install
52
+
53
+ ```bash
54
+ pip install pypopur
55
+ ```
56
+
57
+ Requires Python 3.11+.
58
+
59
+ ## Quick start — local control
60
+
61
+ ```python
62
+ import asyncio
63
+ from pypopur import LocalTuyaTransport, PopurClient
64
+
65
+ async def main():
66
+ transport = LocalTuyaTransport(
67
+ device_id="your-device-id",
68
+ host="192.168.1.x",
69
+ local_key="your-local-key",
70
+ )
71
+ client = PopurClient(transport)
72
+
73
+ async with client:
74
+ snapshot = await client.refresh()
75
+ print(snapshot.machine_status, snapshot.cat_present, snapshot.bin_full)
76
+ await client.start_cleaning()
77
+
78
+ asyncio.run(main())
79
+ ```
80
+
81
+ ## Quick start — cloud account
82
+
83
+ ```python
84
+ import asyncio
85
+ from pypopur.mobile import MobileAppProfile, PopurAccount, ThingMobileApi
86
+
87
+ async def main():
88
+ api = ThingMobileApi(
89
+ MobileAppProfile.bundled_popur_app2(),
90
+ install_id="a-stable-id-for-this-install",
91
+ )
92
+ account = PopurAccount(api)
93
+ await account.login("you@example.com", "your-password")
94
+
95
+ home_id = (await account.homes())[0]["gid"]
96
+ devices = await account.home_devices(home_id)
97
+
98
+ for dev in devices:
99
+ print(dev.name, dev.device_id, dev.local_key)
100
+
101
+ asyncio.run(main())
102
+ ```
103
+
104
+ ## Quick start — pets
105
+
106
+ ```python
107
+ pets = await account.pets(home_id)
108
+ for pet in pets:
109
+ print(pet.name, pet.pet_type, pet.weight)
110
+
111
+ page = await account.pet_records(home_id, page_size=50)
112
+ for record in page.records:
113
+ usage = record.toilet_usage()
114
+ if usage:
115
+ print(record.pet_id, usage.weight_grams, "g", usage.duration_seconds, "s")
116
+ ```
117
+
118
+ ## Quick start — MQTT events
119
+
120
+ ```python
121
+ events = account.connect_events(
122
+ devices={dev.device_id: dev.local_key},
123
+ on_event=lambda e: print(e.dev_id, e.dps),
124
+ on_connect=lambda: print("connected"),
125
+ )
126
+ await events.connect()
127
+ ```
128
+
129
+ ## Architecture
130
+
131
+ ```
132
+ ┌─────────────────────────────────────────────────┐
133
+ │ PopurClient │
134
+ │ typed controls + DeviceSnapshot │
135
+ ├─────────────────────────────────────────────────┤
136
+ │ FallbackTransport │
137
+ │ LAN primary (3.5) ──fail──► Cloud fallback │
138
+ ├──────────┬──────────────────┬───────────────────┤
139
+ │ LAN TCP │ MQTT TLS │ Cloud HTTPS │
140
+ │ :6668 │ smart/mb/in/ │ a1.tuyaus.com │
141
+ │ DP r/w │ DP deltas │ shadow, pets, │
142
+ │ ~23 DPs │ alerts │ mgmt APIs │
143
+ └──────────┴──────────────────┴───────────────────┘
144
+ ```
145
+
146
+ - **LAN** — fast path for reads/writes; ~23 DPs in status responses
147
+ - **MQTT** — real-time push (protocol-4 deltas, protocol-56 alerts); deltas only, no snapshots
148
+ - **Cloud** — settings DPs LAN omits (102 etc.), pet profiles/records, firmware, timers, device management
149
+
150
+ ## Home Assistant
151
+
152
+ 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`](custom_components/popur/README.md) for details.
153
+
154
+ ## Status
155
+
156
+ - **Tested against**: one Popur S7 (Wi-Fi 3.0.30 / MCU 4.3.0), firmware-4 protocol family
157
+ - **LAN protocol**: verified live — handshake, DP read/write, heartbeat, reconnect
158
+ - **Cloud APIs**: signature-exact to the app; read paths verified live, write paths untested
159
+ - **MQTT**: verified live — connect, subscribe, decode protocol-4/56 frames, reconnect
160
+ - **Test suite**: 769 tests, all passing
161
+
162
+ ## Limitations
163
+
164
+ - Only tested against one device and one firmware version — other S7s may differ
165
+ - App-layer write APIs (pets, device management) are signature-verified but never executed live
166
+ - The standalone LAN transport doesn't fetch the thing model — no schema validation on writes
167
+ - MQTT is event-driven (deltas only) — it can't replace polling for state seeding or reconciliation
168
+ - Settings DPs and pet data require the cloud — they don't exist on the LAN channel
169
+
170
+ ## Development
171
+
172
+ ```bash
173
+ python -m pytest tests/ # 769 tests
174
+ ruff check src tests # lint
175
+ ```
176
+
177
+ ## License
178
+
179
+ MIT — see [LICENSE](LICENSE).
180
+
181
+ ## Disclaimer
182
+
183
+ 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.
@@ -0,0 +1,152 @@
1
+ # pypopur
2
+
3
+ **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.
4
+
5
+ [![PyPI](https://img.shields.io/pypi/v/pypopur)](https://pypi.org/project/pypopur/)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
7
+ [![Tests](https://github.com/BookCatKid/pypopur/actions/workflows/ci.yml/badge.svg)](https://github.com/BookCatKid/pypopur/actions/workflows/ci.yml)
8
+
9
+ ## Features
10
+
11
+ - **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
12
+ - **Cloud APIs** — full ThingClips/Popur app surface: auth, homes, devices, DP shadow, pets, pet records, firmware, timers, device management
13
+ - **MQTT events** — real-time DP deltas and alert events over TLS to the app's broker, with reconnect/resubscribe handling
14
+ - **Hybrid transport** — LAN-first with automatic cloud fallback; slow reconciliation tier for settings DPs and pet data the LAN omits
15
+ - **Discovery** — passive UDP announcements plus active host location via ARP MAC match and port-6668 subnet scan
16
+ - **DP codecs** — all firmware-4 packed payloads decoded (settings, calibration, radar config, cat weight, visit records)
17
+ - **Pet tracking** — household pet profiles, per-visit weight/duration records decoded from the app's own format
18
+ - **Home Assistant integration** — full entity coverage (sensors, switches, buttons, numbers, selects) in `custom_components/popur/`
19
+
20
+ ## Install
21
+
22
+ ```bash
23
+ pip install pypopur
24
+ ```
25
+
26
+ Requires Python 3.11+.
27
+
28
+ ## Quick start — local control
29
+
30
+ ```python
31
+ import asyncio
32
+ from pypopur import LocalTuyaTransport, PopurClient
33
+
34
+ async def main():
35
+ transport = LocalTuyaTransport(
36
+ device_id="your-device-id",
37
+ host="192.168.1.x",
38
+ local_key="your-local-key",
39
+ )
40
+ client = PopurClient(transport)
41
+
42
+ async with client:
43
+ snapshot = await client.refresh()
44
+ print(snapshot.machine_status, snapshot.cat_present, snapshot.bin_full)
45
+ await client.start_cleaning()
46
+
47
+ asyncio.run(main())
48
+ ```
49
+
50
+ ## Quick start — cloud account
51
+
52
+ ```python
53
+ import asyncio
54
+ from pypopur.mobile import MobileAppProfile, PopurAccount, ThingMobileApi
55
+
56
+ async def main():
57
+ api = ThingMobileApi(
58
+ MobileAppProfile.bundled_popur_app2(),
59
+ install_id="a-stable-id-for-this-install",
60
+ )
61
+ account = PopurAccount(api)
62
+ await account.login("you@example.com", "your-password")
63
+
64
+ home_id = (await account.homes())[0]["gid"]
65
+ devices = await account.home_devices(home_id)
66
+
67
+ for dev in devices:
68
+ print(dev.name, dev.device_id, dev.local_key)
69
+
70
+ asyncio.run(main())
71
+ ```
72
+
73
+ ## Quick start — pets
74
+
75
+ ```python
76
+ pets = await account.pets(home_id)
77
+ for pet in pets:
78
+ print(pet.name, pet.pet_type, pet.weight)
79
+
80
+ page = await account.pet_records(home_id, page_size=50)
81
+ for record in page.records:
82
+ usage = record.toilet_usage()
83
+ if usage:
84
+ print(record.pet_id, usage.weight_grams, "g", usage.duration_seconds, "s")
85
+ ```
86
+
87
+ ## Quick start — MQTT events
88
+
89
+ ```python
90
+ events = account.connect_events(
91
+ devices={dev.device_id: dev.local_key},
92
+ on_event=lambda e: print(e.dev_id, e.dps),
93
+ on_connect=lambda: print("connected"),
94
+ )
95
+ await events.connect()
96
+ ```
97
+
98
+ ## Architecture
99
+
100
+ ```
101
+ ┌─────────────────────────────────────────────────┐
102
+ │ PopurClient │
103
+ │ typed controls + DeviceSnapshot │
104
+ ├─────────────────────────────────────────────────┤
105
+ │ FallbackTransport │
106
+ │ LAN primary (3.5) ──fail──► Cloud fallback │
107
+ ├──────────┬──────────────────┬───────────────────┤
108
+ │ LAN TCP │ MQTT TLS │ Cloud HTTPS │
109
+ │ :6668 │ smart/mb/in/ │ a1.tuyaus.com │
110
+ │ DP r/w │ DP deltas │ shadow, pets, │
111
+ │ ~23 DPs │ alerts │ mgmt APIs │
112
+ └──────────┴──────────────────┴───────────────────┘
113
+ ```
114
+
115
+ - **LAN** — fast path for reads/writes; ~23 DPs in status responses
116
+ - **MQTT** — real-time push (protocol-4 deltas, protocol-56 alerts); deltas only, no snapshots
117
+ - **Cloud** — settings DPs LAN omits (102 etc.), pet profiles/records, firmware, timers, device management
118
+
119
+ ## Home Assistant
120
+
121
+ 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`](custom_components/popur/README.md) for details.
122
+
123
+ ## Status
124
+
125
+ - **Tested against**: one Popur S7 (Wi-Fi 3.0.30 / MCU 4.3.0), firmware-4 protocol family
126
+ - **LAN protocol**: verified live — handshake, DP read/write, heartbeat, reconnect
127
+ - **Cloud APIs**: signature-exact to the app; read paths verified live, write paths untested
128
+ - **MQTT**: verified live — connect, subscribe, decode protocol-4/56 frames, reconnect
129
+ - **Test suite**: 769 tests, all passing
130
+
131
+ ## Limitations
132
+
133
+ - Only tested against one device and one firmware version — other S7s may differ
134
+ - App-layer write APIs (pets, device management) are signature-verified but never executed live
135
+ - The standalone LAN transport doesn't fetch the thing model — no schema validation on writes
136
+ - MQTT is event-driven (deltas only) — it can't replace polling for state seeding or reconciliation
137
+ - Settings DPs and pet data require the cloud — they don't exist on the LAN channel
138
+
139
+ ## Development
140
+
141
+ ```bash
142
+ python -m pytest tests/ # 769 tests
143
+ ruff check src tests # lint
144
+ ```
145
+
146
+ ## License
147
+
148
+ MIT — see [LICENSE](LICENSE).
149
+
150
+ ## Disclaimer
151
+
152
+ 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.
@@ -0,0 +1,60 @@
1
+ [build-system]
2
+ requires = ["setuptools>=69"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "pypopur"
7
+ version = "0.1.0"
8
+ description = "Async Python client for the Popur S7 smart litter box — LAN control, cloud APIs, MQTT events, and pet tracking"
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ authors = [{name = "BookCatKid"}]
14
+ keywords = ["popur", "s7", "litter-box", "tuya", "thingclips", "home-assistant", "iot", "smart-home"]
15
+ classifiers = [
16
+ "Development Status :: 3 - Alpha",
17
+ "Intended Audience :: Developers",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3.11",
20
+ "Programming Language :: Python :: 3.12",
21
+ "Programming Language :: Python :: 3.13",
22
+ "Topic :: Home Automation",
23
+ "Topic :: Software Development :: Libraries :: Python Modules",
24
+ "Typing :: Typed",
25
+ ]
26
+ dependencies = [
27
+ "cryptography>=43",
28
+ "tinytuya>=1.15",
29
+ ]
30
+
31
+ [project.optional-dependencies]
32
+ test = ["pytest>=8", "pytest-asyncio>=0.24"]
33
+
34
+ [project.urls]
35
+ Homepage = "https://github.com/BookCatKid/pypopur"
36
+ Repository = "https://github.com/BookCatKid/pypopur"
37
+ Issues = "https://github.com/BookCatKid/pypopur/issues"
38
+ Documentation = "https://github.com/BookCatKid/pypopur#readme"
39
+ Changelog = "https://github.com/BookCatKid/pypopur/releases"
40
+
41
+ [tool.setuptools.packages.find]
42
+ where = ["src"]
43
+
44
+ [tool.setuptools.package-data]
45
+ pypopur = ["py.typed"]
46
+
47
+ [tool.pytest.ini_options]
48
+ addopts = "-q"
49
+ asyncio_mode = "auto"
50
+ testpaths = ["tests"]
51
+
52
+ [tool.ruff]
53
+ target-version = "py311"
54
+ line-length = 100
55
+
56
+ [tool.ruff.lint.per-file-ignores]
57
+ "**/__init__.py" = ["F401"]
58
+ # Ported modules mirror the app's catch-Throwable semantics (listener dispatch,
59
+ # frame parsing) — broad catches are the contract.
60
+ "src/pypopur/**" = ["BLE001", "S110", "S112"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+