Production-ready MQTT client for OpenClaw automation. Universal - not bound to specific systems.
Universal MQTT Client for OpenClaw with Node.js/mqtt.js. Connect to any MQTT broker to subscribe to topics, publish messages, and react to state changes. The client automatically handles reconnection, supports wildcards for flexible topic patterns, and can trigger alerts when values cross thresholds (e.g., battery below 10% or temperature above 30°C). Use this skill to integrate OpenClaw with smart home systems such as ioBroker, Home Assistant, Zigbee2MQTT, Shelly devices, other OpenClaw instances (to communicate between them), or any other MQTT-based system.
bash
npm install mqtt
javascript
const { MqttClient } = require('./scripts/mqtt-client.js');
const client = new MqttClient({
broker: process.env.MQTT_BROKER,
username: process.env.MQTT_USERNAME,
password: process.env.MQTT_PASSWORD
});
client.on('message', (topic, payload) => console.log(`${topic}: ${payload}`));
await client.connect();
await client.subscribe('home/#');
VariableDefaultDescriptionMQTT_BROKER``localhostBroker URL (with or without protocol)MQTT_BROKER_PORT``1883Broker portMQTT_USERNAME-Username (optional)MQTT_PASSWORD-Password (optional)MQTT_CLIENT_IDauto-generatedClient ID (max 23 chars)MQTT_SUBSCRIBE_TOPIC``#Default topic to subscribeMQTT_KEEPALIVE``60Keep-alive interval (seconds)MQTT_RECONNECT_PERIOD``5000Reconnect interval (ms)
When first used, the skill automatically creates config in ~/.openclaw/openclaw.json:
json
{
"skills": {
"entries": {
"mqtt-client": {
"enabled": true,
"env": {
"MQTT_BROKER": "localhost",
"MQTT_BROKER_PORT": "1883"
}
}
}
}
}
⚠️ Existing values are NOT overwritten.
javascript
const client = new MqttClient({
broker: 'mqtt://localhost:1883',
reconnectPeriod: 5000,
connectTimeout: 30000,
maxReconnectAttempts: 10
});
javascript
const client = new MqttClient({
broker: 'mqtt://localhost:1883',
keepalive: 60 // seconds
});
javascript
const client = new MqttClient({
will: {
topic: 'openclaw/status',
payload: JSON.stringify({ status: 'offline' }),
qos: 1,
retain: true
}
});
javascript
await client.disconnect(); // with timeout
await client.disconnect(5000);
javascript
// Single topic
await client.subscribe('home/bridge/info');
// Multiple topics
await client.subscribe(['home/bridge/info', 'home/bridge/state']);
WildcardDescriptionExample+Single levelhome/+/temperature``#Multi levelhome/sensors/#
LevelNameDescription0At most onceFire and forget1At least onceAcknowledged delivery2Exactly onceHandshake protocoljavascript
await client.subscribe('topic', { qos: 2 });
javascript
await client.subscribe('new/topic');
await client.unsubscribe('old/topic');
await client.unsubscribeAll();
javascript
// Simple
await client.publish('home/lights/set', 'ON');
// With options
await client.publish('home/lights/set', 'ON', { qos: 1, retain: true });
// As JSON (auto-stringified)
await client.publish('home/lights/set', { state: 'ON', brightness: 255 });
javascript
// Set retained
await client.publish('home/announcement', 'Hello', { retain: true });
// Delete retained (empty payload)
await client.publish('home/announcement', '', { retain: true });
Automatic parsing - payload is already an object for JSON messages:
javascript
client.on('message', (topic, payload) => {
if (typeof payload === 'object') {
console.log('JSON:', payload.key);
}
});
React to value changes with triggers:
javascript
// Battery low trigger
client.addTrigger('battery-low', {
topic: 'home/+/battery',
path: 'value',
operator: '<',
threshold: 10,
valueType: 'number',
cooldown: 60000,
callback: (event) => console.log('⚠️ Low battery:', event.value)
});
// Temperature high trigger
client.addTrigger('temp-high', {
topic: 'home/sensors/+/temperature',
path: 'value',
operator: '>',
threshold: 30,
valueType: 'number',
callback: (event) => console.log('🔥 Hot:', event.value)
});
OperatorDescription>Greater than<Less than>=Greater or equal<=Less or equal==Equal!=Not equalcontainsString containsstartsWithString starts with
javascript
const health = client.getHealth();
// { connected, reconnecting, lastConnected, messagesReceived, latency }
javascript
const state = client.getState();
// { status, broker, subscriptions }
javascript
// Last messages for topic
const history = client.getMessageHistory('home/+/temperature');
// Last message
const last = client.getLastMessage('home/sensors/#');
// Clear history
client.clearHistory();
OptionTypeDefaultDescriptionbrokerstringenvMQTT Broker URLusernamestringenvUsernamepasswordstringenvPasswordclientIdstringautoClient IDreconnectPeriodnumber5000Reconnect interval (ms)connectTimeoutnumber30000Connection timeout (ms)keepalivenumber60Keep-alive (s)messageHistorySizenumber50Max history entriesparseJsonbooleantrueAuto JSON parselogLevelstringinfodebug/info/warn/error
MethodDescriptionconnect()Establish connectiondisconnect([ms])Graceful disconnectsubscribe(topic, opts)Subscribe to topic(s)unsubscribe(topic)Unsubscribepublish(topic, payload, opts)Publish messagegetMessageHistory([topic])Get message historygetHealth()Health statusgetState()Current stateisConnected()Connection checkaddTrigger(id, config)Add threshold triggerremoveTrigger(id)Remove triggergetTriggers()List triggers
EventDescriptionconnectSuccessfully connecteddisconnectDisconnectedmessageMessage received (topic, payload, packet)errorError occurredofflineClient offlinereconnectingAttempting reconnectreconnectSuccessfully reconnected
mqtt-client.js - Main librarymqtt-topics.md - Topic naming conventions