Electron から MQTT.js SDK を使ってデプロイメントに接続する
本記事では、Electron プロジェクトでの MQTT の利用方法を主に紹介し、シンプルな MQTT デスクトップクライアントの作成、およびクライアントと MQTT ブローカー間の接続、サブスクライブ、サブスクライブ解除、メッセージ送受信などの機能を実装する方法を解説します。
事前準備
MQTT ブローカーのデプロイ
- EMQX が提供する 無料のパブリック MQTT ブローカー を利用できます。このサービスは EMQX プラットフォーム をベースに構築されています。ブローカーへのアクセス情報は以下の通りです:
- ブローカー:broker.emqx.io
- TCP ポート:1883
- SSL/TLS ポート:8883
- WebSocket ポート:8083
- WebSocket TLS/SSL ポート:8084
- また、独自の MQTT ブローカーを作成することも可能です。デプロイメントが稼働状態になったら、デプロイメント概要ページで接続情報を確認できます。後のクライアント接続段階で必要となるユーザー名とパスワードは、アクセス制御 -> 認証 で設定してください。
Electron アプリケーションの作成
Electron は GitHub によって開発・保守されているオープンソースのソフトウェアフレームワークです。Chromium レンダリングエンジンと Node.js ランタイムを組み合わせ、Web 技術を用いたデスクトップ GUI アプリケーションの開発を可能にします。Electron は Atom、GitHub Desktop、Light Table、Visual Studio Code、WordPress Desktop などの主要なオープンソースプロジェクトの GUI フレームワークとして使われています。^1
基本的な Electron プロジェクトは、package.json
(メタデータ)、main.js
(コード)、index.html
(GUI)の3ファイルで構成されます。Electron の実行ファイル(Windows では electron.exe、macOS では electron.app、Linux では electron)がフレームを提供し、開発者はフラグの追加、アイコンのカスタマイズ、実行ファイルの名前変更や編集を自由に行えます。
新規プロジェクトの構築方法は多様ですが、ここではいくつかの簡単な例を挙げます:
手動で作成する場合、プロジェクトディレクトリで以下を実行します
shellcd your-project npm init npm i -D electron@latest
詳細な手順は以下のドキュメントも参照してください。
公式テンプレートプロジェクト
electron-quick-start
を使った迅速な開発アドレス: https://github.com/electron/electron-quick-start
shell# リポジトリをクローン git clone https://github.com/electron/electron-quick-start # リポジトリに移動 cd electron-quick-start # 依存関係をインストール npm install # アプリを起動 npm start
React.js
を使った開発が可能なテンプレートプロジェクトelectron-react-boilerplate
を使った迅速な開発アドレス: https://github.com/electron-react-boilerplate/electron-react-boilerplate
shellgit clone --depth 1 --single-branch https://github.com/electron-react-boilerplate/electron-react-boilerplate.git your-project-name cd your-project-name yarn
Vue.js
を使った開発が可能なelectron-vue
によるプロジェクトの迅速な構築は、vue-cli
ツールを用いたプロジェクト初期化と連携します。アドレス: https://github.com/SimulatedGREG/electron-vue
shell# vue-cli をインストールし、ボイラープレートを作成 npm install -g vue-cli vue init simulatedgreg/electron-vue my-project # 依存関係をインストールし、アプリを起動 cd my-project yarn # または npm install yarn run dev # または npm run dev
本記事では、公式の electron quick start プロジェクトテンプレートを使ってプロジェクトを初期化し、例題プロジェクトを迅速に構築します。
依存関係のインストール
コマンドラインから以下を実行します。
npm install mqtt --save
依存関係のインストール後、デバッグ用にコンソールを開きたい場合は、main.js
のコードを修正し、win.webContents.openDevTools()
のコメントアウトを解除してください。
// DevTools を開く
mainWindow.webContents.openDevTools()
この場合、ローカルにインストールした MQTT.js
モジュールは、フロントエンドビルダーでフロントエンドページをパッケージングしない限り、renderer.js
に直接読み込めません。ビルドツールを使う方法以外に、以下の2つの解決策があります:
webPreferences
内でnodeIntegration
を true に設定する方法。これにより、webview
内で Node 統合が有効になり、require
やprocess
などの Node API を使って低レベルのシステムリソースにアクセスできます。Node 統合はデフォルトで無効です。javascriptconst mainWindow = new BrowserWindow({ width: 800, height: 600, webPreferences: { nodeIntegration: true, preload: path.join(__dirname, 'preload.js'), }, })
preload.js
内で MQTT.js モジュールをインポートする方法。Node 統合が無効でも、このスクリプトはすべての Node API にアクセス可能です。ただし、このスクリプトの実行完了後、Node 経由で注入されたグローバルオブジェクトは削除されます。MQTT.js モジュールをメインプロセスでインポートし接続する方法。Electron では、
ipcMain
とipcRenderer
モジュールを使い、開発者が定義した「チャネル」を介してプロセス間通信を行います。これらのチャネルは任意の名前を付けられ、双方向に通信可能です。使用例は IPC チュートリアル を参照してください。
接続
より直感的に説明するため、例題の主要な接続コードは renderer.js
ファイルに記述します。セキュリティ面を考慮し、インストールした MQTT モジュールは Node.js API の require メソッドを使い、preload.js
ファイル内で読み込みます(上記の方法2)。また、この方法ではグローバルな window オブジェクトに注入します。
注意: Context isolation (contextIsolation) は Electron 12 以降デフォルトで有効になっています。preload スクリプトはアタッチ先の renderer と
window
グローバルを共有しますが、contextIsolation
のため preload スクリプトから直接window
に変数をアタッチできません。
そのため、webPreferences
で contextIsolation: false
を設定して無効化します。
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: false, // バージョン12.0.0以上はデフォルトで有効
}
})
これにより、読み込んだモジュールを renderer.js
で直接アクセス可能になります。
- MQTT モジュールのインポート例
// preload.js
const mqtt = require('mqtt')
window.mqtt = mqtt
TCP ポートで接続
クライアント ID、ユーザー名、パスワードを以下のコードで設定します。クライアント ID は一意である必要があります。
const clientId = 'emqx_vue3_' + Math.random().toString(16).substring(2, 8)
const username = 'emqx_test'
const password = 'emqx_test'
以下のコードでクライアントと MQTT ブローカー間の接続を確立します。
const client = mqtt.connect('mqtt://broker.emqx.io:1883', {
clientId,
username,
password,
// ...その他のオプション
})
TCP セキュアポートで接続
TLS/SSL 暗号化が有効な場合、接続のパラメーターオプションは TCP ポート接続時と同様です。プロトコルを mqtts
に変更し、正しいポート番号を指定するだけで構いません。
以下のコードでクライアントと MQTT ブローカー間の接続を確立します。
const client = mqtt.connect('mqtts://broker.emqx.io:8883', {
clientId,
username,
password,
// ...その他のオプション
})
WebSocket ポートで接続
MQTT WebSocket は接続パスとして /path
を統一的に使用します。接続時に指定が必要で、EMQX ブローカーは /mqtt
をパスとして使います。
そのため、WebSocket 接続時はポート番号の変更とプロトコルを ws
に切り替えるだけでなく、/mqtt
パスを追加する必要があります。
以下のコードでクライアントと MQTT ブローカー間の接続を確立します。
const client = mqtt.connect('ws://broker.emqx.io:8083/mqtt', {
clientId,
username,
password,
// ...その他のオプション
})
WebSocket セキュアポートで接続
TLS/SSL 暗号化が有効な場合、接続のパラメーターオプションは WebSocket ポート接続時と同様です。プロトコルを wss
に変更し、正しいポート番号を指定するだけで構いません。
以下のコードでクライアントと MQTT ブローカー間の接続を確立します。
const client = mqtt.connect('wss://broker.emqx.io:8084/mqtt', {
clientId,
username,
password,
// ...その他のオプション
})
サブスクライブとパブリッシュ
トピックのサブスクライブ
サブスクライブするトピックと対応する QoS レベル(任意)を設定し、MQTT.js の subscribe
メソッドを呼び出してサブスクライブ操作を行います。
function onSub() {
if (client.connected) {
const { topic, qos } = subscriber
client.subscribe(
topic.value,
{ qos: parseInt(qos.value, 10) },
(error, res) => {
if (error) {
console.error('Subscribe error: ', error)
} else {
console.log('Subscribed: ', res)
}
}
)
}
}
トピックのサブスクライブ解除
サブスクライブ解除時は、不要になったトピックと対応する QoS(任意)を渡す必要があります。
https://github.com/mqttjs/MQTT.js#mqttclientunsubscribetopictopic-array-options-callback
function onUnsub() {
if (client.connected) {
const { topic } = subscriber
client.unsubscribe(topic.value, (error) => {
if (error) {
console.error('Unsubscribe error: ', error)
} else {
console.log('Unsubscribed: ', topic.value)
}
})
}
}
メッセージのパブリッシュ
メッセージをパブリッシュする際は、MQTT ブローカーに対して対応するトピックとメッセージ内容を通知する必要があります。
https://github.com/mqttjs/MQTT.js#mqttclientpublishtopic-message-options-callback
function onSend() {
if (client.connected) {
const { topic, qos, payload } = publisher
client.publish(topic.value, payload.value, {
qos: parseInt(qos.value, 10),
retain: false,
})
}
}
メッセージの受信
// onConnect 関数内
client.on('message', (topic, message) => {
const msg = document.createElement('div')
msg.setAttribute('class', 'message-body')
msg.innerText = `${message.toString()}\nOn topic: ${topic}`
document.getElementById('article').appendChild(msg)
})
MQTT ブローカーからの切断
クライアントによるアクティブな切断
https://github.com/mqttjs/MQTT.js#mqttclientendforce-options-callback
function onDisconnect() {
if (client.connected) {
client.end()
client.on('close', () => {
connectBtn.innerText = 'Connect'
console.log(options.clientId + ' disconnected')
})
}
}
接続のテスト
ここまでで、Electron で作成した MQTT 5.0 クライアントツール - MQTTX(こちらも Electron で開発)を使い、メッセージの送受信をテストします。
MQTTX からクライアントにメッセージを送信すると、メッセージが正常に受信されていることが確認できます:
自作クライアントから MQTTX にメッセージを送信すると、MQTTX 側でも正常にメッセージを受信していることが確認できます:
まとめ
ここまでで、Electron を使ってシンプルな MQTT デスクトップクライアントを作成し、クライアントと MQTT ブローカー間の接続、メッセージ送受信、サブスクライブ解除、切断のシナリオをシミュレーションできました。完全なサンプルソースコードは MQTT Client Electron ページからダウンロード可能です。また、MQTT Client Example ページでは他言語のデモ例も多数公開されていますので、ぜひご覧ください。