You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
picoclaw-xmp/README.md

98 lines
3.6 KiB

# F3 Picoclaw XMPP
An XMPP ↔ PicoClaw bridge over MQTT, built as two independent long-running
services that talk to each other over localhost HTTP. Each service owns exactly
one persistent connection (one XMPP, one MQTT), so there is no connection
contention between them.
## How it works
```
XMPP user <─> [xmpp_service.py] ──HTTP /publish──> [mqtt_service.py] <─> MQTT broker
^ XMPP conn :19080 MQTT conn :19081
└───────────────── HTTP /send ◄──────────────────┘
```
- **`src/xmpp_service.py`** (port `19080`) — one XMPP connection (receive and
send). Exposes `POST /send` `{jid, text}` to send an outgoing XMPP message
(chat, or groupchat when the JID is a known room). Every incoming XMPP
message is forwarded via `POST /publish` to the MQTT service.
- **`src/mqtt_service.py`** (port `19081`) — one MQTT connection subscribed to
`{prefix}/{agent_id}/+/response`. Exposes `POST /publish` `{jid, text}` to
publish `{"text": ...}` to `{prefix}/{agent_id}/{jid}/request`. Every MQTT
response is forwarded via `POST /send` to the XMPP service.
The `{jid}` segment is the bare JID of the XMPP sender, so PicoClaw keeps an
independent conversation per sender. PicoClaw's streamed reply chunks arrive as
separate MQTT messages and are each forwarded as their own XMPP message.
If a service cannot reach the other over HTTP, it logs the error and continues.
## Requirements
- Python >= 3.10
- [uv](https://docs.astral.sh/uv/) package manager
- An MQTT broker (e.g. Mosquitto, EMQX)
- A running [PicoClaw](https://github.com/sipeed/picoclaw) gateway with the MQTT
channel enabled (`channel_list.mqtt` with a matching `agent_id` and
`topic_prefix`)
## Setup
```bash
# Install dependencies
uv sync
# Configure environment
cp .env.example .env
# Edit .env with your XMPP credentials and MQTT settings
```
## Environment Variables
| Variable | Description |
|---|---|
| `XMPP_USERNAME` | Full JID for the XMPP bot account |
| `XMPP_PASSWORD` | XMPP account password |
| `XMPP_ROOMS` | Comma-separated list of MUC rooms to join |
| `MQTT_BROKER` | MQTT broker URL, e.g. `tcp://host:1883` or `ssl://host:8883` |
| `MQTT_USERNAME` | Optional broker username |
| `MQTT_PASSWORD` | Optional broker password |
| `MQTT_TOPIC_PREFIX` | Topic namespace prefix (default `/picoclaw`) |
| `MQTT_AGENT_ID` | PicoClaw agent id used in the topic path (default `assistant`) |
| `MQTT_CONN_CLIENT_ID` | Broker-level client id for MQTT connections |
| `MQTT_QOS` | MQTT QoS for publish/subscribe: `0`, `1`, or `2` (default `0`) |
| `DEBUG` | Logging level: `debug`, `info`, `warn`, `error`, `critical` |
The service ports are hardcoded: XMPP service on `127.0.0.1:19080`, MQTT
service on `127.0.0.1:19081`.
## Usage
Run both services (e.g. two terminals, or a process manager like systemd):
```bash
# Terminal 1 - XMPP side
uv run python -m src.xmpp_service
# Terminal 2 - MQTT side
uv run python -m src.mqtt_service
```
## Project Structure
```
src/
├── xmpp_service.py # XMPP connection + HTTP :19080 (incoming -> MQTT, /send)
├── mqtt_service.py # MQTT connection + HTTP :19081 (responses -> XMPP, /publish)
├── xmpp.py # XMPP bot client (slixmpp)
├── picoclaw.py # PicoClaw MQTT client (paho-mqtt)
└── log.py # Logging configuration
```
## Tech Stack
- Python 3.14 / asyncio
- [slixmpp](https://github.com/poezio/slixmpp) for XMPP
- [paho-mqtt](https://github.com/eclipse/paho.mqtt.python) for MQTT
- [aiohttp](https://docs.aiohttp.org/) for the HTTP service APIs