mqttxx 2.0.3__py3-none-any.whl → 3.2.1__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 +11 -6
- mqttxx/client.py +239 -183
- mqttxx/config.py +14 -0
- mqttxx/events.py +359 -0
- mqttxx/protocol.py +93 -22
- mqttxx/rpc.py +81 -22
- mqttxx-3.2.1.dist-info/METADATA +931 -0
- mqttxx-3.2.1.dist-info/RECORD +12 -0
- mqttxx/conventions.py +0 -145
- mqttxx-2.0.3.dist-info/METADATA +0 -490
- mqttxx-2.0.3.dist-info/RECORD +0 -12
- {mqttxx-2.0.3.dist-info → mqttxx-3.2.1.dist-info}/LICENSE +0 -0
- {mqttxx-2.0.3.dist-info → mqttxx-3.2.1.dist-info}/WHEEL +0 -0
- {mqttxx-2.0.3.dist-info → mqttxx-3.2.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
mqttxx/__init__.py,sha256=_WoU5oZfMOJetRH1JkLuICp8PlqxpN3BWyXeZEjXeVo,1908
|
|
2
|
+
mqttxx/client.py,sha256=7umy-hJ-TUW9XD_rMdTh71q9Z7rlifnVPvHA80aIbcI,18794
|
|
3
|
+
mqttxx/config.py,sha256=A4AK54FL3eNyOCQ4E5IowIkC34zpT43d1bJzX0VOAHk,5986
|
|
4
|
+
mqttxx/events.py,sha256=bs7Khp6ygDK_aEOXftb74HO3SMqOY_Ln9Se_ro6ryiE,11747
|
|
5
|
+
mqttxx/exceptions.py,sha256=EC9NZmvZbfrK9iEdSyoYt4pUl0dp7yVup8mjOfgBaMw,3704
|
|
6
|
+
mqttxx/protocol.py,sha256=EwXJQ0Ic25oJx55coAfvntOxWh5FYYlBSMV93Cm6hHU,7445
|
|
7
|
+
mqttxx/rpc.py,sha256=KBu2006o9NukqbUk7ehvyIwgrdbO1CqY4rdo-OKr1KM,15831
|
|
8
|
+
mqttxx-3.2.1.dist-info/LICENSE,sha256=tfwCF8HPjAvvq_veljjH3_YL90X6ttkjTpRl3BGcXTg,1067
|
|
9
|
+
mqttxx-3.2.1.dist-info/METADATA,sha256=wFasvCdIOFRVZLnYEM84gxbRLE9uWd4CYa0VEtOFMGE,25544
|
|
10
|
+
mqttxx-3.2.1.dist-info/WHEEL,sha256=Z4pYXqR_rTB7OWNDYFOm1qRk0RX6GFP2o8LgvP453Hk,91
|
|
11
|
+
mqttxx-3.2.1.dist-info/top_level.txt,sha256=4SNRXgOpGCT6ThBzAb8zhUmTyZwnIracAIJWAdXXzw0,7
|
|
12
|
+
mqttxx-3.2.1.dist-info/RECORD,,
|
mqttxx/conventions.py
DELETED
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
# 约定式 RPC 管理器 - 去角色设计
|
|
2
|
-
|
|
3
|
-
from typing import Any, Optional
|
|
4
|
-
from loguru import logger
|
|
5
|
-
|
|
6
|
-
from .client import MQTTClient
|
|
7
|
-
from .config import RPCConfig
|
|
8
|
-
from .rpc import RPCManager, AuthCallback
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class ConventionalRPCManager(RPCManager):
|
|
12
|
-
"""约定式 RPC 管理器
|
|
13
|
-
|
|
14
|
-
设计原则:
|
|
15
|
-
- 约定优于配置
|
|
16
|
-
- 自动订阅本地 topic
|
|
17
|
-
- 自动注入 reply_to
|
|
18
|
-
|
|
19
|
-
核心功能:
|
|
20
|
-
1. 初始化时自动订阅 `my_topic`
|
|
21
|
-
2. 调用时自动将 `my_topic` 注入到 `reply_to`
|
|
22
|
-
|
|
23
|
-
适用场景:
|
|
24
|
-
- 边缘设备 ↔ 云端(edge/xxx ↔ cloud/xxx)
|
|
25
|
-
- 微服务之间(auth-service ↔ user-service)
|
|
26
|
-
- IoT 网关 ↔ 设备(gateway/001 ↔ device/123)
|
|
27
|
-
- 任何需要简化 RPC 调用的场景
|
|
28
|
-
|
|
29
|
-
示例:
|
|
30
|
-
# 边缘设备
|
|
31
|
-
rpc = ConventionalRPCManager(client, my_topic="edge/device_123")
|
|
32
|
-
|
|
33
|
-
@rpc.register("get_status")
|
|
34
|
-
async def get_status(params):
|
|
35
|
-
return {"status": "online"}
|
|
36
|
-
|
|
37
|
-
# 调用云端(自动注入 reply_to="edge/device_123")
|
|
38
|
-
config = await rpc.call("cloud/config-service", "get_config")
|
|
39
|
-
|
|
40
|
-
# 云端服务
|
|
41
|
-
rpc = ConventionalRPCManager(client, my_topic="cloud/config-service")
|
|
42
|
-
|
|
43
|
-
# 调用边缘设备(自动注入 reply_to="cloud/config-service")
|
|
44
|
-
status = await rpc.call("edge/device_123", "execute_command")
|
|
45
|
-
"""
|
|
46
|
-
|
|
47
|
-
def __init__(
|
|
48
|
-
self,
|
|
49
|
-
client: MQTTClient,
|
|
50
|
-
my_topic: str,
|
|
51
|
-
config: Optional[RPCConfig] = None,
|
|
52
|
-
auth_callback: Optional[AuthCallback] = None,
|
|
53
|
-
):
|
|
54
|
-
"""初始化约定式 RPC 管理器
|
|
55
|
-
|
|
56
|
-
Args:
|
|
57
|
-
client: MQTTClient 实例
|
|
58
|
-
my_topic: 本节点的 topic(自动订阅,自动注入到 reply_to)
|
|
59
|
-
config: RPC 配置(可选)
|
|
60
|
-
auth_callback: 权限检查回调(可选)
|
|
61
|
-
|
|
62
|
-
自动行为:
|
|
63
|
-
- 自动订阅 my_topic
|
|
64
|
-
- 自动绑定消息处理器
|
|
65
|
-
|
|
66
|
-
示例:
|
|
67
|
-
# 边缘设备
|
|
68
|
-
rpc = ConventionalRPCManager(client, my_topic="edge/device_123")
|
|
69
|
-
|
|
70
|
-
# 云端服务
|
|
71
|
-
rpc = ConventionalRPCManager(client, my_topic="cloud/server_001")
|
|
72
|
-
|
|
73
|
-
# 微服务
|
|
74
|
-
rpc = ConventionalRPCManager(client, my_topic="auth-service")
|
|
75
|
-
|
|
76
|
-
# 多层级
|
|
77
|
-
rpc = ConventionalRPCManager(client, my_topic="region/zone/device")
|
|
78
|
-
"""
|
|
79
|
-
super().__init__(client, config, auth_callback)
|
|
80
|
-
|
|
81
|
-
self._my_topic = my_topic
|
|
82
|
-
|
|
83
|
-
# 自动订阅
|
|
84
|
-
client.subscribe(my_topic, self.handle_rpc_message)
|
|
85
|
-
|
|
86
|
-
logger.info(f"ConventionalRPCManager 已初始化 - my_topic: {my_topic}")
|
|
87
|
-
|
|
88
|
-
async def call(
|
|
89
|
-
self,
|
|
90
|
-
topic: str,
|
|
91
|
-
method: str,
|
|
92
|
-
params: Any = None,
|
|
93
|
-
timeout: Optional[float] = None,
|
|
94
|
-
reply_to: Optional[str] = None,
|
|
95
|
-
) -> Any:
|
|
96
|
-
"""调用远程方法(自动注入 reply_to)
|
|
97
|
-
|
|
98
|
-
Args:
|
|
99
|
-
topic: 对方的 topic
|
|
100
|
-
method: 方法名
|
|
101
|
-
params: 参数(可选)
|
|
102
|
-
timeout: 超时时间(可选)
|
|
103
|
-
reply_to: 响应 topic(可选,默认使用 my_topic)
|
|
104
|
-
|
|
105
|
-
Returns:
|
|
106
|
-
方法返回值
|
|
107
|
-
|
|
108
|
-
Raises:
|
|
109
|
-
MQTTXError: 客户端未连接
|
|
110
|
-
RPCTimeoutError: 调用超时
|
|
111
|
-
RPCRemoteError: 远程执行失败
|
|
112
|
-
|
|
113
|
-
示例:
|
|
114
|
-
# 边缘设备调用云端
|
|
115
|
-
rpc = ConventionalRPCManager(client, my_topic="edge/device_123")
|
|
116
|
-
result = await rpc.call("cloud/server_001", "get_config")
|
|
117
|
-
# 等价于:await super().call("cloud/server_001", "get_config", reply_to="edge/device_123")
|
|
118
|
-
|
|
119
|
-
# 云端调用边缘设备
|
|
120
|
-
rpc = ConventionalRPCManager(client, my_topic="cloud/server_001")
|
|
121
|
-
result = await rpc.call("edge/device_123", "execute_command", params={"cmd": "restart"})
|
|
122
|
-
|
|
123
|
-
# 微服务调用
|
|
124
|
-
rpc = ConventionalRPCManager(client, my_topic="auth-service")
|
|
125
|
-
user = await rpc.call("user-service", "get_user", params={"id": 123})
|
|
126
|
-
"""
|
|
127
|
-
# 自动注入 reply_to
|
|
128
|
-
reply_to = reply_to or self._my_topic
|
|
129
|
-
|
|
130
|
-
return await super().call(
|
|
131
|
-
topic=topic,
|
|
132
|
-
method=method,
|
|
133
|
-
params=params,
|
|
134
|
-
timeout=timeout,
|
|
135
|
-
reply_to=reply_to,
|
|
136
|
-
)
|
|
137
|
-
|
|
138
|
-
@property
|
|
139
|
-
def my_topic(self) -> str:
|
|
140
|
-
"""获取当前节点的 topic
|
|
141
|
-
|
|
142
|
-
Returns:
|
|
143
|
-
当前节点订阅的 topic
|
|
144
|
-
"""
|
|
145
|
-
return self._my_topic
|
mqttxx-2.0.3.dist-info/METADATA
DELETED
|
@@ -1,490 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: mqttxx
|
|
3
|
-
Version: 2.0.3
|
|
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.3.dist-info/RECORD
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
mqttxx/__init__.py,sha256=9jhV_niQTYU2D4aD4AfVvWAAfbYf4o2vjwbwSMA_Rv4,1722
|
|
2
|
-
mqttxx/client.py,sha256=0wtw8fXW1buqnGbKOrg5sBJDORdSoHb3ErapiK3pkl4,16358
|
|
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=diXZg-H_MB6SiXll4Q9voh2ztC21_UAOgMKeLyBBJ4s,13597
|
|
8
|
-
mqttxx-2.0.3.dist-info/LICENSE,sha256=tfwCF8HPjAvvq_veljjH3_YL90X6ttkjTpRl3BGcXTg,1067
|
|
9
|
-
mqttxx-2.0.3.dist-info/METADATA,sha256=DuDs0jX9uoJD6dg_7r4uspORGjKsc_jN1WHRx27zZB8,11545
|
|
10
|
-
mqttxx-2.0.3.dist-info/WHEEL,sha256=Z4pYXqR_rTB7OWNDYFOm1qRk0RX6GFP2o8LgvP453Hk,91
|
|
11
|
-
mqttxx-2.0.3.dist-info/top_level.txt,sha256=4SNRXgOpGCT6ThBzAb8zhUmTyZwnIracAIJWAdXXzw0,7
|
|
12
|
-
mqttxx-2.0.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|