pyqttier 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.
- pyqttier-0.1.0/.github/workflows/mypy.yml +27 -0
- pyqttier-0.1.0/.github/workflows/python-tests.yml +30 -0
- pyqttier-0.1.0/.gitignore +151 -0
- pyqttier-0.1.0/LICENSE +21 -0
- pyqttier-0.1.0/PKG-INFO +88 -0
- pyqttier-0.1.0/README.md +77 -0
- pyqttier-0.1.0/examples/README.md +24 -0
- pyqttier-0.1.0/examples/mock_example.py +132 -0
- pyqttier-0.1.0/examples/usage_example.py +436 -0
- pyqttier-0.1.0/pyproject.toml +22 -0
- pyqttier-0.1.0/src/pyqttier/__init__.py +2 -0
- pyqttier-0.1.0/src/pyqttier/connection.py +200 -0
- pyqttier-0.1.0/src/pyqttier/interface.py +57 -0
- pyqttier-0.1.0/src/pyqttier/lwt.py +23 -0
- pyqttier-0.1.0/src/pyqttier/message.py +223 -0
- pyqttier-0.1.0/src/pyqttier/mock.py +144 -0
- pyqttier-0.1.0/src/pyqttier/py.typed +0 -0
- pyqttier-0.1.0/src/pyqttier/transport.py +27 -0
- pyqttier-0.1.0/tests/__init__.py +1 -0
- pyqttier-0.1.0/tests/test_pyqttier.py +346 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
name: Mypy Type Check
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ main ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ main ]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
mypy:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"]
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
18
|
+
uses: actions/setup-python@v5
|
|
19
|
+
with:
|
|
20
|
+
python-version: ${{ matrix.python-version }}
|
|
21
|
+
- name: Install dependencies
|
|
22
|
+
run: |
|
|
23
|
+
python -m pip install --upgrade pip
|
|
24
|
+
pip install paho-mqtt mypy
|
|
25
|
+
- name: Run mypy
|
|
26
|
+
run: |
|
|
27
|
+
PYTHONPATH=src mypy --check-untyped-defs ./src/
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
name: Python Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ main ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ main ]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"]
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
18
|
+
uses: actions/setup-python@v5
|
|
19
|
+
with:
|
|
20
|
+
python-version: ${{ matrix.python-version }}
|
|
21
|
+
- name: Install dependencies
|
|
22
|
+
run: |
|
|
23
|
+
python -m pip install --upgrade pip
|
|
24
|
+
pip install paho-mqtt
|
|
25
|
+
- name: Run unit tests
|
|
26
|
+
run: |
|
|
27
|
+
PYTHONPATH=src python -m unittest discover tests
|
|
28
|
+
- name: Run mock example
|
|
29
|
+
run: |
|
|
30
|
+
PYTHONPATH=src python examples/mock_example.py
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
*.manifest
|
|
31
|
+
*.spec
|
|
32
|
+
|
|
33
|
+
# Installer logs
|
|
34
|
+
pip-log.txt
|
|
35
|
+
pip-delete-this-directory.txt
|
|
36
|
+
|
|
37
|
+
# Unit test / coverage reports
|
|
38
|
+
htmlcov/
|
|
39
|
+
.tox/
|
|
40
|
+
.nox/
|
|
41
|
+
.coverage
|
|
42
|
+
.coverage.*
|
|
43
|
+
.cache
|
|
44
|
+
nosetests.xml
|
|
45
|
+
coverage.xml
|
|
46
|
+
*.cover
|
|
47
|
+
*.py,cover
|
|
48
|
+
.hypothesis/
|
|
49
|
+
.pytest_cache/
|
|
50
|
+
cover/
|
|
51
|
+
|
|
52
|
+
# Translations
|
|
53
|
+
*.mo
|
|
54
|
+
*.pot
|
|
55
|
+
|
|
56
|
+
# Django stuff:
|
|
57
|
+
*.log
|
|
58
|
+
local_settings.py
|
|
59
|
+
db.sqlite3
|
|
60
|
+
db.sqlite3-journal
|
|
61
|
+
|
|
62
|
+
# Flask stuff:
|
|
63
|
+
instance/
|
|
64
|
+
.webassets-cache
|
|
65
|
+
|
|
66
|
+
# Scrapy stuff:
|
|
67
|
+
.scrapy
|
|
68
|
+
|
|
69
|
+
# Sphinx documentation
|
|
70
|
+
docs/_build/
|
|
71
|
+
|
|
72
|
+
# PyBuilder
|
|
73
|
+
.pybuilder/
|
|
74
|
+
target/
|
|
75
|
+
|
|
76
|
+
# Jupyter Notebook
|
|
77
|
+
.ipynb_checkpoints
|
|
78
|
+
|
|
79
|
+
# IPython
|
|
80
|
+
profile_default/
|
|
81
|
+
ipython_config.py
|
|
82
|
+
|
|
83
|
+
# pyenv
|
|
84
|
+
.python-version
|
|
85
|
+
|
|
86
|
+
# pipenv
|
|
87
|
+
Pipfile.lock
|
|
88
|
+
|
|
89
|
+
# poetry
|
|
90
|
+
poetry.lock
|
|
91
|
+
|
|
92
|
+
# pdm
|
|
93
|
+
.pdm.toml
|
|
94
|
+
.pdm-python
|
|
95
|
+
.pdm-build/
|
|
96
|
+
|
|
97
|
+
# PEP 582
|
|
98
|
+
__pypackages__/
|
|
99
|
+
|
|
100
|
+
# Celery stuff
|
|
101
|
+
celerybeat-schedule
|
|
102
|
+
celerybeat.pid
|
|
103
|
+
|
|
104
|
+
# SageMath parsed files
|
|
105
|
+
*.sage.py
|
|
106
|
+
|
|
107
|
+
# Environments
|
|
108
|
+
.env
|
|
109
|
+
.venv
|
|
110
|
+
env/
|
|
111
|
+
venv/
|
|
112
|
+
ENV/
|
|
113
|
+
env.bak/
|
|
114
|
+
venv.bak/
|
|
115
|
+
|
|
116
|
+
# Spyder project settings
|
|
117
|
+
.spyderproject
|
|
118
|
+
.spyproject
|
|
119
|
+
|
|
120
|
+
# Rope project settings
|
|
121
|
+
.ropeproject
|
|
122
|
+
|
|
123
|
+
# mkdocs documentation
|
|
124
|
+
/site
|
|
125
|
+
|
|
126
|
+
# mypy
|
|
127
|
+
.mypy_cache/
|
|
128
|
+
.dmypy.json
|
|
129
|
+
dmypy.json
|
|
130
|
+
|
|
131
|
+
# Pyre type checker
|
|
132
|
+
.pyre/
|
|
133
|
+
|
|
134
|
+
# pytype static type analyzer
|
|
135
|
+
.pytype/
|
|
136
|
+
|
|
137
|
+
# Cython debug symbols
|
|
138
|
+
cython_debug/
|
|
139
|
+
|
|
140
|
+
# IDEs
|
|
141
|
+
.vscode/
|
|
142
|
+
.idea/
|
|
143
|
+
*.swp
|
|
144
|
+
*.swo
|
|
145
|
+
*~
|
|
146
|
+
.DS_Store
|
|
147
|
+
|
|
148
|
+
# Project specific
|
|
149
|
+
uv.lock
|
|
150
|
+
check_py37_compat.py
|
|
151
|
+
PYTHON37_COMPAT.md
|
pyqttier-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Jacob Brunson
|
|
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.
|
pyqttier-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyqttier
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Add your description here
|
|
5
|
+
Author-email: Jacob Brunson <github@jacobbrunson.com>
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Requires-Python: >=3.7
|
|
8
|
+
Requires-Dist: paho-mqtt>=2.1.0
|
|
9
|
+
Requires-Dist: pydantic>=2.5.3
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
|
|
12
|
+
# PyQTTier
|
|
13
|
+
|
|
14
|
+
A Python MQTT client library providing a clean, type-safe wrapper around paho-mqtt with support for MQTT 5.0 features.
|
|
15
|
+
|
|
16
|
+
## Features
|
|
17
|
+
|
|
18
|
+
- **MQTT 5.0 Support** - Full support for MQTT 5.0 protocol features including message properties, correlation data, and response topics
|
|
19
|
+
- **Type Safety** - Strongly typed with mypy-checked type hints for reliability
|
|
20
|
+
- **Multiple Transports** - Supports TCP, WebSocket, and Unix socket connections
|
|
21
|
+
- **Mock Implementation** - Built-in `MockConnection` for easy testing without a broker
|
|
22
|
+
- **Python 3.7+** - Compatible with Python 3.7 through 3.12
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pip install pyqttier
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Quick Start
|
|
31
|
+
|
|
32
|
+
### Basic Usage
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
from pyqttier.connection import Mqtt5Connection
|
|
36
|
+
from pyqttier.transport import MqttTransport, MqttTransportType
|
|
37
|
+
from pyqttier.message import Message
|
|
38
|
+
|
|
39
|
+
# Connect to broker
|
|
40
|
+
transport = MqttTransport(MqttTransportType.TCP, host="localhost", port=1883)
|
|
41
|
+
conn = Mqtt5Connection(transport=transport, client_id="my-client")
|
|
42
|
+
|
|
43
|
+
# Subscribe to a topic
|
|
44
|
+
def on_message(msg: Message):
|
|
45
|
+
print(f"Received: {msg.payload.decode()}")
|
|
46
|
+
|
|
47
|
+
conn.subscribe("sensors/temperature", callback=on_message)
|
|
48
|
+
|
|
49
|
+
# Publish a message
|
|
50
|
+
msg = Message(topic="sensors/temperature", payload=b"23.5", qos=1)
|
|
51
|
+
conn.publish(msg)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Testing with MockConnection
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
from pyqttier.mock import MockConnection
|
|
58
|
+
from pyqttier.message import Message
|
|
59
|
+
|
|
60
|
+
# Create mock connection for testing
|
|
61
|
+
conn = MockConnection()
|
|
62
|
+
|
|
63
|
+
# Publish and verify
|
|
64
|
+
conn.publish(Message(topic="test", payload=b"data", qos=1))
|
|
65
|
+
assert len(conn.published_messages) == 1
|
|
66
|
+
assert conn.published_messages[0].topic == "test"
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Examples
|
|
70
|
+
|
|
71
|
+
See the [examples/](examples/) directory for more detailed usage examples:
|
|
72
|
+
|
|
73
|
+
- `usage_example.py` - Real broker connections, wildcards, request-response patterns
|
|
74
|
+
- `mock_example.py` - Testing with MockConnection
|
|
75
|
+
|
|
76
|
+
## Development
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
# Type checking
|
|
80
|
+
uv run mypy --check-untyped-defs ./src/
|
|
81
|
+
|
|
82
|
+
# Unit tests
|
|
83
|
+
uv run pytest
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## License
|
|
87
|
+
|
|
88
|
+
See [LICENSE](LICENSE) file for details.
|
pyqttier-0.1.0/README.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# PyQTTier
|
|
2
|
+
|
|
3
|
+
A Python MQTT client library providing a clean, type-safe wrapper around paho-mqtt with support for MQTT 5.0 features.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **MQTT 5.0 Support** - Full support for MQTT 5.0 protocol features including message properties, correlation data, and response topics
|
|
8
|
+
- **Type Safety** - Strongly typed with mypy-checked type hints for reliability
|
|
9
|
+
- **Multiple Transports** - Supports TCP, WebSocket, and Unix socket connections
|
|
10
|
+
- **Mock Implementation** - Built-in `MockConnection` for easy testing without a broker
|
|
11
|
+
- **Python 3.7+** - Compatible with Python 3.7 through 3.12
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install pyqttier
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
### Basic Usage
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
from pyqttier.connection import Mqtt5Connection
|
|
25
|
+
from pyqttier.transport import MqttTransport, MqttTransportType
|
|
26
|
+
from pyqttier.message import Message
|
|
27
|
+
|
|
28
|
+
# Connect to broker
|
|
29
|
+
transport = MqttTransport(MqttTransportType.TCP, host="localhost", port=1883)
|
|
30
|
+
conn = Mqtt5Connection(transport=transport, client_id="my-client")
|
|
31
|
+
|
|
32
|
+
# Subscribe to a topic
|
|
33
|
+
def on_message(msg: Message):
|
|
34
|
+
print(f"Received: {msg.payload.decode()}")
|
|
35
|
+
|
|
36
|
+
conn.subscribe("sensors/temperature", callback=on_message)
|
|
37
|
+
|
|
38
|
+
# Publish a message
|
|
39
|
+
msg = Message(topic="sensors/temperature", payload=b"23.5", qos=1)
|
|
40
|
+
conn.publish(msg)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Testing with MockConnection
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
from pyqttier.mock import MockConnection
|
|
47
|
+
from pyqttier.message import Message
|
|
48
|
+
|
|
49
|
+
# Create mock connection for testing
|
|
50
|
+
conn = MockConnection()
|
|
51
|
+
|
|
52
|
+
# Publish and verify
|
|
53
|
+
conn.publish(Message(topic="test", payload=b"data", qos=1))
|
|
54
|
+
assert len(conn.published_messages) == 1
|
|
55
|
+
assert conn.published_messages[0].topic == "test"
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Examples
|
|
59
|
+
|
|
60
|
+
See the [examples/](examples/) directory for more detailed usage examples:
|
|
61
|
+
|
|
62
|
+
- `usage_example.py` - Real broker connections, wildcards, request-response patterns
|
|
63
|
+
- `mock_example.py` - Testing with MockConnection
|
|
64
|
+
|
|
65
|
+
## Development
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
# Type checking
|
|
69
|
+
uv run mypy --check-untyped-defs ./src/
|
|
70
|
+
|
|
71
|
+
# Unit tests
|
|
72
|
+
uv run pytest
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## License
|
|
76
|
+
|
|
77
|
+
See [LICENSE](LICENSE) file for details.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# PyQTTier Examples
|
|
2
|
+
|
|
3
|
+
This directory contains concise examples for using PyQTTier.
|
|
4
|
+
|
|
5
|
+
## Usage Example (`usage_example.py`)
|
|
6
|
+
|
|
7
|
+
Demonstrates connecting to an MQTT broker, publishing/subscribing, retained messages, wildcards, and request-response. Requires a broker on `localhost:1883`:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
docker run -p 1883:1883 eclipse-mosquitto
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Run the examples:
|
|
14
|
+
```bash
|
|
15
|
+
uv run examples/usage_example.py
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Mock Connection Example (`mock_example.py`)
|
|
19
|
+
|
|
20
|
+
Shows how to use `MockConnection` for testing without a real broker. Run with:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
uv run examples/mock_example.py
|
|
24
|
+
```
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Example demonstrating how to use MockConnection for testing MQTT-based applications.
|
|
4
|
+
|
|
5
|
+
This example shows:
|
|
6
|
+
1. Publishing messages
|
|
7
|
+
2. Subscribing to topics with callbacks
|
|
8
|
+
3. Using global message callbacks
|
|
9
|
+
4. Topic wildcard matching
|
|
10
|
+
5. Simulating received messages
|
|
11
|
+
6. Verifying published messages
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import sys
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
# Add src to path for standalone execution
|
|
18
|
+
sys.path.insert(0, str(Path(__file__).parent.parent / 'src'))
|
|
19
|
+
|
|
20
|
+
from pyqttier.mock import MockConnection
|
|
21
|
+
from pyqttier.message import Message
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def example_basic_publish_subscribe():
|
|
25
|
+
"""Example 1: Basic publish and subscribe with callbacks."""
|
|
26
|
+
print("=" * 60)
|
|
27
|
+
print("Example 1: Basic Publish and Subscribe")
|
|
28
|
+
print("=" * 60)
|
|
29
|
+
|
|
30
|
+
# Create a mock connection
|
|
31
|
+
conn = MockConnection().set_connected(True)
|
|
32
|
+
|
|
33
|
+
# Publish a message
|
|
34
|
+
msg = Message(
|
|
35
|
+
topic="sensors/temperature",
|
|
36
|
+
payload=b"23.5",
|
|
37
|
+
qos=1,
|
|
38
|
+
retain=False
|
|
39
|
+
)
|
|
40
|
+
fut = conn.publish(msg)
|
|
41
|
+
print(f"📤 Published temperature reading: {msg.payload.decode()}°C")
|
|
42
|
+
|
|
43
|
+
assert len(conn.published_messages) == 1, "Should have one published message"
|
|
44
|
+
|
|
45
|
+
# Verify the message was received
|
|
46
|
+
print(f"✓ Published {len(conn.published_messages)} message.\n")
|
|
47
|
+
|
|
48
|
+
def example_basic_subscribe():
|
|
49
|
+
"""Example 2: Basic subscribe with topic filter and callback."""
|
|
50
|
+
print("=" * 60)
|
|
51
|
+
print("Example 2: Basic Subscribe with Callback")
|
|
52
|
+
print("=" * 60)
|
|
53
|
+
|
|
54
|
+
# Create a mock connection
|
|
55
|
+
conn = MockConnection().set_connected(True)
|
|
56
|
+
|
|
57
|
+
received_count = 0
|
|
58
|
+
|
|
59
|
+
# Define a callback for received messages
|
|
60
|
+
def message_callback(msg: Message):
|
|
61
|
+
nonlocal received_count
|
|
62
|
+
print(f"📥 Received message on topic '{msg.topic}': {msg.payload.decode()}")
|
|
63
|
+
received_count += 1
|
|
64
|
+
|
|
65
|
+
# Subscribe to a topic
|
|
66
|
+
sub_id = conn.subscribe("sensors/+", message_callback)
|
|
67
|
+
print(f"🔔 Subscribed to 'sensors/+' with subscription ID {sub_id}")
|
|
68
|
+
|
|
69
|
+
# Simulate receiving a message
|
|
70
|
+
incoming_msg = Message(
|
|
71
|
+
topic="sensors/humidity",
|
|
72
|
+
payload=b"45%",
|
|
73
|
+
qos=1,
|
|
74
|
+
retain=False
|
|
75
|
+
)
|
|
76
|
+
conn.simulate_message(incoming_msg)
|
|
77
|
+
|
|
78
|
+
assert received_count == 1, "Should have received one message"
|
|
79
|
+
print(f"✓ Received {received_count} message(s) via subscription.\n")
|
|
80
|
+
|
|
81
|
+
def example_request_response():
|
|
82
|
+
"""Example 3: Request-Response pattern using response topics."""
|
|
83
|
+
print("=" * 60)
|
|
84
|
+
print("Example 3: Request-Response Pattern")
|
|
85
|
+
print("=" * 60)
|
|
86
|
+
|
|
87
|
+
# Create a mock connection
|
|
88
|
+
conn = MockConnection().set_connected(True)
|
|
89
|
+
|
|
90
|
+
responses_received = 0
|
|
91
|
+
|
|
92
|
+
# Define a callback for responses
|
|
93
|
+
def response_callback(msg: Message):
|
|
94
|
+
nonlocal responses_received
|
|
95
|
+
responses_received += 1
|
|
96
|
+
print(f"📥 Received response on topic '{msg.topic}': {msg.payload.decode()}")
|
|
97
|
+
|
|
98
|
+
# Subscribe to the response topic
|
|
99
|
+
response_topic = "responses/device123"
|
|
100
|
+
conn.subscribe(response_topic, response_callback)
|
|
101
|
+
print(f"🔔 Subscribed to response topic '{response_topic}'")
|
|
102
|
+
|
|
103
|
+
# Simulate sending a request
|
|
104
|
+
request_msg = Message(
|
|
105
|
+
topic="requests/device123",
|
|
106
|
+
payload=b"GetStatus",
|
|
107
|
+
qos=1,
|
|
108
|
+
retain=False,
|
|
109
|
+
response_topic=response_topic
|
|
110
|
+
)
|
|
111
|
+
conn.publish(request_msg)
|
|
112
|
+
print(f"📤 Sent request: {request_msg.payload.decode()}")
|
|
113
|
+
|
|
114
|
+
assert len(conn.published_messages) == 1, "Should have one published request message"
|
|
115
|
+
|
|
116
|
+
# Simulate receiving a response
|
|
117
|
+
response_msg = Message(
|
|
118
|
+
topic=response_topic,
|
|
119
|
+
payload=b"Status: OK",
|
|
120
|
+
qos=1,
|
|
121
|
+
retain=False
|
|
122
|
+
)
|
|
123
|
+
conn.simulate_message(response_msg)
|
|
124
|
+
|
|
125
|
+
assert responses_received == 1, "Should have received one response"
|
|
126
|
+
|
|
127
|
+
print("✓ Completed request-response simulation.\n")
|
|
128
|
+
|
|
129
|
+
if __name__ == "__main__":
|
|
130
|
+
example_basic_publish_subscribe()
|
|
131
|
+
example_basic_subscribe()
|
|
132
|
+
example_request_response()
|