await-mqtt
v0.2.5
Published
๐ Promise-based async/await wrapper for MQTT.js with React hooks
Maintainers
Readme
await-mqtt
๐ Elegant Promise-based async/await wrapper for MQTT.js with TypeScript support and React hooks
Features
- Promise-based API: Full Promise support for all MQTT operations
- Async/Await Syntax: Clean, modern JavaScript with exception handling
- TypeScript Ready: Comprehensive type definitions included
- React Hooks: Easy integration with React applications
- Smart Message Handling: Topic filtering with wildcard support
- JSON Support: Automatic serialization for object messages
- Timeout Control: Configurable timeouts for all operations
- Connection Management: Auto-reconnect with configurable options
- Simple Architecture: Straightforward integration with minimal setup
- Lightweight: Minimal overhead on top of MQTT.js
- Bun Optimized: Built with performance in mind
Installation
# Using npm
npm install await-mqtt mqtt
# Using yarn
yarn add await-mqtt mqtt
# Using bun
bun add await-mqtt mqtt
# If using with React
npm install await-mqtt mqtt reactQuick Start
Standard Usage
import { Mqtt } from 'await-mqtt';
async function main() {
// Create a client and connect in one step
const mqtt = await Mqtt({
url: 'mqtt://broker.example.com',
clientId: 'my-client',
});
try {
console.log('Connected successfully!');
// Subscribe to topics
await mqtt.subscribe('sensors/#');
// Register message handler with async support
mqtt.onMessage('sensors/temperature', async (topic, message) => {
const temperature = parseFloat(message.toString());
console.log(`Temperature: ${temperature}ยฐC`);
// Perform async operations inside handlers
await processTemperature(temperature);
});
// Publish string message
await mqtt.publish('sensors/command', 'READ');
// Publish JSON object (automatically serialized)
await mqtt.publish('sensors/config', {
interval: 5000,
precision: 2,
enabled: true
});
// Clean disconnect when done
await mqtt.end();
} catch (err) {
console.error('MQTT Error:', err);
}
}
main();React Hooks Usage
import React, { useState } from 'react';
import { MqttProvider, useMqtt, usePublish, useMessages } from 'await-mqtt/react';
// Main component with MQTT provider
function App() {
return (
<MqttProvider options={{ clientId: 'react-client' }}>
<div className="app">
<h1>MQTT React Demo</h1>
<ConnectionControl />
<MessagePublisher />
<MessageSubscriber />
</div>
</MqttProvider>
);
}
// Connection control component
function ConnectionControl() {
const { connect, disconnect, isConnected, isConnecting, error } = useMqtt();
const [brokerUrl, setBrokerUrl] = useState('ws://broker.example.com:8883');
return (
<div>
<h2>Connection Status: {isConnected ? 'Connected' : (isConnecting ? 'Connecting...' : 'Disconnected')}</h2>
{error && <p className="error">Error: {error.message}</p>}
<input
type="text"
value={brokerUrl}
onChange={(e) => setBrokerUrl(e.target.value)}
placeholder="Broker URL"
/>
<button onClick={() => isConnected ? disconnect() : connect(brokerUrl)}>
{isConnected ? 'Disconnect' : 'Connect'}
</button>
</div>
);
}
// Message publisher component
function MessagePublisher() {
const { sendMessage, isPublishing } = usePublish();
const [topic, setTopic] = useState('test/topic');
const [message, setMessage] = useState('Hello MQTT');
const handlePublish = async () => {
const result = await sendMessage(topic, message);
if (result.success) {
console.log('Message published successfully');
}
};
return (
<div>
<h2>Publish Message</h2>
<input
type="text"
value={topic}
onChange={(e) => setTopic(e.target.value)}
placeholder="Topic"
/>
<input
type="text"
value={message}
onChange={(e) => setMessage(e.target.value)}
placeholder="Message"
/>
<button onClick={handlePublish} disabled={isPublishing}>
{isPublishing ? 'Publishing...' : 'Publish'}
</button>
</div>
);
}
// Message subscriber component
function MessageSubscriber() {
const [topic, setTopic] = useState('test/topic');
const { messages, isSubscribed } = useMessages(topic, 10);
return (
<div>
<h2>Subscribe to Messages</h2>
<input
type="text"
value={topic}
onChange={(e) => setTopic(e.target.value)}
placeholder="Topic"
/>
<p>Status: {isSubscribed ? 'Subscribed' : 'Not subscribed'}</p>
<div className="messages">
<h3>Received Messages</h3>
{messages.length === 0 ? (
<p>No messages received</p>
) : (
<ul>
{messages.map((msg, idx) => (
<li key={idx}>
<div>{msg.topic} - {new Date(msg.timestamp).toLocaleTimeString()}</div>
<div>{typeof msg.payload === 'object' ? JSON.stringify(msg.payload) : String(msg.payload)}</div>
</li>
))}
</ul>
)}
</div>
</div>
);
}
export default App;API Reference
Creating a Client
import { Mqtt } from 'await-mqtt';
// Create and connect with default options
const mqtt = await Mqtt({
url: 'mqtt://broker.example.com'
});
// Create and connect with custom options
const mqtt = await Mqtt({
url: 'mqtt://broker.example.com',
clientId: 'my-client',
clean: true,
keepalive: 30,
// Additional await-mqtt options
autoReconnect: true,
reconnectInterval: 1000,
maxReconnectAttempts: 10
});Connection Management
Since the Mqtt function connects automatically, you typically don't need to call connect separately. However, if you need to reconnect:
reconnect()
Reconnect to the broker if disconnected.
// Reconnect to the broker
await mqtt.reconnect();end(force?)
Disconnect from the broker.
// Graceful disconnect (default)
await mqtt.end();
// Force disconnect immediately
await mqtt.end(true);reconnect(clean?)
Manually trigger a reconnection.
await mqtt.reconnect();
// Reconnect with clean session
await mqtt.reconnect(true);Messaging
publish(topic, payload, options?)
Publish a message to a topic with Promise support.
// String payload
await mqtt.publish('home/lights/living-room', 'ON');
// Buffer payload
await mqtt.publish('home/sensor/data', Buffer.from([0x01, 0x02, 0x03]));
// Object payload (automatically JSON serialized)
await mqtt.publish('home/settings', {
brightness: 80,
color: '#FFFFFF',
mode: 'auto'
});
// With QoS and other options
await mqtt.publish('home/alerts', 'MOTION_DETECTED', {
qos: 2,
retain: true,
timeout: 5000 // ms, await-mqtt specific
});subscribe(topic, options?)
Subscribe to topics with flexible formats.
// Single topic
await mqtt.subscribe('home/temperature');
// Multiple topics as array
await mqtt.subscribe(['home/temperature', 'home/humidity']);
// With QoS
await mqtt.subscribe('home/temperature', { qos: 1 });
// Multiple topics with different QoS levels
await mqtt.subscribe({
'home/temperature': { qos: 0 },
'home/humidity': { qos: 1 },
'home/pressure': { qos: 2 }
});
// With timeout
await mqtt.subscribe('home/#', {
qos: 1,
timeout: 5000 // ms, await-mqtt specific
});unsubscribe(topic, options?)
Unsubscribe from topics.
// Single topic
await mqtt.unsubscribe('home/temperature');
// Multiple topics
await mqtt.unsubscribe(['home/temperature', 'home/humidity']);
// With timeout
await mqtt.unsubscribe('home/#', {
timeout: 5000 // ms, await-mqtt specific
});onMessage(topic, handler)
Register message handlers with wildcard support.
// Basic handler
mqtt.onMessage('home/temperature', (topic, message) => {
console.log(`Temperature: ${message}`);
});
// Async handler
mqtt.onMessage('home/commands', async (topic, message) => {
const command = message.toString();
await executeCommand(command);
await mqtt.publish('home/commands/response', 'SUCCESS');
});
// Wildcard handler
mqtt.onMessage('home/+/temperature', (topic, message) => {
const room = topic.split('/')[1];
console.log(`${room} temperature: ${message}ยฐC`);
});
// Global message handler
mqtt.onMessage('#', (topic, message) => {
console.log(`[${topic}] ${message}`);
});React Hooks
MqttProvider
Provides MQTT context to all child components.
import { MqttProvider } from 'await-mqtt/react';
function App() {
return (
<MqttProvider
options={{
clientId: 'react-app',
url: 'ws://broker.example.com:8883'
}}
autoConnect={true}
>
{/* Your app components */}
</MqttProvider>
);
}useMqtt
Primary hook for accessing the MQTT client and connection state.
import { useMqtt } from 'await-mqtt/react';
function ConnectionStatus() {
const {
client, // The MqttManager instance
isConnected, // Boolean indicating connection status
isConnecting, // Boolean indicating if connection is in progress
error, // Error object if connection failed
connect, // Function to connect to a broker
disconnect, // Function to disconnect from the broker
publish, // Function to publish a message
subscribe, // Function to subscribe to a topic
unsubscribe, // Function to unsubscribe from a topic
onMessage, // Function to register a message handler
getSubscriptions // Function to get active subscriptions
} = useMqtt();
return (
<div>
<div>Status: {isConnected ? 'Connected' : (isConnecting ? 'Connecting...' : 'Disconnected')}</div>
{error && <div>Error: {error.message}</div>}
<button onClick={() => connect('ws://broker.example.com:8883')}>Connect</button>
<button onClick={disconnect}>Disconnect</button>
</div>
);
}useSubscription
Hook for subscribing to a specific topic.
import { useSubscription } from 'await-mqtt/react';
function TopicSubscriber({ topic }) {
const { isSubscribed, error } = useSubscription(topic, { qos: 1 });
return (
<div>
<div>Topic: {topic}</div>
<div>Status: {isSubscribed ? 'Subscribed' : 'Not subscribed'}</div>
{error && <div>Error: {error.message}</div>}
</div>
);
}useMessages
Hook for collecting messages from a specific topic.
import { useMessages } from 'await-mqtt/react';
function MessageList({ topic }) {
const { messages, clearMessages, isSubscribed, subscriptionError } = useMessages(topic, 10);
return (
<div>
<div>Topic: {topic}</div>
<div>Status: {isSubscribed ? 'Subscribed' : 'Not subscribed'}</div>
<button onClick={clearMessages}>Clear Messages</button>
<ul>
{messages.map((msg, idx) => (
<li key={idx}>
<div>{new Date(msg.timestamp).toLocaleTimeString()}</div>
<div>{typeof msg.payload === 'object'
? JSON.stringify(msg.payload)
: String(msg.payload)}
</div>
</li>
))}
</ul>
</div>
);
}useMessage
Hook for tracking the latest message from a specific topic.
import { useMessage } from 'await-mqtt/react';
function LatestMessage({ topic }) {
const { message, clearMessage, isSubscribed, subscriptionError } = useMessage(topic);
return (
<div>
<div>Topic: {topic}</div>
<div>Status: {isSubscribed ? 'Subscribed' : 'Not subscribed'}</div>
<button onClick={clearMessage}>Clear Message</button>
<div>
{message === null ? (
<p>No message received</p>
) : (
<div>
{typeof message === 'object'
? JSON.stringify(message, null, 2)
: String(message)}
</div>
)}
</div>
</div>
);
}usePublish
Hook for publishing messages with state tracking.
import { usePublish } from 'await-mqtt/react';
function MessagePublisher() {
const { sendMessage, isPublishing, lastError } = usePublish();
const [topic, setTopic] = useState('test/topic');
const [message, setMessage] = useState('Hello MQTT');
const handlePublish = async () => {
const result = await sendMessage(topic, message, { qos: 1 });
if (result.success) {
console.log('Message published successfully');
}
};
return (
<div>
<input
type="text"
value={topic}
onChange={(e) => setTopic(e.target.value)}
placeholder="Topic"
/>
<input
type="text"
value={message}
onChange={(e) => setMessage(e.target.value)}
placeholder="Message"
/>
<button onClick={handlePublish} disabled={isPublishing}>
{isPublishing ? 'Publishing...' : 'Publish'}
</button>
{lastError && <div>Error: {lastError.message}</div>}
</div>
);
}Examples
Home Automation
import { Mqtt } from 'await-mqtt';
async function setupHomeAutomation() {
try {
// Create and connect client
const mqtt = await Mqtt({
url: 'mqtt://home-broker.local',
clientId: 'home-controller'
});
// Subscribe to all device commands
await mqtt.subscribe('home/+/command');
// Handle lights
mqtt.onMessage('home/lights/+/command', async (topic, message) => {
const room = topic.split('/')[2];
const command = message.toString();
console.log(`Setting ${room} lights to ${command}`);
await controlLight(room, command);
// Confirm the action
await mqtt.publish(`home/lights/${room}/status`, command);
});
// Handle temperature sensors
mqtt.onMessage('home/sensors/+/temperature', (topic, message) => {
const room = topic.split('/')[2];
const temperature = parseFloat(message.toString());
console.log(`${room} temperature: ${temperature}ยฐC`);
// Trigger climate control if needed
if (temperature > 25) {
mqtt.publish(`home/climate/${room}/command`, 'COOL');
} else if (temperature < 18) {
mqtt.publish(`home/climate/${room}/command`, 'HEAT');
}
});
} catch (error) {
console.error('Failed to setup home automation:', error);
}
}React Smart Home Dashboard
import React, { useState } from 'react';
import { MqttProvider, useMqtt, useMessage, usePublish } from 'await-mqtt/react';
function SmartHomeApp() {
return (
<MqttProvider
options={{
url: 'ws://home-broker.local:8883',
clientId: 'home-dashboard'
}}
autoConnect={true}
>
<div className="smart-home-dashboard">
<h1>Smart Home Dashboard</h1>
<ConnectionStatus />
<div className="rooms-grid">
<RoomPanel room="living-room" />
<RoomPanel room="kitchen" />
<RoomPanel room="bedroom" />
<RoomPanel room="bathroom" />
</div>
</div>
</MqttProvider>
);
}
function ConnectionStatus() {
const { isConnected, isConnecting, error, connect, disconnect } = useMqtt();
return (
<div className="connection-status">
<div className={`status-indicator ${isConnected ? 'connected' : 'disconnected'}`}>
{isConnected ? 'Connected' : (isConnecting ? 'Connecting...' : 'Disconnected')}
</div>
{error && <div className="error-message">Error: {error.message}</div>}
<button onClick={() => isConnected ? disconnect() : connect('ws://home-broker.local:8883')}>
{isConnected ? 'Disconnect' : 'Connect'}
</button>
</div>
);
}
function RoomPanel({ room }) {
return (
<div className="room-panel">
<h2>{room.replace('-', ' ')}</h2>
<div className="sensors">
<TemperatureSensor room={room} />
<HumiditySensor room={room} />
</div>
<div className="controls">
<LightControl room={room} />
<ClimateControl room={room} />
</div>
</div>
);
}
function TemperatureSensor({ room }) {
const { message } = useMessage(`home/sensors/${room}/temperature`);
return (
<div className="sensor temperature-sensor">
<h3>Temperature</h3>
<div className="sensor-value">
{message !== null ? `${message}ยฐC` : '--'}
</div>
</div>
);
}
function HumiditySensor({ room }) {
const { message } = useMessage(`home/sensors/${room}/humidity`);
return (
<div className="sensor humidity-sensor">
<h3>Humidity</h3>
<div className="sensor-value">
{message !== null ? `${message}%` : '--'}
</div>
</div>
);
}
function LightControl({ room }) {
const { message: status } = useMessage(`home/lights/${room}/status`);
const { sendMessage } = usePublish();
const isOn = status === 'ON';
const toggleLight = async () => {
const newStatus = isOn ? 'OFF' : 'ON';
await sendMessage(`home/lights/${room}/command`, newStatus);
};
return (
<div className="control light-control">
<h3>Lights</h3>
<button
className={`toggle-button ${isOn ? 'on' : 'off'}`}
onClick={toggleLight}
>
{isOn ? 'ON' : 'OFF'}
</button>
</div>
);
}
function ClimateControl({ room }) {
const { message: currentMode } = useMessage(`home/climate/${room}/status`);
const { sendMessage } = usePublish();
const setMode = async (mode) => {
await sendMessage(`home/climate/${room}/command`, mode);
};
return (
<div className="control climate-control">
<h3>Climate</h3>
<div className="mode-buttons">
<button
className={`mode-button ${currentMode === 'OFF' ? 'active' : ''}`}
onClick={() => setMode('OFF')}
>
Off
</button>
<button
className={`mode-button ${currentMode === 'HEAT' ? 'active' : ''}`}
onClick={() => setMode('HEAT')}
>
Heat
</button>
<button
className={`mode-button ${currentMode === 'COOL' ? 'active' : ''}`}
onClick={() => setMode('COOL')}
>
Cool
</button>
<button
className={`mode-button ${currentMode === 'AUTO' ? 'active' : ''}`}
onClick={() => setMode('AUTO')}
>
Auto
</button>
</div>
</div>
);
}
export default SmartHomeApp;License
MIT
