[PR]記事内のアフィリエイトリンクから収入を得る場合があります

自作IoT:冬に備えてファンヒーターをリモート操作する方法

ファンヒーターにSwitchBotスイッチみたいなの 冬に備えてSwitchBotスイッチもどきを作る話。

世の中IoTが盛んでAmazonのセールなんかでも頻りにSwitchBotが少しだけ安く売られてたりしてホイホイ飛びつく人も多かろう。そこへいくと自作IoTの話はそれほど需要がなさそうで、PVなんてのを見ても対して伸びてない。だがいいのだ。

ブログなんて所詮自己満足の世界で自家発電しているようなものだ。

自家発電して虚しくなるより自作したものが家の中で活きればそのほうが楽しい。

ということで今回はSwitchBotみたいなのを作ってみた。

かなり簡単にできた。

参考にはしていないが後から調べたらSwitchBotスイッチを同じファンヒーターにつけている先駆者がいた。

SwitchBot で離れた場所から 部屋 を暖かくしてみた
...

上記の人が使っている本家SwitchBotスイッチとはこれのこと↓

B07B7NXV4R
SwitchBot スイッチボット スイッチ ボタンに適用 指ロボット スマートホーム ワイヤレス タイマー スマホで遠隔操作 Alexa, Google Home, Siri, IFTTTなどに対応(ハブ必要)

¥3,430(2022/11/30 08:02時点の価格)
平均評価点:5つ星のうち4.1
>>楽天市場で探す
>>Yahoo!ショッピングで探す

これを音声でも操作したかったら↓このSwitchBotハブも必要らしい。

B07TTH5TMW
SwitchBot スマートリモコン ハブミニ アレクサ スイッチボット Hub Mini スマートホーム 学習リモコン 赤外線家電を管理 スケジュール 遠隔操作 Alexa Google Home IFTTT Siriに対応(ホワイト)

¥3,494(2022/11/30 08:04時点の価格)
平均評価点:5つ星のうち4.1
>>楽天市場で探す
>>Yahoo!ショッピングで探す

SwitchBotハブは用途が広いがSwitchBotスイッチはただの指の代わりにしてはちょっと高すぎるんじゃね?というケチな動機から自作を決意。

スポンサーリンク

今回のゴール

  1. 室温を遠隔地から計測→使うサービスは例によってBlynkレガシー
    簡易的な補正機能も備える
  2. 石油ファンヒーターのスイッチを遠隔から押す
    応用としてサーボを増やせば温度設定ボタンも押せる
  3. 電池駆動のために電圧監視&報告

Blynkレガシーはすでに公式サービスを終了しているが自前サーバーで運用している我が家は果たして継続して使えるのか不安はあるが行けるところまで行く。

Blynk Legacyを自前サーバー構築で無料かつ使い放題スマートホーム実現の方法
...

免責について

この記事の内容を完コピして使うのは構わないが何かあっても苦情等は一切受け付けないので自己責任にて行ってほしい。

またファンヒーターのスイッチ操作に今回使うが、火を扱う機器なので本来は不在中にやるべきではない。

我が家の場合はSwitchBotカメラで遠隔からでも状態を確認しつつスイッチ操作をするという前提で、さらにすぐ家に戻れるときに限るという運用をするつもりであったがどっちみち怖いから留守中は操作しない。

フロー(チャートにはなってない)

フローチャートと呼ぶには稚拙であるが流れを大まかにお伝えする必要があるだろう。

