0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SwitchBot プラグMiniの電源のON/OFF状態を見れるExporter作ってみた

Last updated at Posted at 2025-07-06

はじめに

みなさん初めまして.今回は自分の研究室の環境で使用しているSwitchBot プラグMiniのON/OFF状態をみることができるexporterを開発したのでどんなものなのかの概要や使い方を説明していきたいと思います.

概要

SwitchBot プラグミニは,家庭の家電をカンタンにスマート化できるコンパクトなプラグ型デバイスです.できることとしてはコンセントと対象機器の間に設置し電源をONするのかOFFにするのかの切り替えができます.
私の所属する研究室の環境ではESXiがインストールされた10台の物理マシンがありそのうちの6台(Mint,Rose,Jasmine,Lotus,Violet,Plum)に取り付けられています.
image.png
6台は研究室の学生が自由にVMを作成して実験できるマシンです.使用している学生がいない時間にSwichBot プラグMiniが電源をOFFにします.

現在私たちの研究室で動いているアルゴリズムは.CDSLの山野倖平さんが作成したものになります.

詳しいアルゴリズムを知りたい方がいれば以下のURLから見てみてください!⇩

今回はこのアルゴリズムが動いている前提で話を進めていきます.

環境

  • Ubuntu 24.04.1 LTS
  • Python 3.10.12

ライブラリ

  • Flask==2.3.3
  • requests==2.31.0
  • prometheus_client

ファイル構成

プロジェクトのファイルは以下にあります.
https://github.com/cdsl-research/switchbot-exporter

switchbot-exporter
├── app.py →exporterの実行ファイル
├── Dockerfile
└── requirements.txt →Pythonライブラリインストール用

使い方

ファイル内は以下のようになっています.埋める箇所はswitchbot_targetsheadersのAPIトークンです.

from flask import Flask, Response
from prometheus_client import Gauge, generate_latest
import requests

app = Flask(__name__)

# デバイス情報(名前はユニークに)
switchbot_targets = {
    "mint": "$ID",
    "rose": "$ID",
    "jasmine": "$ID",
    "plum": "$ID",
    "lotus": "$ID",
    "violet": "$ID"
}

# Prometheus用のメトリクス定義
power_gauge = Gauge('switchbot_power_status', 'Power status of SwitchBot device (1=on, 0=off)', ['device_name'])

# SwitchBot APIトークン
headers = {
    "Authorization": "$APIトークン"
}

def update_metrics():
    for device_name, device_id in switchbot_targets.items():
        url = f"https://api.switch-bot.com/v1.0/devices/{device_id}/status"
        try:
            response = requests.get(url, headers=headers)
            body = response.json().get("body", {})
            power = body.get("power", None)

            if power == "on":
                power_gauge.labels(device_name=device_name).set(1)
            elif power == "off":
                power_gauge.labels(device_name=device_name).set(0)
            else:
                power_gauge.labels(device_name=device_name).set(-1)  # 異常系
        except Exception as e:
            print(f"{device_name}: Error fetching status: {e}")
            power_gauge.labels(device_name=device_name).set(-1)  # エラー

@app.route("/metrics")
def metrics():
    update_metrics()
    return Response(generate_latest(), mimetype="text/plain")

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=9080)

1. cdで移動

monitoring@monitoring-master-ml:~$ cd switchbot-exporter/
monitoring@monitoring-master-ml:~/switchbot-exporter$ 

2. switchbot_targetsの確認

以下のようにcurlコマンドを使って出力を確認します.その際に$TokenにSwitchbotのAPI用のTokenが必要です.

monitoring@monitoring-master-ml:~/switchbot-exporter$ curl -X GET "https://api.switch-bot.com/v1.0/devices" \
  -H "Authorization: $Token"
monitoring@monitoring-master-ml:~/

3. switchbot_targetsをapp.pyに入れる

先ほど調べたIDをapp.pyの以下の箇所に入れます.

switchbot_targets = {
    "mint": "$ID",
    "rose": "$ID",
    "jasmine": "$ID",
    "plum": "$ID",
    "lotus": "$ID",
    "violet": "$ID"
}

4. Tokenの入力

app.py内のheadersの変数の箇所にcurlでも使用したAPIトークンを入れます.

headers = {
    "Authorization": "$APIトークン"
}

5. 仮想環境の作成と有効化
以下のコマンドを実行

