mqttxx 2.0.2__py3-none-any.whl → 3.1.2__py3-none-any.whl

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.
@@ -0,0 +1,13 @@
1
+ mqttxx/__init__.py,sha256=bNvLfcG5AgCpOrS1B4LEsDeVi-cKLuw0fYTOdlJoxS8,2072
2
+ mqttxx/client.py,sha256=lc1V6yPqq15PhmmU6L1Tspikg1b18jPLTkZe7ElmSTE,21807
3
+ mqttxx/config.py,sha256=A4AK54FL3eNyOCQ4E5IowIkC34zpT43d1bJzX0VOAHk,5986
4
+ mqttxx/conventions.py,sha256=ZP5FkYBCJiee1co041pbPv4x31i9HuG9KbeUFaDsoBs,4683
5
+ mqttxx/events.py,sha256=IG6ca4Gmm1P4sJUmZXTw6I2lHjVawJkpBjzX1GNerBM,11368
6
+ mqttxx/exceptions.py,sha256=EC9NZmvZbfrK9iEdSyoYt4pUl0dp7yVup8mjOfgBaMw,3704
7
+ mqttxx/protocol.py,sha256=GZYL2Fvi4-R0Tuyxrcp8-ttLWx9wyoH4-ZtzNq1wYxo,10034
8
+ mqttxx/rpc.py,sha256=6syEzmDZr2BQE2_LbdtrdTeWkrz3MAlAebbuX9O5-pY,15197
9
+ mqttxx-3.1.2.dist-info/LICENSE,sha256=tfwCF8HPjAvvq_veljjH3_YL90X6ttkjTpRl3BGcXTg,1067
10
+ mqttxx-3.1.2.dist-info/METADATA,sha256=ncDgBLxFEkCYeQhIz1nO3Mt5SUrodJq89U2ONATVoOg,24337
11
+ mqttxx-3.1.2.dist-info/WHEEL,sha256=Z4pYXqR_rTB7OWNDYFOm1qRk0RX6GFP2o8LgvP453Hk,91
12
+ mqttxx-3.1.2.dist-info/top_level.txt,sha256=4SNRXgOpGCT6ThBzAb8zhUmTyZwnIracAIJWAdXXzw0,7
13
+ mqttxx-3.1.2.dist-info/RECORD,,
@@ -1,490 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: mqttxx
3
- Version: 2.0.2
4
- Summary: 基于 aiomqtt 的高级 MQTT 客户端和 RPC 框架
5
- Author: MQTTX Team
6
- License: MIT
7
- Keywords: mqtt,rpc,async,iot,messaging
8
- Description-Content-Type: text/markdown
9
- License-File: LICENSE
10
- Requires-Dist: aiomqtt <3.0.0,>=2.0.0
11
- Requires-Dist: loguru >=0.7.0
12
- Provides-Extra: dev
13
- Requires-Dist: pytest ; extra == 'dev'
14
- Requires-Dist: pytest-asyncio ; extra == 'dev'
15
- Requires-Dist: ruff ; extra == 'dev'
16
- Requires-Dist: build ; extra == 'dev'
17
- Requires-Dist: twine ; extra == 'dev'
18
-
19
- # MQTTX
20
-
21
- 基于 [aiomqtt](https://github.com/sbtinstruments/aiomqtt) 的高级 MQTT 客户端和 RPC 框架。
22
-
23
- **核心特性:**
24
- - ✅ 纯 async/await,无回调
25
- - ✅ 自动重连、订阅队列化
26
- - ✅ 双向对等 RPC(带权限控制)
27
- - ✅ TLS/SSL、认证支持
28
- - ✅ 约定式 RPC(零配置)
29
-
30
- ---
31
-
32
- ## 安装
33
-
34
- ```bash
35
- pip install mqttxx
36
- ```
37
-
38
- ---
39
-
40
- ## 快速开始
41
-
42
- ### 1. MQTT 基础用法
43
-
44
- ```python
45
- import asyncio
46
- from mqttxx import MQTTClient, MQTTConfig
47
-
48
- async def main():
49
- config = MQTTConfig(
50
- broker_host="localhost",
51
- broker_port=1883,
52
- client_id="device_123"
53
- )
54
-
55
- async with MQTTClient(config) as client:
56
- # 订阅主题
57
- def on_message(topic, message):
58
- print(f"{topic}: {message}")
59
-
60
- client.subscribe("sensors/#", on_message)
61
-
62
- # 发布消息
63
- await client.publish("sensors/temperature", "25.5", qos=1)
64
-
65
- await asyncio.sleep(60)
66
-
67
- asyncio.run(main())
68
- ```
69
-
70
- ---
71
-
72
- ### 2. 约定式 RPC(零配置)
73
-
74
- **推荐**:使用 `ConventionalRPCManager`,自动订阅 + 自动注入 reply_to。
75
-
76
- ```python
77
- from mqttxx import MQTTClient, MQTTConfig, ConventionalRPCManager
78
-
79
- # 边缘设备
80
- async def edge_device():
81
- client_id = "device_123"
82
- config = MQTTConfig(
83
- broker_host="localhost",
84
- client_id=client_id,
85
- )
86
-
87
- async with MQTTClient(config) as client:
88
- # 自动订阅 edge/device_123
89
- rpc = ConventionalRPCManager(client, my_topic=f"edge/{client_id}")
90
-
91
- @rpc.register("get_status")
92
- async def get_status(params):
93
- return {"status": "online"}
94
-
95
- # 调用云端(自动注入 reply_to="edge/device_123")
96
- config = await rpc.call("cloud/config-service", "get_device_config")
97
- print(config)
98
-
99
- await asyncio.sleep(60)
100
-
101
- # 云端服务
102
- async def cloud_service():
103
- client_id = "config-service"
104
- config = MQTTConfig(
105
- broker_host="localhost",
106
- client_id=client_id,
107
- )
108
-
109
- async with MQTTClient(config) as client:
110
- # 自动订阅 cloud/config-service
111
- rpc = ConventionalRPCManager(client, my_topic=f"cloud/{client_id}")
112
-
113
- @rpc.register("get_device_config")
114
- async def get_device_config(params):
115
- return {"update_interval": 60, "servers": ["s1", "s2"]}
116
-
117
- # 调用边缘设备(自动注入 reply_to="cloud/config-service")
118
- status = await rpc.call("edge/device_123", "execute_command", params={"cmd": "restart"})
119
- print(status)
120
-
121
- await asyncio.sleep(60)
122
-
123
- # 运行边缘设备或云端
124
- asyncio.run(edge_device()) # 或 asyncio.run(cloud_service())
125
- ```
126
-
127
- **对比传统 RPC:**
128
-
129
- | 场景 | 传统 RPC | 约定式 RPC |
130
- |-----|---------|-----------|
131
- | 初始化 | `rpc = RPCManager(client)`<br>`client.subscribe("edge/123", rpc.handle_rpc_message)` | `rpc = ConventionalRPCManager(client, my_topic="edge/123")`<br>→ 自动订阅 |
132
- | 调用 | `await rpc.call(topic="cloud/svc", method="get", reply_to="edge/123")` | `await rpc.call("cloud/svc", "get")` |
133
- | 代码量 | 100% | **60%** ↓ |
134
-
135
- ---
136
-
137
- ### 3. RPC 基础用法(传统模式)
138
-
139
- 需要手动订阅和传递 `reply_to`,适用于需要精细控制的场景。
140
-
141
- ```python
142
- from mqttxx import MQTTClient, MQTTConfig, RPCManager
143
-
144
- async def main():
145
- config = MQTTConfig(broker_host="localhost", client_id="device_001")
146
-
147
- async with MQTTClient(config) as client:
148
- rpc = RPCManager(client)
149
-
150
- # 注册本地方法
151
- @rpc.register("get_status")
152
- async def get_status(params):
153
- return {"status": "online", "cpu": 45.2}
154
-
155
- # 订阅 RPC 主题
156
- client.subscribe(
157
- "server/device_001",
158
- rpc.handle_rpc_message
159
- )
160
-
161
- # 调用远程方法
162
- result = await rpc.call(
163
- topic="bots/device_002",
164
- method="get_data",
165
- reply_to="server/device_001",
166
- timeout=5
167
- )
168
- print(result) # {"data": [1, 2, 3]}
169
-
170
- await asyncio.sleep(60)
171
-
172
- asyncio.run(main())
173
- ```
174
-
175
- ---
176
-
177
- ### 4. RPC 权限控制
178
-
179
- ```python
180
- from mqttxx import RPCManager, RPCRequest
181
-
182
- async def auth_check(caller_id: str, method: str, request: RPCRequest) -> bool:
183
- # 敏感方法只允许管理员
184
- if method in ["delete_user", "reset_system"]:
185
- return caller_id in ["admin_001", "admin_002"]
186
- return True
187
-
188
- rpc = RPCManager(client, auth_callback=auth_check)
189
-
190
- @rpc.register("delete_user")
191
- async def delete_user(params):
192
- return {"result": "user deleted"}
193
-
194
- # 未授权调用会返回 "Permission denied"
195
- ```
196
-
197
- ---
198
-
199
- ### 5. TLS/SSL 和认证
200
-
201
- ```python
202
- from mqttxx import MQTTConfig, TLSConfig, AuthConfig
203
- from pathlib import Path
204
-
205
- config = MQTTConfig(
206
- broker_host="secure.mqtt.example.com",
207
- broker_port=8883,
208
- tls=TLSConfig(
209
- enabled=True,
210
- ca_certs=Path("ca.crt"),
211
- certfile=Path("client.crt"),
212
- keyfile=Path("client.key"),
213
- ),
214
- auth=AuthConfig(
215
- username="mqtt_user",
216
- password="mqtt_password",
217
- ),
218
- )
219
-
220
- async with MQTTClient(config) as client:
221
- await client.publish("secure/topic", "encrypted message")
222
- ```
223
-
224
- ---
225
-
226
- ## API 速查
227
-
228
- ### MQTTClient
229
-
230
- ```python
231
- class MQTTClient:
232
- def __init__(self, config: MQTTConfig)
233
- async def connect(self) -> None
234
- async def disconnect(self) -> None
235
- def subscribe(self, topic: str, handler: Callable) -> None
236
- async def publish(self, topic: str, payload: str, qos: int = 0) -> None
237
-
238
- @property
239
- def is_connected(self) -> bool
240
- ```
241
-
242
- ---
243
-
244
- ### RPCManager(传统 RPC)
245
-
246
- ```python
247
- class RPCManager:
248
- def __init__(self, client: MQTTClient, config: RPCConfig = None, auth_callback: AuthCallback = None)
249
-
250
- def register(self, method_name: str) # 装饰器
251
- def unregister(self, method_name: str) -> None
252
- def handle_rpc_message(self, topic: str, message: RPCRequest | RPCResponse) -> None
253
-
254
- async def call(
255
- self,
256
- topic: str,
257
- method: str,
258
- params: Any = None,
259
- reply_to: str = None, # 必填
260
- timeout: float = None,
261
- ) -> Any
262
- ```
263
-
264
- ---
265
-
266
- ### ConventionalRPCManager(约定式 RPC)
267
-
268
- ```python
269
- class ConventionalRPCManager(RPCManager):
270
- def __init__(
271
- self,
272
- client: MQTTClient,
273
- my_topic: str, # 本节点 topic(自动订阅,自动注入到 reply_to)
274
- config: RPCConfig = None,
275
- auth_callback: AuthCallback = None,
276
- )
277
-
278
- async def call(
279
- self,
280
- topic: str, # 对方的 topic
281
- method: str,
282
- params: Any = None,
283
- timeout: float = None,
284
- reply_to: str = None, # 可选,默认使用 my_topic
285
- ) -> Any
286
-
287
- # 属性
288
- my_topic: str # 当前 topic(只读)
289
- ```
290
-
291
- **使用示例:**
292
-
293
- ```python
294
- # 边缘设备
295
- rpc = ConventionalRPCManager(client, my_topic="edge/device_123")
296
- config = await rpc.call("cloud/config-service", "get_config")
297
-
298
- # 云端服务
299
- rpc = ConventionalRPCManager(client, my_topic="cloud/config-service")
300
- status = await rpc.call("edge/device_123", "execute_command")
301
-
302
- # 微服务
303
- rpc = ConventionalRPCManager(client, my_topic="auth-service")
304
- user = await rpc.call("user-service", "get_user", params={"id": 123})
305
- ```
306
-
307
- ---
308
-
309
- ## 配置对象
310
-
311
- ### MQTTConfig
312
-
313
- ```python
314
- @dataclass
315
- class MQTTConfig:
316
- broker_host: str
317
- broker_port: int = 1883
318
- client_id: str = "" # 空字符串 = 自动生成
319
- keepalive: int = 60
320
- clean_session: bool = False
321
- tls: TLSConfig = field(default_factory=TLSConfig)
322
- auth: AuthConfig = field(default_factory=AuthConfig)
323
- reconnect: ReconnectConfig = field(default_factory=ReconnectConfig)
324
- max_queued_messages: int = 0 # 0 = 无限
325
- max_payload_size: int = 1024 * 1024 # 1MB
326
- ```
327
-
328
- ### TLSConfig
329
-
330
- ```python
331
- @dataclass
332
- class TLSConfig:
333
- enabled: bool = False
334
- ca_certs: Optional[Path] = None
335
- certfile: Optional[Path] = None
336
- keyfile: Optional[Path] = None
337
- verify_mode: str = "CERT_REQUIRED" # CERT_REQUIRED | CERT_OPTIONAL | CERT_NONE
338
- check_hostname: bool = True
339
- ```
340
-
341
- ### AuthConfig
342
-
343
- ```python
344
- @dataclass
345
- class AuthConfig:
346
- username: Optional[str] = None
347
- password: Optional[str] = None
348
- ```
349
-
350
- ### ReconnectConfig
351
-
352
- ```python
353
- @dataclass
354
- class ReconnectConfig:
355
- enabled: bool = True
356
- interval: int = 5 # 初始重连间隔(秒)
357
- max_attempts: int = 0 # 0 = 无限重试
358
- backoff_multiplier: float = 1.5 # 指数退避倍数
359
- max_interval: int = 60 # 最大重连间隔(秒)
360
- ```
361
-
362
- ### RPCConfig
363
-
364
- ```python
365
- @dataclass
366
- class RPCConfig:
367
- default_timeout: float = 30.0 # 默认超时时间(秒)
368
- max_concurrent_calls: int = 100 # 最大并发调用数
369
- ```
370
-
371
- ---
372
-
373
- ## 异常系统
374
-
375
- ```python
376
- # 基础异常
377
- class MQTTXError(Exception)
378
- class ConnectionError(MQTTXError)
379
- class MessageError(MQTTXError)
380
- class RPCError(MQTTXError)
381
-
382
- # RPC 异常
383
- class RPCTimeoutError(RPCError) # RPC 调用超时
384
- class RPCRemoteError(RPCError) # 远程方法执行失败
385
- class RPCMethodNotFoundError(RPCError) # 方法未找到
386
- class PermissionDeniedError(RPCError) # 权限拒绝
387
- class TooManyConcurrentCallsError(RPCError) # 并发调用超限
388
-
389
- # 错误码
390
- class ErrorCode(IntEnum):
391
- NOT_CONNECTED = 1001
392
- RPC_TIMEOUT = 3002
393
- PERMISSION_DENIED = 4001
394
- # ... 更多错误码见源码
395
- ```
396
-
397
- **使用示例:**
398
-
399
- ```python
400
- from mqttxx import RPCTimeoutError, RPCRemoteError
401
-
402
- try:
403
- result = await rpc.call_bot("456", "get_data", timeout=5)
404
- except RPCTimeoutError:
405
- print("调用超时")
406
- except RPCRemoteError as e:
407
- print(f"远程方法执行失败: {e}")
408
- ```
409
-
410
- ---
411
-
412
- ## RPC 消息协议
413
-
414
- ### 请求
415
-
416
- ```json
417
- {
418
- "type": "rpc_request",
419
- "request_id": "uuid-string",
420
- "method": "get_status",
421
- "params": {"id": 123},
422
- "reply_to": "server/device_001",
423
- "caller_id": "device_002"
424
- }
425
- ```
426
-
427
- ### 响应(成功)
428
-
429
- ```json
430
- {
431
- "type": "rpc_response",
432
- "request_id": "uuid-string",
433
- "result": {"status": "online"}
434
- }
435
- ```
436
-
437
- ### 响应(错误)
438
-
439
- ```json
440
- {
441
- "type": "rpc_response",
442
- "request_id": "uuid-string",
443
- "error": "Permission denied"
444
- }
445
- ```
446
-
447
- ---
448
-
449
- ## v2.0.0 重大变更
450
-
451
- 从 v2.0.0 开始,完全重写为基于 aiomqtt(纯 async/await),**不兼容** v0.x.x(gmqtt)。
452
-
453
- **主要变化:**
454
- - ✅ aiomqtt 替代 gmqtt
455
- - ✅ 原生 dataclass 替代 python-box(性能提升 6 倍)
456
- - ✅ 修复所有 P0 缺陷(并发竞态、连接状态、RPC 超时兜底)
457
- - ✅ 新增约定式 RPC(`ConventionalRPCManager`)
458
- - ✅ 新增权限控制(`auth_callback`)
459
- - ✅ 新增 TLS/SSL 支持
460
-
461
- **迁移关键点:**
462
- 1. 使用 `MQTTConfig` 配置对象
463
- 2. 使用 `async with` 上下文管理器
464
- 3. `publish_message()` → `publish()`
465
- 4. 移除 `EventEmitter`(改用 dict)
466
-
467
- ---
468
-
469
- ## 依赖
470
-
471
- - Python >= 3.10
472
- - aiomqtt >= 2.0.0, < 3.0.0
473
- - loguru >= 0.7.0
474
-
475
- ---
476
-
477
- ## 开发
478
-
479
- ```bash
480
- git clone https://github.com/yourusername/mqttx.git
481
- cd mqttx
482
- pip install -e ".[dev]"
483
- pytest tests/
484
- ```
485
-
486
- ---
487
-
488
- ## License
489
-
490
- MIT
@@ -1,12 +0,0 @@
1
- mqttxx/__init__.py,sha256=9jhV_niQTYU2D4aD4AfVvWAAfbYf4o2vjwbwSMA_Rv4,1722
2
- mqttxx/client.py,sha256=mpJS4oTm9V9uqoU4kiRlNK-M8pBVb5mImvxvjyIi57E,16452
3
- mqttxx/config.py,sha256=MlhayABPcyHWAQR1kkWT5SNlVPIcJ5j8tBikvt1kMNE,5101
4
- mqttxx/conventions.py,sha256=d_OwOvLG4cdptZ2wmrsKe055LRVZXMO0pmt-OAgJhvI,4507
5
- mqttxx/exceptions.py,sha256=EC9NZmvZbfrK9iEdSyoYt4pUl0dp7yVup8mjOfgBaMw,3704
6
- mqttxx/protocol.py,sha256=mZfGY3naGGT_mDsv9enFPiAgUFjoqA5WbZhaT5xYCBQ,5313
7
- mqttxx/rpc.py,sha256=9klpfn3Qh2keq8Sk312n0psXa3BSI8ThlFXJZ0O1_wU,13749
8
- mqttxx-2.0.2.dist-info/LICENSE,sha256=tfwCF8HPjAvvq_veljjH3_YL90X6ttkjTpRl3BGcXTg,1067
9
- mqttxx-2.0.2.dist-info/METADATA,sha256=19jtxWnJVWu0vsr5qL5xQl8akMy_H-oktO-x5pPA-Gs,11545
10
- mqttxx-2.0.2.dist-info/WHEEL,sha256=Z4pYXqR_rTB7OWNDYFOm1qRk0RX6GFP2o8LgvP453Hk,91
11
- mqttxx-2.0.2.dist-info/top_level.txt,sha256=4SNRXgOpGCT6ThBzAb8zhUmTyZwnIracAIJWAdXXzw0,7
12
- mqttxx-2.0.2.dist-info/RECORD,,
File without changes