アレクサとIFTTTの離別
2023年11月からアレクサの定型アクションのトリガーとしてIFTTTが利用できなくなりました。事前にアナウンスされていたようで、実際気づいたのは、アレクサに照明をオンオフを指示しても全く反応なくなったのがきっかけでした。
ちなみに家の照明はパナソニックの赤外線スイッチ、よくあるリモコンタイプのシーラングライトを採用していますがAlexa、GoogleHomeなどのIoT接続できるような代物のではありません。
電子工作界隈では定番の工作、赤外線LEDを使ったマルチリモコン風のものを作りアレクサから操作していました。
アレクサからの流れは以下のような流れで指示が飛びます。今回はIFTTTとアレクサの間の指示が飛ばなくなり使えなくなりました。少し前にIFTTTの有料化があり、この時にも騒ぎとなりました。この時は課金(移行プランで月2ドル)して継続して使うようにした経緯があります。アレクサと連携できなくなった今、もうIFTTTには用はありません。
今回はアレクサとIFTTT間の連携が無くなってしまったので、根本的な見直しをする必要があります。
fauxmoESPの導入
アレクサからトリガーに出来そうなアクションを見てみます。IFTTTはもちろん消え去っています( ´∀` )
この中で出来そうな筆頭は「スキル」なんですが、一度スキルを作ってみてPOSTリクエストまで投げることは出来たんですが、開発段階ではアクションスキルを指定できないみたいなんです。つまりは公開してパブリックにしないダメなわけで自宅用に限定した用途では敷居が高いです。またスキルを公開してメンテナンスする気も全くありません。
あとは、もうスマートホームの仮想スイッチぐらいしかないわけです。ここでfauxmoESPというESP32/8266用の仮想スマートスイッチライブラリの登場です。昔、ESP8266の時に触ったことがありました。
この時なぜ導入に後ろ向きだったかというと
- 3台以上の仮想スイッチを割り当てると動作がおかしくなる(ESP8266のみ?)
- 3rdGenのアレクサだと80ポートを割り当てないとダメ ⇒ Httpサーバーが立てにくい(81ポートで立てざる得ない)
この2点で見送っていました。上の動作不良は解決しているようです。
「Httpサーバーが立てにくい」というのは直でESP8266にアクセスしてリモコンWebページからでも赤外線操作を実現するために80ポートを利用する必要があったためです。
今回は、ESP32で構築し、下のようなイメージで赤外線リモコンを動作させる予定ですので、ESP32でIFTTTとBeebotteの役割をまとめてやらせたいと思います。赤外線LEDを発するESP8266はすでにHTTPで照明をオンオフできるようにI/Fを実装しているのでいじる必要ないです。
こうすれば800円ぐらいのESP32をつなげておくだけで、IFTTTやBeebotteを使わなくてよいし、インターネットを経由する必要もないし、万事OKというわけです。
コーディング
実際のコードはなんともあっけないです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 |
#include <WiFi.h> #include <HTTPClient.h> #include <WiFiClient.h> #include "fauxmoESP.h" #define RETRY_COUNT 3 //Httpリクエストに失敗したときのリトライ回数 HTTPClient http; WiFiClient wifiClient; fauxmoESP fauxmo; const char *living_light = "居間照明"; const char *url_living_light_on = "http://192.168.10.40:81/light?act=on"; const char *url_living_light_off = "http://192.168.10.40:81/light?act=off"; const char *living_weak_light = "居間照明弱"; const char *url_living_light_lamp = "http://192.168.10.40:81/light?act=lamp"; const char *washitu_light = "和室照明"; const char *url_washitu_light_on = "http://192.168.10.43/light2?act=power"; const char *kitchen_light = "キッチン照明"; const char *url_kitchen_light_on = "http://192.168.10.40:81/light3?act=on"; const char *url_kitchen_light_off = "http://192.168.10.40:81/light3?act=off"; const char *study_light = "勉強照明"; const char *url_study_light_on = "http://192.168.10.40:81/light4?act=on"; const char *url_study_light_off = "http://192.168.10.40:81/light4?act=off"; const char *father_light = "父部屋照明"; const char *url_father_light_on = "http://192.168.10.41:81/light?act=on"; const char *url_father_light_jyoya= "http://192.168.10.41:81/light?act=night"; const char *url_father_light_off = "http://192.168.10.41:81/light?act=off"; //ネットワーク環境 const char* ssid = "*********"; const char* password = "*********"; // 静的IPアドレス IPAddress ip(192, 168, 10, 55); IPAddress gateway(192,168, 10, 1); IPAddress subnet(255, 255, 255, 0); IPAddress DNS(192, 168, 10, 1); //httpリクエスト bool reqestData(String url){ http.setConnectTimeout(200); http.setTimeout(300); http.begin(wifiClient,url); bool ret = false; int httpCode = http.GET(); if(httpCode > 0) { if(httpCode == HTTP_CODE_OK) { ret = true; } } http.end(); return ret; } //wifi接続 void connectWifi(){ // Connect to Wi-Fi WiFi.mode(WIFI_STA); WiFi.config(ip, gateway, subnet, DNS); delay(100); WiFi.begin(ssid, password); Serial.print("Connecting to WiFi"); while (WiFi.status() != WL_CONNECTED) { delay(50); Serial.print("."); } Serial.println(""); Serial.println(WiFi.localIP()); } void setup() { // put your setup code here, to run once: Serial.begin(115200); //Wifi接続 connectWifi(); //fauxmo初期化 fauxmo.createServer(true); fauxmo.setPort(80); //※3rd GEN Alexa fauxmo.enable(true); //仮想スイッチデバイスの追加 fauxmo.addDevice(living_light); fauxmo.addDevice(washitu_light); fauxmo.addDevice(living_weak_light); fauxmo.addDevice(kitchen_light); fauxmo.addDevice(study_light); fauxmo.addDevice(father_light); //アレクサからのトリガー受信処理 fauxmo.onSetState([](unsigned char device_id, const char *device_name, bool state, unsigned char value) { Serial.printf("Device #%d (%s) state: %s value: %d\n", device_id, device_name, state ? "ON" : "OFF", value); //寝室ライト if (strcmp(device_name, washitu_light) == 0) { for(int i = 0; i < RETRY_COUNT; i++) { bool ret = reqestData(url_washitu_light_on); if(ret) break; delay(500); } Serial.println("和室オンOK"); } //リビングライト if (strcmp(device_name, living_light) == 0) { String url = state ? url_living_light_on : url_living_light_off; for(int i = 0; i < RETRY_COUNT; i++) { bool ret = reqestData(url); if(ret) break; delay(500); } Serial.println("リビングOK"); } //リビングライト弱調光 if (strcmp(device_name, living_weak_light) == 0) { for(int i = 0; i < RETRY_COUNT; i++) { bool ret = reqestData(url_living_light_lamp); if(ret) break; delay(500); } Serial.println("リビングライト弱調光OK"); } //キッチンライト if (strcmp(device_name, kitchen_light) == 0) { String url = state ? url_kitchen_light_on : url_kitchen_light_off; for(int i = 0; i < RETRY_COUNT; i++) { bool ret = reqestData(url); if(ret) break; delay(500); } Serial.println("キッチンOK"); } //勉強ライト if (strcmp(device_name, study_light) == 0) { String url = state ? url_study_light_on : url_study_light_off; for(int i = 0; i < RETRY_COUNT; i++) { bool ret = reqestData(url); if(ret) break; delay(500); } Serial.println("勉強ライトOK"); } //父部屋ライト if (strcmp(device_name, father_light) == 0) { if(state){ for(int i = 0; i < RETRY_COUNT; i++) { bool ret = reqestData(url_father_light_on); if(ret) break; delay(500); } Serial.println("父部屋ライトオンOK"); }else{ for(int i = 0; i < RETRY_COUNT; i++) { bool ret = reqestData(url_father_light_jyoya); if(ret) break; delay(500); } delay(1000); for(int i = 0; i < RETRY_COUNT; i++) { bool ret = reqestData(url_father_light_off); if(ret) break; delay(500); } Serial.println("父部屋ライトオフOK"); } } }); } void loop() { // put your main code here, to run repeatedly: fauxmo.handle(); } |
まぁ、仮想デバイスのトリガーが発生したらhttpリクエストを送るだけです。httpリクエストはWebHookと同義ですし、Getリクエストとなっていますが、Postを送りたければPostのメソッドを実装すれば送ることが可能です。まぁESP32なので色々やりたい放題できるでしょう。
ちなみに、父部屋の消灯の部分がurl_father_light_jyoyaを送ってからurl_father_light_offを送っていますが、このシーリングライト(NEC製)はライトオンオフが同一の赤外線のコードなので、消灯信号を送っても照明が消えていればついてしまうのです。なので、一旦常夜灯を点灯送信した上で消灯を送れば確実に消せるので、そういう仕様にしてあります。また、静的IPを取得してますがDHCPでもかまいません。あとはコードを追えば、なんとなくわかると思います。
下にESP8266側のサーバー側の抜粋を掲載します。全掲載すると長いので、照明用のサーバーの一部を掲載しておきます。getLightIrCodeはactの値でそれに該当するir_code(赤外線コード)を返す関数です。これは機種ごとに違いますし、ここで書くと長すぎますのでマイコンで赤外線リモコンを作るサイトにお任せします。
AsyncWebServerを使って、レスポンス処理をして赤外線を出すのはこんな感じで実装してます的な雰囲気のために掲載しました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
//照明用サーバー server.on("/light", HTTP_GET, [](AsyncWebServerRequest *request){ AsyncWebServerResponse *response; //GETリクエストでactパラメーターがあるかどうか if(request->hasParam("act")){ //actパラメーター取得 char act_st[20]; (request->getParam("act")->value()).toCharArray(act_st,20); //actパラメータによる赤外線コードの設定 uint64_t ir_code; bool flag; getLightIrCode(act_st,flag,ir_code); //ir_codeがアサインされていれば送信する。されていなければエラーを返す if(flag){ char act_msg[50]; sprintf(act_msg,"LIGHT: send %s",act_st); Serial.println (act_msg); response = request->beginResponse(200, "text/plain", act_msg); response->addHeader("Access-Control-Allow-Origin", "*"); request->send(response); //request->send_P(200, "text/plain", act_msg); irsendwithPro(LIGHT_IR_PRO,ir_code); }else{ Serial.println ("LIGHT: param is not assigned"); response = request->beginResponse(200, "text/plain", "LIGHT: param is not assigned"); response->addHeader("Access-Control-Allow-Origin", "*"); request->send(response); //request->send_P(200, "text/plain", "LIGHT: param is not assigned"); } }else{ //GETリクエストのactパラメータがない Serial.println("LIGHT: param is not given"); response = request->beginResponse(200, "text/plain", "LIGHT: param is not given"); response->addHeader("Access-Control-Allow-Origin", "*"); request->send(response); //request->send_P(200, "text/plain", "LIGHT: param is not given"); } }); |
あくまでも抜粋ですのでAsyncWebServerについて調べてもらえれば、情報は大量にあります。
とりあえず、今回はEPS32一つでなんとかなったので良かったことにします。またネット経由せずにLAN内で完結できるので反応が爆速になりました