#author("2021-10-15T22:46:38+09:00","ocha","ocha") #author("2021-10-15T22:47:07+09:00","ocha","ocha") [[How2Homebridge]] ** はじめに [#m4bb9e15] HomeKitのデバイスを、他のコンピュータのコマンドラインから動かす方法を説明します。 Homebridge, Mosquittoを使います。 Raspberry Piの上に、Homebridge, Mosquittoが動いていて、 Appleのホーム.appのにHomebridgeが接続されていて、 パソコン(Mac)の上にMosquittoのクライアント機能がインストールされていることが前提です。 以下のページを見てください。 [[How2Homebridge]] [[How2MQTT]] ** MQTT Thingプラグインをインストールする [#h2247cfe] Homebridgeのコントロールページから、プラグインを選び、検索窓にmqttthingとタイプします。するとHomebridge Mqttthingというプラグインが現れます。 #ref(mqttt2.png); これをインストールします。 インストールが終わると設定画面が出ます。これはいつでも設定できるのですが、 とりあえず、Stateless Programmable Switchを選んでみます。 また、名前をSwitch1とし、MQTTのタイプ名も適当に入れておきます。 タイプ名は、mqtttで始まるようにしました。MQTTTプラグイン関係であることがわかりやすいかとおもいました。 #ref(mqttt3.png); これで保存して、Homebridgeを再起動します(右上方の電源スイッチのようなアイコンを押す)。 再起動後に、アクセサリのタブを見ると、押しボタンスイッチのようなアイコンが見えます。 stateless programmable switchは、テレビのリモコンみたいに押しボタン方式のスイッチです。 #ref(mqttt5.png); コンフィグボタンを押すと、Homebridgeの全体の設定が見えます。まだMQTT Thingしか入れてないのでわかりやすいです。 #ref(mqttt4.png); このうち、 "accessories": [ { "type": "statelessProgrammableSwitch", "name": "Switch1", "topics": { "getOnline": "mqttt/online", "getSwitch": "mqttt/switch" }, "accessory": "mqttthing" } ], の部分がMQTT Thingの部分です。このプラグインのページに詳しい説明があります。 https://github.com/arachnetech/homebridge-mqttthing#readme 変更したり書き換えたりする場合は、このコンフィグファイルを編集するのが簡単で良いと思います。 テレビのリモコンのように、多数の押しボタンスイッチをまとめて定義することもできます。 LEDの調光器リモコンのように、on/off/明るさ/色合いなどのスイッチをまとめて定義するのに便利です。 これ以外にも多くの特性を持つデバイスが用意されています。 ** MQTT Thingで使えるデバイス [#o5b0db87] ここではstateless programmable switchを使いましたが、他にも多数のデバイスが使えます。 stateless programmable switchは、(HomeKitから見て)入力専用ですが、 Switchは入出力に対応しています。 設定して、mosquitto_subコマンドでモニターすると動きがわかります。 これを使えば、HomeKitからのon/offを受け取ってLEDを光らせるなどの仕掛けをArduinoで作れます。 ** ホーム.appで設定する [#d858e51d] HomebridgeがHomeKit環境に登録してあれば、ここで定義したSwitch1がホーム.appに現れています。 #ref(mqttt6.png); このアイコンを右クリックして、「設定」を選ぶと動作をしてできます。 #ref(mqttt7.png); 1回押し、2回押し(ダブルクリック)、長押しの選択があります。ここでは1回押しで、隣のIkeaの電球、をonにする設定をしました。 #ref(mqttt8.png); ** MQTTへの出版で電球をonにする [#w8a7e69d] 今までの設定で、 - MQTTサーバのアドレスは192.168.0.134 - Switch1のトピックは mqttt/switch でした。Switch1には、1回押し、2回押し(ダブルクリック)、長押しの3種類があります。それぞれに対応するメッセージは、1,2,Lです。 これは設定ファイルで変更することも可能ですが、1,2,Lがデフォルトです。 なので、MQTTの出版をすれば、IKEAの電球をonにできます。パソコンから、 % mosquitto_pub -h 192.168.0.134 -t mqttt/switch -m 1 とすれば良いです。他のターミナルのウィンドウから、 % mosquitto_sub -h 192.168.0.134 -t # -v として、メッセージをモニターしておくとデバッグに役立ちます。 ** 電球をon/offとトグルする [#q51cebb9] 電球をoffにしたい場合は、2回押しや長押しに電球offを割り当てても良いです。 また、1回押しのたびに反転することもできます。 1回押しに実行されるショートカットを以下のように記述すれば良いです。 #ref(mqttt9.png); これで、 % mosquitto_pub -h 192.168.0.134 -t mqttt/switch -m 1 とするたびに、電球がon/offします。 ** PythonからMQTTブローカにアクセスする [#k0790222] pahoというライブラリを使いました。 https://www.eclipse.org/paho/index.php?page=clients/python/index.php 以下のプログラムでHomeKitで設定された灯を点滅させることができました。 #!/Library/Frameworks/Python.framework/Versions/3.9/bin/python3 import paho.mqtt.client as mqtt # import MQTT lib. broker_address="192.168.0.134" client = mqtt.Client() #create new instance client.connect(broker_address) #connect to broker client.publish("mqttt/switch","1") #publish ** ArduinoからMQTTブローカにアクセスする [#i3acb8a3] 同じくArduinoからの例です。 https://github.com/knolleary/pubsubclient にある、pubsubclientライブラリを使ってます。 上のページにあるサンプルを参考にしたプログラムが以下です。 ここでは、13番に接続したスイッチが押された時に、MQTTに1を送ります。それでHomeKitが灯をon/offしてくれるはずです。 元のサンプルは、送受信に対応していたので、subscribeの部分も残ってます。その部分は今回は使っていないです。 HomeKitからの指示でArduinoでLEDを点灯させるなどの場合には、このcallbackの関数のところに、その処理を書けば良いです。。 WiFiの設定もしているので、長いですが、多分これで動きます。 #include <WiFi.h> #include <PubSubClient.h> //input & output ping const int PUSHSW=13; //external push switch //WiFi const char SSID[] = "siiolab408_2G"; const char PASSWORD[] = "xxxxxxx"; //PubSubClient (MQTT) const char mqttbroker[] = "192.168.0.134"; //MQTT broker address const int mqttport=1883; //MQTT port const char subtopic[] = "mqttthing/sub"; //mqtt topic to subscribe これは今回使用しません const char pubtopic[] = "mqttthing/switch"; //mqtt topic to publish char clientID[] = "ESP32_xx:xx:xx:xx:xx:xx"; //MAC address is set in setup() WiFiClient wifiClient; PubSubClient mqttClient(wifiClient); //購読の結果が返ってきたら呼び出される関数 void callback(char* topic, byte* payload, unsigned int length) { Serial.print("Message arrived [");Serial.print(topic);Serial.print("] "); String msg = String((char*) payload); Serial.println(msg); //必要ならばこの後に、得られたメッセージに従った処理を書く //payload[length]='\0'; //メッセージの最後を0にしてC言語型文字列にする //String msg = String((char*) payload); //それをStringインスタンスに変換する //if(msg.compareTo("true")==0) { // digitalWrite(xxxx, LOW);//メッセージに従った処理を書く // } } //connecting MQTT void connectMQTT() { mqttClient.setServer(mqttbroker, mqttport); mqttClient.setCallback(callback); Serial.println("Attempting MQTT connection. "); // Loop until we're reconnected while (! mqttClient.connected() ) { // Attempt to connect if (mqttClient.connect(clientID)) { Serial.println("Connected. "); // Once connected, publish an announcement... mqttClient.publish(pubtopic,"Arduino is ready."); // ... and subscribe mqttClient.subscribe(subtopic); } else { Serial.print("Failed, rc="); Serial.print(mqttClient.state()); Serial.println(", try again in 5 seconds. "); // Wait 5 seconds before retrying delay(5000); } } } //connecting WiFi void connectWiFi() { WiFi.begin(SSID, PASSWORD); Serial.print("Attempting WiFi connection. SSID: "); Serial.print(SSID); while (WiFi.status() != WL_CONNECTED) { Serial.print("."); delay(1000); } Serial.print("\nWiFi connected. Assigned address is: "); Serial.println(WiFi.localIP()); } void setup() { pinMode(PUSHSW, INPUT_PULLUP); Serial.begin(115200); // ESP standard speed 115200 while (!Serial) ; // wait for serial port to connect. String wifiMACString = WiFi.macAddress(); //WiFi MAC address wifiMACString.toCharArray(&clientID[6], 18, 0); //"EPS32_xx:xx:xx:xx:xx:xx" Serial.print("\nMQTT clientID: ");Serial.println(clientID); connectWiFi(); connectMQTT(); } //last state of the PUSHSW int lastSWstate; void loop() { if( WiFi.status() == WL_DISCONNECTED) connectWiFi(); if (! mqttClient.connected()) connectMQTT(); mqttClient.loop(); int newSWstate=digitalRead(PUSHSW); if(lastSWstate!=newSWstate) { //update only when SW changed if(newSWstate==LOW) mqttClient.publish(pubtopic,"1"); } lastSWstate=newSWstate; delay(500); delay(500); }