monitoring@monitoring-master-ml:~/switchbot-exporter$ python3 -m venv switchbot-exporter
monitoring@monitoring-master-ml:~/switchbot-exporter$ source switchbot-exporter/bin/activate
(switchbot-exporter) monitoring@monitoring-master-ml:~/switchbot-exporter$ 

6. 必要なライブラリのインストール

pip installでrequirements.txtを指定する.

(switchbot-exporter) monitoring@monitoring-master-ml:~/switchbot-exporter$ pip install -r requirements.txt
Collecting Flask==2.3.3 (from -r requirements.txt (line 1))
  Downloading flask-2.3.3-py3-none-any.whl.metadata (3.6 kB)
Collecting requests==2.31.0 (from -r requirements.txt (line 2))
  Downloading requests-2.31.0-py3-none-any.whl.metadata (4.6 kB)
Collecting prometheus_client (from -r requirements.txt (line 3))
  Downloading prometheus_client-0.22.1-py3-none-any.whl.metadata (1.9 kB)
Collecting Werkzeug>=2.3.7 (from Flask==2.3.3->-r requirements.txt (line 1))
  Using cached werkzeug-3.1.3-py3-none-any.whl.metadata (3.7 kB)
Collecting Jinja2>=3.1.2 (from Flask==2.3.3->-r requirements.txt (line 1))
  Using cached jinja2-3.1.6-py3-none-any.whl.metadata (2.9 kB)
Collecting itsdangerous>=2.1.2 (from Flask==2.3.3->-r requirements.txt (line 1))
  Using cached itsdangerous-2.2.0-py3-none-any.whl.metadata (1.9 kB)
Collecting click>=8.1.3 (from Flask==2.3.3->-r requirements.txt (line 1))
  Using cached click-8.2.1-py3-none-any.whl.metadata (2.5 kB)
Collecting blinker>=1.6.2 (from Flask==2.3.3->-r requirements.txt (line 1))
  Using cached blinker-1.9.0-py3-none-any.whl.metadata (1.6 kB)
Collecting charset-normalizer<4,>=2 (from requests==2.31.0->-r requirements.txt (line 2))
  Using cached charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (35 kB)
Collecting idna<4,>=2.5 (from requests==2.31.0->-r requirements.txt (line 2))
  Using cached idna-3.10-py3-none-any.whl.metadata (10 kB)
Collecting urllib3<3,>=1.21.1 (from requests==2.31.0->-r requirements.txt (line 2))
  Using cached urllib3-2.5.0-py3-none-any.whl.metadata (6.5 kB)
Collecting certifi>=2017.4.17 (from requests==2.31.0->-r requirements.txt (line 2))
  Using cached certifi-2025.6.15-py3-none-any.whl.metadata (2.4 kB)
Collecting MarkupSafe>=2.0 (from Jinja2>=3.1.2->Flask==2.3.3->-r requirements.txt (line 1))
  Using cached MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (4.0 kB)
