Skip to main content

Event-driven MQTT bridge for real-world reinforcement learning

Project description

mqtt-rl-bridge — Event-Driven MQTT Bridge for Real-World Reinforcement Learning

PyPI Python License: MIT Tests

A lightweight, framework-agnostic package that connects any MQTT-enabled sensor or robot to a reinforcement learning (RL) agent — in real time.

Use it to train RL policies on physical systems:

  • HVAC controllers
  • Robotic arms
  • Drones
  • Smart agriculture
  • Industrial IoT
  • Home automation

No Gymnasium dependency. No hardcoded examples. Just three abstract methods to implement.


Features

  • Zero RL framework lock-in — works with Stable-Baselines3, RLlib, CleanRL, or your custom loop
  • Event-driven MQTT — real-time sensor updates via paho-mqtt
  • Thread-safe message queue — no race conditions
  • Timeout fallbacks — robust to network glitches
  • Minimal API — subclass MQTTRLEnv and implement 3 methods
  • No hardware in tests — full unit-testable with mocks

Installation

pip install mqtt-rl-bridge

Optional extras:

pip install "mqtt-rl-bridge[gym]"      # for Gymnasium space support
pip install "mqtt-rl-bridge[dev]"      # for testing + linting

Quick Start

1. Create Your Environment

# my_env.py
import numpy as np
from mqtt_rl_bridge import MQTTRLEnv

class TemperatureControlEnv(MQTTRLEnv):
    def __init__(self, **kwargs):
        super().__init__(
            sensor_topic="home/sensors",
            action_topic="home/hvac",
            timeout=8.0,
            step_delay=2.0,  # wait for HVAC to respond
            **kwargs
        )

    def _extract_observation(self, raw: dict) -> np.ndarray:
        temp = float(raw.get("temperature", 22.0))
        target = float(raw.get("setpoint", 22.0))
        return np.array([temp, target], dtype=np.float32)

    def _encode_action(self, action: int) -> dict:
        modes = ["cool", "off", "heat"]
        return {"mode": modes[action]}

    def _compute_reward(self, obs, action, next_obs) -> float:
        temp, target = next_obs
        error = abs(temp - target)
        energy = 0.1 if action != 1 else 0.0
        return -error - energy

    def _is_done(self, obs, action) -> tuple[bool, bool]:
        temp, target = obs
        return abs(temp - target) < 0.5, False

2. Use in Any RL Loop

Custom Training Loop

from my_env import TemperatureControlEnv
import numpy as np

env = TemperatureControlEnv(broker_host="192.168.1.100")
obs = env.reset()

for _ in range(200):
    action = np.random.randint(0, 3)
    obs, reward, done, truncated, info = env.step(action)
    print(f"Temp: {obs[0]:.1f}°C → Reward: {reward:.3f}")
    if done or truncated:
        obs = env.reset()

env.close()

Stable-Baselines3 (PPO)

from my_env import TemperatureControlEnv
from stable_baselines3 import PPO

env = TemperatureControlEnv(broker_host="192.168.1.100")
model = PPO("MlpPolicy", env, verbose=1, learning_rate=3e-4)
model.learn(total_timesteps=10_000)
model.save("temp_control_ppo")
env.close()

Ray RLlib

import ray
from ray.rllib.algorithms.ppo import PPOConfig
from my_env import TemperatureControlEnv

ray.init()
config = PPOConfig().environment(
    TemperatureControlEnv,
    env_config={"broker_host": "192.168.1.100"}
)
algo = config.build()
algo.train()
algo.save("rllib_checkpoint")
algo.stop()

API Reference

MQTTRLEnv — Abstract Base Class

class MQTTRLEnv(ABC):
    def __init__(
        self,
        broker_host: str = "127.0.0.1",
        sensor_topic: str = "sensors",
        action_topic: str = "actions",
        timeout: float = 5.0,
        step_delay: float = 0.0,
    )