初期化(setup)

  1.  Wi-Fiマルチ設定
    想定されるどれかのWi-Fiに接続できるようにしておく
  2. ArduinoOTA設定
    これはサンプルをコピーすればでき、あとでアップデートしたいときにいちいちパソコンと繋げなくて遠隔からできるから便利だ。
  3. DHT初期化
    温度湿度センサーの初期化でお決まりの呪文を唱える。
  4. タイマータスク設置
    Blynkライブラリのタイマーを使うので簡単。詳細はBlynkのドキュメントを参照されたい。
  5. Blynk接続
    Blynk.config()関数のパラメーターは1トークン、2ローカルサーバーのIPアドレス(同一LAN内か否かでも変わる、3ポート番号
    さらにBlynk接続時にBLYNK_CONNECTED()がパラレルで呼ばれるのでそっちにも必要な初期化コードを書いておく
  6. GPIO出力設定
    ここではサーボのGPIOピン番号を決めてOUTPUT設定するぐらいだ。

ループ(loop)

  1. AuduinoOTAポーリング
  2. Blynkポーリング
  3. タイマーポーリング
  4. Bynk接続断回数オーバーで再起動
  5. タイマーフラグが立っていたら以下処理
    5-1温度・湿度取得
    5-2 時刻取得
    5-3 温度または湿度が不正確ならMessage
    5-4 温度・湿度が取得できたら所定のVPINに書き込む
    5-5 温度がしきい値超えてたら警告送る
    5-6 生存報告ピンに時刻を書き込む
    5-7 電圧を取得して電圧ピンに書き込む

その他に3秒~5秒頻度のタイマー処理ではフラグを立てる程度の最小限の処理を行う。

準備するハード

必須部品

  • ESP32
    ESP32でもESP8266でも動くようにコードは書いてあるがおすすめは合法的なESP32だ。って書くとESP8266が違法みたいに感じるがあくまで技適未取得なんちゃらかんちゃら仮申請みたいなのを経ずに使ったら違法ということで買ってプログラムを焼くだけなら合法だ。
  • サーボSG90など
    ファンヒーターのボタンを押すだけなのでそんなにパワーがある必要はないと思うのでSG90であればいけると思う。不安ならもっと強いサーボでももちろん可。
  • 温度センサーDHT11など
    温度センサーは安いDHT11で良いだろう。モジュール化されているものを入手すれば次項の抵抗は不要。
  • 抵抗300Ω程度
    温度センサーDHT11単体で買うと抵抗を別途用意して付ける必要がある。
  • 強力な両面テープ
    ファンヒーターのスイッチ近くにサーボを貼り付ける。意外とこれ大事。

あればいい部品

  • ブレッドボード
    毎回書いているが可逆的(分解して再利用する可能性あり)に組み立てたいならこれあったほうが良い。
  • ジャンプワイヤー
    同上
  • ケース
    接触して壊れる心配がまったくないならなくても可。
  • 電源取れない場所ではモバイルバッテリー
    ファンヒーターにSwitchBotスイッチみたいなの

つなぎ方

▼これだけじゃ参考にならんかもな。

ファンヒーターにSwitchBotスイッチみたいなの

図を作るのは苦手なので表にすると

マイコンボード側のPIN制御部品の配線
5(ESP8266)
5(ESP32)
電源サーボの信号線
14(ESP8266)
14(ESP32)
温度サーボの信号線
12(ESP8266)
14(ESP32)
DHT11の信号線
VIN5Vサーボの電源線
3.3VDHT11の電源線
GNDサーボ、DHT11それぞれのGND
その他DHT11の電源線と信号線を適当な抵抗でつなぐ

スマホでの設定

Blynkのウィジェット画面上から説明すると

延長=電源サーボの左回転で運転延長する

電源=電源サーボの右回転でON/OFFする

温度↓=温度サーボの左回転
温度↑=同じく右回転

2個のゲージの左が室温度、右が湿度、といってもケースの中で基板と一緒だからその影響もかなり受けると思う。

その下の折れ線グラフはボードの電圧監視。ESP8266は特に分圧しなくとも電圧測定ができるようで一応載せている。用途としてはバッテリー駆動させたときの降下警告できたらなあという程度。

数字を入れるボックスは温度の補正値を入れる。その右の決定ボタンで決定される。

グラフの下の数値入力はまず温度補正用。DHT11というセンサーはあまり正確に温度測定できないっぽいので実温度に合わせて補正をかけられるようにした。

その下の6個の数値入力ボックスは左上から

  • 電源サーボ左トリム
  • 電源サーボ右トリム
  • 電源サーボニュートラル角度
  • 温度サーボニュートラル角度
  • 温度サーボ左トリム
  • 温度サーボ右トリム

トリムというボックスに角度値を入れてちょうどよいボタン操作の強さを決める仕組みだ。

ハードの取り付け

両面テープで貼り付けてから回転角度の調節をしてもよい。っていうかそのつもりでプログラムも作った。

なぜなら貼り付ける位置の微妙な違いによってスイッチを押すまでのサーボ角度が変わってくるから。

そういう調整もプログラムとスマホアプリのウィジェットでできるようにした。

ファンヒーターにSwitchBotスイッチみたいなの

プログラムコード(無保証)

意味のわからないところがあったらtwitterでつぶやいてくれれば答えられるものは答えます。ちゃんと動かないんだけど?というのは無しで。

まあ誰も読まんだろうけど。


const char* version_info = "V0.0.1";
#define WIFIMULTI
#define IrqInterval 5000L //タイマー割り込み間隔定義 最後にLつける

#include "personal.h"
#include "ESP8266ESP32.h"
#include  <WiFiUdp.h>
#include  <ArduinoOTA.h>
#if defined(ESP8266)
#include  <ESP8266mDNS.h>
#include <ESP8266WiFiMulti.h>
#else
#include <WiFiMulti.h>
#endif

// ADC_MODE 参考URL
// https://intellectualcuriosity.hatenablog.com/entry/2018/02/22/145715
#define LIMIT_VOLTAGE 2.8
#if defined(ESP8266)
ADC_MODE(ADC_VCC);
#endif

// for TIME
#define JST    3600* 9
void DisplayTime(char *);
BlynkTimer timer;
bool checkTimerTask = true;

// for Blynk communication
int BlynkDisconnect = 0;
#define VPIN_NOTICE         V0
#define VPIN_ALIVE          V2
#define VPIN_NUTRAL_PWR     V3    // 電源中立位置角度再定義用
#define VPIN_NUTRAL_TMP     V4    // 温度中立位置角度再定義用
#define VPIN_PWR_SERVO_R    V5    // サーボ右回転
#define VPIN_PWR_SERVO_L    V6    // サーボ左回転
#define VPIN_PWR_R_TRIM     V7
#define VPIN_PWR_L_TRIM     V8
#define VPIN_TMP_SERVO_R    V9    // 温度上げる
#define VPIN_TMP_SERVO_L    V10   // 温度下げる
#define TEMP_PIN            V12   //温度通知バーチャルピン
#define HUMI_PIN            V13   //湿度通知バーチャルピン
#define VPIN_CRCT           V14   // 温度補正
#define VPIN_CRCT_OK        V15   // 温度補正決定
#define VPIN_VOLT           V16   // 電圧
#define VPIN_TMP_R_TRIM     V17
#define VPIN_TMP_L_TRIM     V18

int notice_flug = 0;

// for servo
int POWER_NUTRAL_DEGREE = 80; // 中立位置角度
int TEMPE_NUTRAL_DEGREE = 80; // 中立位置角度
int PWR_R_DEG, PWR_L_DEG;
int TMP_R_DEG, TMP_L_DEG;

// for NetWork communication
const char THIS_HOST_NAME[] = "FANHEATER";
const char *DeviceName = THIS_HOST_NAME;
const char* SSID_NAME = ssid_name[0];
const char* PASSWORD = passwords[0];
#if defined(ESP8266)
ESP8266WiFiMulti  wifiMulti;
#else
WiFiMulti  wifiMulti;
#endif

// GPIO
#if defined(ESP8266)
#define servoPIN_0     5  //D6 8266
#define servoPIN_1    14  //D5
#define DHTPIN  D6  // 8266 GPIO5
#else
#define servoPIN_0     5
#define servoPIN_1    14
#define DHTPIN  18
#endif

// for 温度測定
#define DHTTYPE DHT11       // DHT型の指定
DHT dht(DHTPIN, DHTTYPE);   // DHTセンサーライブラリーを使うための設定
float TEMP_CORRECT = 0;       // 温度補正
float TEMP_CORRECT_TEMP = 0;  // 温度補正決定までの一時保管

const String mes[6] = {
  "通知ON切替",
  "温度補正値変更",
  "サーボ切替",
  "読み取り失敗",
  "読み取り成功",
  "室温が高すぎです"};

void timerTask()
{
  float temperature = dht.readTemperature(); // 温度読み取り
  float humidity = dht.readHumidity();   // 湿度読み取り
  char str[13];char str2[20];

  DisplayTime(str);

  // 読み取りに失敗しているかチェック
  if (isnan(temperature) || isnan(humidity)) {
    Serial.println("Failed to read from DHT sensor!");
    sprintf(str2, "%s", mes[3]);
    Serial.println(str2);
    Blynk.setProperty(VPIN_ALIVE,"onLabel", str2);
  }else{
    sprintf(str2, "%s", mes[4]);
    Blynk.setProperty(VPIN_ALIVE,"onLabel", str2);
    delay(10);
    // TEMP_PIN ピンに対して温度の値を送信 2022-08-15 補正値付加
    Blynk.virtualWrite(TEMP_PIN, temperature + TEMP_CORRECT);
    delay(10);
    Blynk.virtualWrite(HUMI_PIN, humidity);
  }
  delay(10);
  sprintf(str2, "%s %s", version_info, str);
  
  if(temperature > 40){
    //危険
    Blynk.notify(mes[5]);
    delay(10);
    Blynk.email(YOUR_MAIL_ADDRESS, "Subject: 温度上昇", mes[5]);
    delay(10);
  }

  // 生存報告
  Blynk.setProperty(VPIN_ALIVE,"offLabel", str2);
  delay(100);
  #if defined(ESP8266)
  float vcc = ESP.getVcc() / 1024.0;
  Blynk.virtualWrite(VPIN_VOLT,vcc);
  #endif
}

void my_pwm(int port, int v) {
 for (int i=0;i<40;i++) {
   digitalWrite(port, HIGH);
   delayMicroseconds(v);
   digitalWrite(port, LOW);
   delayMicroseconds(10000);
   delayMicroseconds(10000 - v);
 }
}
void calcPWM(int port,int v)
{
  my_pwm(port,v*10.4+500);
}
void PowerServoNutral()
{
  int deg = POWER_NUTRAL_DEGREE;
  calcPWM(servoPIN_0,deg);
}
void TempeServoNutral()
{
  int deg = POWER_NUTRAL_DEGREE;
  calcPWM(servoPIN_1,deg);
}
#if 1
BLYNK_CONNECTED(){
  
  Blynk.syncAll();
  PowerServoNutral();
  TempeServoNutral();
  Blynk.syncVirtual(VPIN_PWR_R_TRIM);
  Blynk.syncVirtual(VPIN_PWR_L_TRIM);
}
BLYNK_WRITE(VPIN_ALIVE)
{
  if(param.asInt()){
//    ESP.restart();
  }
}
BLYNK_WRITE(VPIN_NOTICE)
{
  notice_flug = param.asInt();
  if(notice_flug){
    Blynk.notify(DeviceName + mes[0]);
  }
}
#endif
BLYNK_WRITE(VPIN_NUTRAL_PWR)
{
  POWER_NUTRAL_DEGREE = param.asInt();
  calcPWM(servoPIN_0,POWER_NUTRAL_DEGREE);
  Serial.printf("VPIN_NUTRAL_PWR servo %d\n", POWER_NUTRAL_DEGREE);
}
BLYNK_WRITE(VPIN_NUTRAL_TMP)
{
  TEMPE_NUTRAL_DEGREE = param.asInt();
  calcPWM(servoPIN_1,TEMPE_NUTRAL_DEGREE);
  Serial.printf("VPIN_NUTRAL_TMP servo %d\n", TEMPE_NUTRAL_DEGREE);
}
BLYNK_WRITE(VPIN_PWR_SERVO_R){ //電源サーボ右制御
  int hmov = param.asInt();
  if(hmov){
    int deg = POWER_NUTRAL_DEGREE + PWR_R_DEG;
    calcPWM(servoPIN_0,deg);
    Serial.printf("VPIN_PWR_SERVO_R servo %d\n", deg);
    PowerServoNutral();
    if(notice_flug){
      Blynk.notify(DeviceName + mes[2]);
    }
  }
}
BLYNK_WRITE(VPIN_TMP_SERVO_R){ //温度サーボ右制御
  int hmov = param.asInt();
  if(hmov){
    int deg = TEMPE_NUTRAL_DEGREE + TMP_R_DEG;
    calcPWM(servoPIN_1,deg);
    Serial.printf("VPIN_TMP_SERVO_R servo %d\n", deg);
    TempeServoNutral();
    if(notice_flug){
      Blynk.notify(DeviceName + mes[2]);
    }
  }
}
BLYNK_WRITE(VPIN_PWR_SERVO_L){ //電源サーボ延長制御
  int hmov = param.asInt();
  if(hmov){
    int deg = POWER_NUTRAL_DEGREE + PWR_L_DEG;
    calcPWM(servoPIN_0,deg);
    Serial.printf("VPIN_PWR_SERVO_L servo %d\n", deg);
    PowerServoNutral();
    if(notice_flug){
      Blynk.notify(DeviceName + mes[2]);
    }
  }
}
BLYNK_WRITE(VPIN_TMP_SERVO_L){ //温度サーボ左制御
  int hmov = param.asInt();
  if(hmov){
    int deg = TEMPE_NUTRAL_DEGREE + TMP_L_DEG;
    calcPWM(servoPIN_1,deg);
    Serial.printf("VPIN_TMP_SERVO_L servo %d\n", deg);
    TempeServoNutral();
    if(notice_flug){
      Blynk.notify(DeviceName + mes[2]);
    }
  }
}
BLYNK_WRITE(VPIN_PWR_R_TRIM)
{
  PWR_R_DEG = param.asInt();
  Serial.printf("VPIN_PWR_R_TRIM servo %d\n", PWR_R_DEG);
}
BLYNK_WRITE(VPIN_TMP_R_TRIM)
{
  TMP_R_DEG = param.asInt();
  Serial.printf("VPIN_TMP_R_TRIM servo %d\n", TMP_R_DEG);
}
BLYNK_WRITE(VPIN_PWR_L_TRIM)
{
  PWR_L_DEG = param.asInt();
  Serial.printf("VPIN_PWR_L_TRIM servo %d\n", PWR_L_DEG);
}
BLYNK_WRITE(VPIN_TMP_L_TRIM)
{
  TMP_L_DEG = param.asInt();
  Serial.printf("VPIN_TMP_L_TRIM servo %d\n", TMP_L_DEG);
}
BLYNK_WRITE(VPIN_CRCT_OK)
{
  if(param.asInt()){
    Blynk.syncVirtual(VPIN_CRCT);
    TEMP_CORRECT = TEMP_CORRECT_TEMP;
    Blynk.notify(DeviceName + mes[1]);
  }
}
BLYNK_WRITE(VPIN_CRCT)
{
  TEMP_CORRECT_TEMP = param.asInt();
}
void checkBlynkStatus() { // called every 3 seconds by SimpleTimer

  bool isconnected = Blynk.connected();
  if (isconnected == false) {
    BlynkDisconnect ++;
    Serial.printf("Blynk disconnect :%d\n",BlynkDisconnect);
    Blynk.config(AUTH, IPAddress(192,168,0,182), 8080);
  }
  if (isconnected == true) {
    BlynkDisconnect = 0;
    digitalWrite(LED_BUILTIN, HIGH); //Turn on WiFi LED
    checkTimerTask = true;
  }
}
void setup()
{
  Serial.begin(115200);
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(LED_BUILTIN, OUTPUT);

  //////  Wi-Fi接続処理 //////
  // Wi-Fi接続
  WiFi.disconnect();
  WiFi.mode(WIFI_STA);
#if defined(WIFIMULTI)
  for(int i = 0;i<ROUTERS;i++){ wifiMulti.addAP(ssid_name[i], passwords[i]); // add Wi-Fi networks you want to connect to } int i = 0; while (wifiMulti.run() != WL_CONNECTED) { // Wait for the Wi-Fi to connect delay(250); Serial.printf("%d ",i); i++; if(i>20)ESP.restart();
  }
#else
  WiFi.begin(SSID_NAME, PASSWORD);
  int i = 0;
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    i++;  Serial.print(i);
    if (i >= 20) ESP.restart();
  }  // Wi-Fi接続ここまで
#endif

/////// Arduino OTA 設定
  // Port defaults to 8266
  // ArduinoOTA.setPort(8266);

  // Hostname defaults to esp8266-[ChipID]
  ArduinoOTA.setHostname(THIS_HOST_NAME);

  // No authentication by default
  //  ArduinoOTA.setPassword(DEVICEPASSWORD);

  // Password can be set with it's md5 value as well
  // MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
  // ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");

    Serial.println("Succesfully Connected!!!");
    configTime( JST, 0, "ntp.nict.jp", "ntp.jst.mfeed.ad.jp");

  // ArduinoOTA special script -----------------------------------------    
  ArduinoOTA.onStart([]() {
    String type;
    if (ArduinoOTA.getCommand() == U_FLASH) {
      type = "sketch";
    } else { // U_FS
      type = "filesystem";
    }

    // NOTE: if updating FS this would be the place to unmount FS using FS.end()
    Serial.println("Start updating " + type);
  });
  ArduinoOTA.onEnd([]() {
    Serial.println("\nEnd");
  });
  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
    Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
  });
  ArduinoOTA.onError([](ota_error_t error) {
    Serial.printf("Error[%u]: ", error);
    if (error == OTA_AUTH_ERROR) {
      Serial.println("Auth Failed");
    } else if (error == OTA_BEGIN_ERROR) {
      Serial.println("Begin Failed");
    } else if (error == OTA_CONNECT_ERROR) {
      Serial.println("Connect Failed");
    } else if (error == OTA_RECEIVE_ERROR) {
      Serial.println("Receive Failed");
    } else if (error == OTA_END_ERROR) {
      Serial.println("End Failed");
    }
  });
  ArduinoOTA.begin();
  Serial.println("Ready");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
  // ArduinoOTA special script ここまで -------------------------    
    
  dht.begin();
  timer.setInterval(IrqInterval, checkBlynkStatus); // check if Blynk server is connected every 3 seconds
  Blynk.config(AUTH, SERVER_IP, 8080); // Blynkトークン, サーバーのIPアドレス, ポート
  led_blink(50,50,20,LED_BUILTIN);
  pinMode(servoPIN_0, OUTPUT);   //
  pinMode(servoPIN_1, OUTPUT);   //
}
void loop()
{
  ArduinoOTA.handle();
  if (WiFi.status() != WL_CONNECTED)
  {
    Serial.println("WiFi Not Connected");
  }  else  {
    Blynk.run();
  }

  timer.run(); // Initiates SimpleTimer
  if(BlynkDisconnect >=10){
    WiFi.disconnect(true);
    delay(50);
    Serial.println("Restart");
    ESP.restart();
  }
  if(checkTimerTask){
    // タイマーで3秒ごとにフラグが立っていたら温度計測と生存報告
    checkTimerTask = false;
    timerTask();
    led_blink(10,10,1,LED_BUILTIN);
  }
}
void led_blink(int OnMsec,int OffMsec,int n,int PORT){
  for(int i=0;i<n;i++){
      digitalWrite(PORT, LOW);   // turn the LED on (HIGH is the voltage level)
      delay(OnMsec);                       // wait for a second
      digitalWrite(PORT, HIGH);    // turn the LED off by making the voltage LOW
      delay(OffMsec);                       // wait for a second
  }
}