Downloading flask-2.3.3-py3-none-any.whl (96 kB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 96.1/96.1 kB 9.1 MB/s eta 0:00:00
Downloading requests-2.31.0-py3-none-any.whl (62 kB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 62.6/62.6 kB 15.3 MB/s eta 0:00:00
Downloading prometheus_client-0.22.1-py3-none-any.whl (58 kB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 58.7/58.7 kB 16.7 MB/s eta 0:00:00
Using cached blinker-1.9.0-py3-none-any.whl (8.5 kB)
Using cached certifi-2025.6.15-py3-none-any.whl (157 kB)
Using cached charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (148 kB)
Using cached click-8.2.1-py3-none-any.whl (102 kB)
Using cached idna-3.10-py3-none-any.whl (70 kB)
Using cached itsdangerous-2.2.0-py3-none-any.whl (16 kB)
Using cached jinja2-3.1.6-py3-none-any.whl (134 kB)
Using cached urllib3-2.5.0-py3-none-any.whl (129 kB)
Using cached werkzeug-3.1.3-py3-none-any.whl (224 kB)
Using cached MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (23 kB)
Installing collected packages: urllib3, prometheus_client, MarkupSafe, itsdangerous, idna, click, charset-normalizer, certifi, blinker, Werkzeug, requests, Jinja2, Flask
Successfully installed Flask-2.3.3 Jinja2-3.1.6 MarkupSafe-3.0.2 Werkzeug-3.1.3 blinker-1.9.0 certifi-2025.6.15 charset-normalizer-3.4.2 click-8.2.1 idna-3.10 itsdangerous-2.2.0 prometheus_client-0.22.1 requests-2.31.0 urllib3-2.5.0
(switchbot-exporter) monitoring@monitoring-master-ml:~/switchbot-exporter$ 

7. app.pyの実行
以下のコマンドを入力しapp.pyを実行

(switchbot-exporter) monitoring@monitoring-master-ml:~/switchbot-exporter$ python3 app.py 
 * Serving Flask app 'app'
 * Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on all addresses (0.0.0.0)
 * Running on http://127.0.0.1:9080
 * Running on http://192.168.100.76:9080
Press CTRL+C to quit

8. 動作確認

以下のようにswitchbot_power_statusのメトリクスが見れればOK!

(switchbot-exporter) monitoring@monitoring-master-ml:~/switchbot-exporter$ curl http://monitoring-master-ml:9080/metrics
# HELP python_gc_objects_collected_total Objects collected during gc
# TYPE python_gc_objects_collected_total counter
python_gc_objects_collected_total{generation="0"} 275.0
python_gc_objects_collected_total{generation="1"} 257.0
python_gc_objects_collected_total{generation="2"} 0.0
# HELP python_gc_objects_uncollectable_total Uncollectable objects found during GC
# TYPE python_gc_objects_uncollectable_total counter
python_gc_objects_uncollectable_total{generation="0"} 0.0
python_gc_objects_uncollectable_total{generation="1"} 0.0
python_gc_objects_uncollectable_total{generation="2"} 0.0
# HELP python_gc_collections_total Number of times this generation was collected
# TYPE python_gc_collections_total counter
python_gc_collections_total{generation="0"} 86.0
python_gc_collections_total{generation="1"} 7.0
python_gc_collections_total{generation="2"} 0.0
# HELP python_info Python platform information
# TYPE python_info gauge
python_info{implementation="CPython",major="3",minor="8",patchlevel="20",version="3.8.20"} 1.0
# HELP process_virtual_memory_bytes Virtual memory size in bytes.
# TYPE process_virtual_memory_bytes gauge
process_virtual_memory_bytes 1.12848896e+08
# HELP process_resident_memory_bytes Resident memory size in bytes.
# TYPE process_resident_memory_bytes gauge
process_resident_memory_bytes 3.6237312e+07
# HELP process_start_time_seconds Start time of the process since unix epoch in seconds.
# TYPE process_start_time_seconds gauge
process_start_time_seconds 1.75178868764e+09
# HELP process_cpu_seconds_total Total user and system CPU time spent in seconds.
# TYPE process_cpu_seconds_total counter
process_cpu_seconds_total 0.31
# HELP process_open_fds Number of open file descriptors.
# TYPE process_open_fds gauge
process_open_fds 6.0
# HELP process_max_fds Maximum number of open file descriptors.
# TYPE process_max_fds gauge
process_max_fds 1.048576e+06
# HELP switchbot_power_status Power status of SwitchBot device (1=on, 0=off)
# TYPE switchbot_power_status gauge
switchbot_power_status{device_name="mint"} 1.0
switchbot_power_status{device_name="rose"} 0.0
switchbot_power_status{device_name="jasmine"} 1.0
switchbot_power_status{device_name="plum"} 1.0
switchbot_power_status{device_name="lotus"} 1.0
switchbot_power_status{device_name="violet"} 1.0
(switchbot-exporter) monitoring@monitoring-master-ml:~/switchbot-exporter$ 

switchbot_power_statusのメトリクスの値が1.0の時は対象機器に電源が供給されていることを示します.0.0は供給されていないことを示します.

switchbot_power_status{device_name="mint"} 1.0
switchbot_power_status{device_name="rose"} 0.0
switchbot_power_status{device_name="jasmine"} 1.0
switchbot_power_status{device_name="plum"} 1.0
switchbot_power_status{device_name="lotus"} 1.0
switchbot_power_status{device_name="violet"} 1.0

終わりに

今回はSwitchBot プラグMiniの電源のON/OFF状態を見れるexporterを開発しました.まだまだ改善するところが多いので今後も修正を加えて使いやすくしていきます!

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?