Parameter Description
broker_host MQTT broker IP or hostname
sensor_topic Topic where sensor data is published
action_topic Topic where agent sends actions
timeout Max wait time for sensor message (seconds)
step_delay Sleep after sending action (for hardware latency)

Methods You Must Implement

Method Signature Purpose
_extract_observation raw: dict → np.ndarray Parse JSON payload into observation
_encode_action action: Any → dict Convert action to MQTT JSON
_compute_reward obs, action, next_obs → float Reward function

Optional Overrides

Method Default Use Case
_is_done (False, False) Terminate episode
reset_hook pass Send reset command on reset()

Core Methods (Gymnasium-Compatible)

obs = env.reset()  np.ndarray
obs, reward, terminated, truncated, info = env.step(action)
env.close()

Works with any RL library that expects this signature.


Testing Without Hardware

from unittest.mock import MagicMock
from mqtt_rl_bridge import MQTTRLEnv

class MockEnv(MQTTRLEnv):
    def __init__(self):
        super().__init__(broker_host="localhost")
        self.broker.get_message = MagicMock(
            return_value={"temp": 25.0, "humidity": 60.0}
        )

    def _extract_observation(self, raw): return np.array([raw["temp"]])
    def _encode_action(self, a): return {"fan": "on" if a == 1 else "off"}
    def _compute_reward(self, o, a, no): return -abs(no[0] - 22.0)

Run unit tests without a broker.


Project Structure

my-iot-rl-project/
├── envs/
│   └── temperature_env.py
├── models/
├── train_ppo.py
├── requirements.txt
└── tests/

requirements.txt:

mqtt-rl-bridge
stable-baselines3
gymnasium

Contributing

  1. Fork the repo
  2. Create a branch: git checkout -b feature/my-cool-env
  3. Install dev deps: pip install "mqtt-rl-bridge[dev]"
  4. Write tests in tests/
  5. Run: pytest
  6. Submit PR

Citation

@software{mqtt-rl-bridge-2025,
  author = {Alex Kagozi},
  title = {mqtt-rl-bridge: Event-Driven MQTT Bridge for Real-World RL},
  year = {2025},
  publisher = {GitHub},
  url = {https://github.com/kagozi/mqtt_rl_bridge}
}

License

MIT License — free for commercial and research use.


Made with Real-World RL in Mind

No toy environments. No simulation gaps.
Train your agent directly on the physical world.


Ready to connect your robot?
Just pip install mqtt-rl-bridge and subclass MQTTRLEnv.


Made with love for real hardware and real learning.

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

mqtt_rl_bridge-0.1.0.tar.gz (7.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mqtt_rl_bridge-0.1.0-py3-none-any.whl (8.1 kB view details)

Uploaded Python 3

File details

Details for the file mqtt_rl_bridge-0.1.0.tar.gz.

File metadata

  • Download URL: mqtt_rl_bridge-0.1.0.tar.gz
  • Upload date:
  • Size: 7.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.2

File hashes

Hashes for mqtt_rl_bridge-0.1.0.tar.gz
Algorithm Hash digest
SHA256 d4199e3db2dfbd3a47c9a0704565bda5c11a434d7aed6ab5416ed2cdf2588632
MD5 b2224108f6c93e667c72b764a024bdef
BLAKE2b-256 bbcf89c964f4cc5d778e787055ac6838a1c67f33e46f89bcd05cf2194f3bafa0

See more details on using hashes here.

File details

Details for the file mqtt_rl_bridge-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: mqtt_rl_bridge-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 8.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.2

File hashes

Hashes for mqtt_rl_bridge-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 8fc97ef98675d3d86184faf3919c5fac12420068d679b40a24db98124c5aee04
MD5 c86d42a468771fb71252b3d5511efd4c
BLAKE2b-256 2bdd1ace58e89bb9cadfdc98108a4f8aaf881a1ef40bf9572e9bddd41b4fac12

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page