スマートスピーカーと連携

  1. Google Assistant Node-RED Bridgeに登録する
    Node-RED Google Assistant Bridge
    Node-RED GoogleAssistant Bridgeにデバイスを登録する
  2. 同じくAlexa Node-RED Bridgeに登録する
    Node-RED Alexa Home Skill Bridge
    Node-RED Alexa Bridgeにデバイスを登録する
  3. 自分ちのサーバーで動いているNode-redに上記デバイスをデプロイする
    Node-redで機器の動きを登録
  4. Google、アレクサそれぞれのスマホアプリを開けば新規としてデバイスが出てくるはずなのでシーンだとかルーティンだとかに登録する。
    例として「アレクサ(またはOKGoogle)ファンヒーターをつけて」と言ったらFanHeaterPowerが作動するというような感じ。

実際の動き

適当にパジャマ姿で動画を撮ってしまって恥ずかしいが興味があれば確認してみてほしい。

まとめ

まとうめようにもまとめようがないが、サーボが余っていたのに何も使わないのはもったいないからリモートでファンヒーターをONやOFFできるようにした。

エアコンと違ってファンヒーターは直接火を使って暖房する器具だからリモートで動かすっていうのは不在中には絶対やらないほうがいい。

したがって実際の運用はリビングルームでまったりしているときに

