Examples

Basic App

import logging
from gourd import Gourd

logging.basicConfig()

app = Gourd(
    app_name='my_app',
    mqtt_host='broker.local',
    username='mqtt',
    password='secret',
)

@app.subscribe('#')
def print_all(message):
    print(f'{message.topic}: {message.payload}')

if __name__ == '__main__':
    app.run_forever()

Multiple Topics, Separate Handlers

@app.subscribe('lights/#')
def handle_lights(message):
    app.log.info(f'Light update: {message.topic} = {message.payload}')

@app.subscribe('sensors/#')
def handle_sensors(message):
    app.log.info(f'Sensor reading: {message.topic} = {message.payload}')

One Handler for Multiple Topics

Stack @app.subscribe decorators to call the same function for multiple topics:

@app.subscribe('home/kitchen/light')
@app.subscribe('home/living_room/light')
def handle_light(message):
    app.log.info(f'{message.topic} is now {message.payload}')

Handling JSON Payloads

message.json is a dict when the payload parses successfully, or an empty dict {} when it doesn't. An empty dict is falsy, so if message.json is a reliable guard:

@app.subscribe('sensors/+/reading')
def handle_reading(message):
    if message.json:
        temp = message.json.get('celsius')
        humidity = message.json.get('humidity')
        app.log.info(f'temp={temp}°C humidity={humidity}%')
    else:
        app.log.warning(f'Unexpected payload on {message.topic}: {message.payload!r}')

Publishing Messages

# Basic publish
app.publish('lights/kitchen', 'ON')

# With explicit QoS
app.publish('alerts/critical', 'FIRE', qos=2)

# Retained message (persisted by broker for new subscribers)
app.publish('devices/thermostat/setpoint', '72', retain=True)

# Delete a retained message (empty payload + retain=True)
app.publish('devices/thermostat/setpoint', None, retain=True)

Background Threads

Use @app.thread() to register functions that gourd will run in background threads when the app starts:

import time
import logging
from gourd import Gourd

logging.basicConfig()

app = Gourd(app_name='sensor_publisher', mqtt_host='broker.local')

@app.subscribe('commands/#')
def handle_command(message):
    app.log.info(f'Command received: {message.topic} = {message.payload}')

@app.thread()
def poll_sensor():
    while True:
        value = read_sensor()  # your sensor reading function
        app.publish('sensors/temperature', str(value))
        time.sleep(10)

if __name__ == '__main__':
    app.run_forever()

Pass arguments that should be forwarded to the function:

@app.thread('sensors/temperature', interval=10)
def poll_sensor(topic, interval=5):
    while True:
        app.publish(topic, str(read_sensor()))
        time.sleep(interval)

If you need the main thread for your own work instead, use loop_start() / loop_stop() directly — see the API reference.


Disable Automatic Features

For a minimal footprint app with no status topic or MQTT logging:

app = Gourd(
    app_name='minimal_app',
    mqtt_host='broker.local',
    log_mqtt=False,       # don't publish logs to MQTT
    status_enabled=False, # don't publish online/offline status
)

CLI with a Non-Standard Module Location

If your app module is not in the current directory, use --sys-path to add it:

gourd --sys-path /opt/myapps mymodule:app

You can pass --sys-path multiple times to add several paths. Run gourd --help to see all available CLI options.