Python SDK for CloudPostOffice — messaging for AI agents, apps, and devices
Project description
cloudpostoffice
Python SDK for CloudPostOffice — super-simple messaging for AI agents, apps, and devices.
Install
pip install cloudpostoffice
Or from source:
git clone https://github.com/CloudPostOffice/python
cd python
pip install .
Requirements: Python 3.10+
Quick start
Each app or device needs a unique device ID and secret key. Create them from your dashboard.
Direct Messages
Send a message directly from one device to another.
import asyncio
import cloudpostoffice as cpo
cpo.configure(base_url="https://cloudpostoffice.com")
d1 = cpo.device("device-1-id", "device-1-secret")
d2 = cpo.device("device-2-id", "device-2-secret")
async def main():
def on_message(msg):
print(msg)
# msg is { "from": "device-1-id", "msg": "hello", "ts": 1234567890000 }
# device-2 listens for incoming messages
await d2.listen(on_message)
# device-1 sends to device-2
await d1.send(to="device-2-id", msg="hello")
d1.disconnect()
asyncio.run(main())
Pub / Sub
Any device can publish or subscribe to a named topic within the same project.
import asyncio
import cloudpostoffice as cpo
cpo.configure(base_url="https://cloudpostoffice.com")
async def main():
publisher = cpo.device("device-1-id", "device-1-secret")
subscriber = cpo.device("device-2-id", "device-2-secret")
def on_news(topic, msg):
print(topic, "->", msg)
# subscriber listens first — always subscribe before publishing
await subscriber.subscribe("news", on_news)
print("Subscribed, waiting for messages...")
# publisher sends
await publisher.publish("news", "CloudPostOffice is alive!")
publisher.disconnect()
# keep the event loop alive to receive messages
await asyncio.Event().wait()
asyncio.run(main())
Decorator-based client (direct MQTT, no auth)
CloudPostOfficeClient connects directly to any MQTT broker without the CloudPostOffice authentication layer. Useful for prototyping or when you already have broker credentials.
import asyncio
from cloudpostoffice import CloudPostOfficeClient
cpo = CloudPostOfficeClient("broker.emqx.io")
@cpo.on("cloudpostoffice/inbox/user123")
def on_inbox(payload: str) -> None:
print(f"[inbox] {payload}")
@cpo.on("cloudpostoffice/alerts/#")
async def on_alert(payload: str) -> None:
print(f"[alert] {payload}")
asyncio.run(cpo.listen())
Both def and async def handlers are accepted transparently.
API
cpo.configure(*, base_url)
Override the API base URL. Call before creating any devices.
cpo.configure(base_url="https://cloudpostoffice.com")
cpo.device(device_id, device_secret) → Device
Creates a device handle. Authenticates and connects to the MQTT broker automatically on first use.
d = cpo.device("my-device-id", "my-secret")
await device.send(*, to, msg)
Sends a direct message to another device on the same project.
| Param | Type | Description |
|---|---|---|
to |
str |
Target device ID |
msg |
any |
Payload — any JSON-serialisable value |
await d1.send(to="device-2-id", msg="hello")
await device.listen(callback)
Registers a callback for messages addressed to this device.
Callback signature: fn(message) — message is {"from", "msg", "ts"}.
def on_message(msg):
print(msg["from"], "says:", msg["msg"])
await d.listen(on_message)
await device.publish(topic_name, message)
Publishes a message to a named topic.
- Topic names must not contain
/,+,#, or--.
await d.publish("alerts", {"level": "warn", "text": "High temp"})
await device.subscribe(topic_name, callback)
Subscribes to a named topic.
Callback signature: fn(topic_name, message).
def on_alert(topic, msg):
print(topic, msg)
await d.subscribe("alerts", on_alert)
device.disconnect()
Signals the background connection task to stop.
d.disconnect()
CloudPostOfficeClient(broker_url, port=1883)
Lightweight decorator-based MQTT client for direct broker connections.
from cloudpostoffice import CloudPostOfficeClient
cpo = CloudPostOfficeClient("broker.emqx.io")
@cpo.on("topic/#")
async def handler(payload: str) -> None:
print(payload)
asyncio.run(cpo.listen())
Running the tests
1. Fill in credentials
Edit .env.test:
CPO_BASE_URL=https://cloudpostoffice.com
CPO_TEST_DEVICE_1_ID=your-device-1-id
CPO_TEST_DEVICE_1_SECRET=your-device-1-secret
CPO_TEST_DEVICE_2_ID=your-device-2-id
CPO_TEST_DEVICE_2_SECRET=your-device-2-secret
Create two devices from your dashboard.
2. Install dependencies
pip install -r requirements.txt
3. Run all tests
python tests/run_all.py
Expected output:
CloudPostOffice Python SDK — Integration Tests
Base URL : https://cloudpostoffice.com
Device 1 : your-device-1-id
Device 2 : your-device-2-id
Pub / Sub (topic bus)
---------------------
PASS pub_test.py — published successfully
PASS sub_test.py — received message on correct topic
Device send / listen
--------------------
PASS device1.py — message sent
PASS device2.py — message received
Security — unauthorized cross-project publish
---------------------------------------------
PASS unauth_test.py — broker rejected unauthorized publish
Passed: 5 Failed: 0
Unit tests (no credentials needed)
python tests/test_client_id_split.py
Notes
- Authentication tokens are valid for 7 days. The SDK reconnects and refreshes automatically when a token expires.
- Topic names must not contain
/,+,#, or--. - Two devices cannot share the same device ID simultaneously within a project.
- On Windows, the SDK automatically switches to
WindowsSelectorEventLoopPolicysoaiomqttworks correctly (paho-mqtt requiresadd_reader/add_writerwhich are not available on the default ProactorEventLoop). - Requires Python 3.10+.
Links
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file cloudpostoffice-1.2.0.tar.gz.
File metadata
- Download URL: cloudpostoffice-1.2.0.tar.gz
- Upload date:
- Size: 14.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d04da078384cc41072a560e21e3a4fbbe8fc37dab03883bc5cf15a6d6ec3deec
|
|
| MD5 |
ae5ddfdef9c920dc4acc4f8980990455
|
|
| BLAKE2b-256 |
54b3f94b64d8d8a0a3529c43b81c155e121b99c33576c4e5d2f7128e0839ae12
|
File details
Details for the file cloudpostoffice-1.2.0-py3-none-any.whl.
File metadata
- Download URL: cloudpostoffice-1.2.0-py3-none-any.whl
- Upload date:
- Size: 11.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
268016b34d0effa3b9b22cd3fbcc1484f5f49779f1cc879fa82075b5dcaca32e
|
|
| MD5 |
026e299fd0bfe5b76ba6c7438e480167
|
|
| BLAKE2b-256 |
014c407b924f4caad7ba04401288d913dd4316fd1d6317848b3d1140ad1df71d
|