「ちょっと寒くなってきたなあ、でもヒーターつけに立ち上がるのだるいなあ」

というシチュエーションで役に立つはず。間違っても留守中に温度を監視して「猫ちゃんが寒かろう」とつけてはならない。そういうときはエアコンをつけよう。

とにかく手ぶらで声だけでファンヒーターをつけられるようにしたので久しぶりにIoT機器が完成して満足感が多少ある。

やっぱり自分にはこんなの自作は無理~という方は↓これがおすすめです。

B07B7NXV4R
SwitchBot スイッチボット スイッチ ボタンに適用 指ロボット スマートホーム ワイヤレス タイマー スマホで遠隔操作 Alexa, Google Home, Siri, IFTTTなどに対応(ハブ必要)

¥3,430(2022/11/30 08:02時点の価格)
平均評価点:5つ星のうち4.1
>>楽天市場で探す
>>Yahoo!ショッピングで探す

B07TTH5TMW
SwitchBot スマートリモコン ハブミニ アレクサ スイッチボット Hub Mini スマートホーム 学習リモコン 赤外線家電を管理 スケジュール 遠隔操作 Alexa Google Home IFTTT Siriに対応(ホワイト)

¥3,494(2022/11/30 08:04時点の価格)
平均評価点:5つ星のうち4.1
>>楽天市場で探す
>>Yahoo!ショッピングで探す

タイトルとURLをコピーしました