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.
- mqttxx/__init__.py +13 -5
- mqttxx/client.py +295 -152
- mqttxx/config.py +14 -0
- mqttxx/conventions.py +8 -5
- mqttxx/events.py +340 -0
- mqttxx/protocol.py +189 -14
- mqttxx/rpc.py +63 -30
- mqttxx-3.1.2.dist-info/METADATA +910 -0
- mqttxx-3.1.2.dist-info/RECORD +13 -0
- mqttxx-2.0.2.dist-info/METADATA +0 -490
- mqttxx-2.0.2.dist-info/RECORD +0 -12
- {mqttxx-2.0.2.dist-info → mqttxx-3.1.2.dist-info}/LICENSE +0 -0
- {mqttxx-2.0.2.dist-info → mqttxx-3.1.2.dist-info}/WHEEL +0 -0
- {mqttxx-2.0.2.dist-info → mqttxx-3.1.2.dist-info}/top_level.txt +0 -0
|
@@ -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,,
|
mqttxx-2.0.2.dist-info/METADATA
DELETED
|
@@ -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
|
mqttxx-2.0.2.dist-info/RECORD
DELETED
|
@@ -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
|
|
File without changes
|
|
File without changes
|