
SwitchBot 温度計の計測値を CloudWatch メトリクスにプッシュしてみた
はじめに
こんにちは、アノテーション テクニカルサポートエンジニアのかねこです。
私の家では SwitchBot の製品を複数導入しています。ハブ をはじめとして、ロボット掃除機 、ペット見守りカメラ 等を使っていて、 Amazon Alexa と連携させながらスマートホーム機能を活用しています。
ところで、先日 SwitchBot アプリから温湿度計 の履歴を確認していたところ、以下のようにとある期間のメトリクスがごっそり消えてしまっていることに気づきました。
この期間に温湿度計は正常に動作しており、少なくともゴールデンウィークに確認したときにはメトリクスは残っていたと記憶しています。
この事象の原因は不明ですが、これをきっかけに、 CloudWatch メトリクスにデータを送信することで、より長い期間メトリクスデータを保持したり、より柔軟に他のアプリと連携できないかと考えるようになりました。
やってみた
構成
EventBridge スケジューラで Lambda 関数を毎分起動するように設定し、 Lambda 関数では Secrets Manager から SwitchBot API 実行のための必要情報を取得、 SwitchBot OpenAPI から計測データを取得の上、 CloudWatch メトリクスにメトリクスデータとして登録します。
以降、 JavaScript のサンプルコードを示しますが、 Node.js のバージョンは v22 で動作を確認しています。
SwitchBot トークンとシークレットキーを取得する
まずは、 SwitchBot OpenAPI を実行するためのトークンとシークレットキーを取得します。
ドキュメント にも記載されていますが、 SwitchBot のアプリから以下の手順でトークンとシークレットキーを取得できます。
(ドキュメントより機械翻訳)
以下の手順に従ってください。App StoreまたはGoogle Play StoreでSwitchBotアプリをダウンロードしてください
SwitchBotアカウントを登録し、アカウントにログインします
アプリ内でオープントークンを生成します。アプリバージョンがV9.0以上の場合、a) プロフィール > 設定 > バージョン情報に移動します。b) アプリバージョンを10回タップします。開発者向けオプションが表示されます。c) 開発者向けオプションをタップします。d) トークンを取得をタップします。
アプリバージョンがV9.0未満の場合、a) プロフィール > 設定へ進み、b) アプリバージョンを10回タップします。開発者向けオプションが表示されます。c) 開発者向けオプションをタップし、d) トークンを取得をタップします。
袖をまくって、SwitchBot OpenAPI を使ってみましょう。
SwitchBot OpenAPI から計測データを取得する
最初に、SwitchBot OpenAPI にリクエストを発行する際は、トークンとシークレットキーのほかに、トークンとタイムスタンプをもとに作成した署名を添えて発行する必要があります。
import { createHmac } from 'crypto';
function createSignature(apiToken, apiSecretKey) {
const t = Date.now();
const nonce = "requestID";
const data = apiToken + t + nonce;
const sign = createHmac('sha256', apiSecretKey).update(data).digest('base64');
return { sign, nonce, t }; // リクエスト発行時に添える署名
}
また、計測データを取得するためには、取得したい温湿度計センサーのデバイス ID を控えておく必要があります。
/devices
API で対象のデバイス ID を取得しましょう。
curl
で取得しても良いかもしれませんが、署名の計算が面倒なのでスクリプトで取得します。
// getDevices.js
const { sign, nonce, t } = createSignature();
const res = await fetch(`https://api.switch-bot.com/1.1/devices`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': API_TOKEN,
'sign': sign,
'nonce' : nonce,
't': t,
}
});
const text = await res.text();
const json = JSON.parse(text);
console.log(JSON.stringify(json, null, ' '));
スクリプトを実行すると、以下のようにデバイス ID を確認することができます。
$ node getDevices.js
{
"statusCode": 100,
"body": {
"deviceList": [
{
"deviceId": "ABCDEFGHIJKL",
"deviceName": "CO2センサー(温湿度計) 84",
"deviceType": "MeterPro(CO2)",
"enableCloudService": true,
"hubDeviceId": "111111111111"
},
{
"deviceId": "ABCDEFGHIJKL",
"deviceName": "防水温湿度計 66",
"deviceType": "WoIOSensor",
"enableCloudService": true,
"hubDeviceId": "111111111111"
},
:
}
}
次に、取得したデバイス ID をもとに、温度計の計測値を SwitchBot OpenAPI の /devices/{deviceId}/status
から取得します。
// getMetrics.js
const { sign, nonce, t } = createSignature(apiToken, apiSecretKey);
const deviceId = 'ABCDEFGHIJKL';
const response = await fetch(`https://api.switch-bot.com/1.1/devices/${deviceId}/status`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': apiToken,
'sign': sign,
'nonce': nonce,
't': t,
}
});
const data = await response.json();
console.log(JSON.stringify(data, null, ' '));
スクリプトを実行すると、以下のように温湿度計の温度、湿度、バッテリー残量を取得することができました。
同様にして、 CO2 センサー の CO2 濃度、温度、湿度も取得できるので、今回はこれら 6 つのメトリクスを CloudWatch メトリクスにプッシュしてみようと思います。
$ node getMetrics.js
{
"statusCode": 100,
"body": {
"version": "V0.9",
"temperature": 16.5,
"battery": 60,
"humidity": 58,
"deviceId": "ABCDEFGHIJKL",
"deviceType": "WoIOSensor",
"hubDeviceId": "111111111111"
},
"message": "success"
}
CloudWatch メトリクスに計測データをプッシュする
これまで取得したトークン、シークレットキー、デバイス ID を Lambda 関数から参照するため、 Secrets Manager シークレットに登録しておきます。
Lambda 関数内で、 Secrets Manager の GetSecretValue
コマンドを実行して SwitchBot OpenAPI の実行に必要な情報を取得し、 CloudWatch の PutMetricData
コマンドを使用して、取得した計測データを CloudWatch メトリクスにプッシュします。
メトリクス名、ディメンション、ユニットは適当にお好みのものをつけると良いです。
// index.mjs (一部抜粋)
export const handler = async (event) => {
try {
console.log('Starting SwitchBot metrics collection...');
// Secrets Managerからシークレットを取得
const secrets = await getSecrets();
const { apiToken, apiSecretKey, outsideDeviceId, insideDeviceId } = secrets;
// 屋外センサーのデータ取得
console.log('Fetching outside sensor data...');
const outsideData = await getSensorData(outsideDeviceId, apiToken, apiSecretKey);
console.log('Outside sensor data:', outsideData);
// 室内センサーのデータ取得
console.log('Fetching inside sensor data...');
const insideData = await getSensorData(insideDeviceId, apiToken, apiSecretKey);
console.log('Inside sensor data:', insideData);
// CloudWatchメトリクスデータの準備
const timestamp = new Date();
const metricData = [];
// 屋外センサーのメトリクス
if (outsideData) {
metricData.push(
{
MetricName: 'Battery',
Dimensions: [
{
Name: 'Location',
Value: 'Outside'
},
{
Name: 'DeviceId',
Value: outsideDeviceId
}
],
Value: outsideData.battery,
Unit: 'Percent',
Timestamp: timestamp
},
{
MetricName: 'Temperature',
Dimensions: [
{
Name: 'Location',
Value: 'Outside'
},
{
Name: 'DeviceId',
Value: outsideDeviceId
}
],
Value: outsideData.temperature,
Unit: 'None',
Timestamp: timestamp
},
{
MetricName: 'Humidity',
Dimensions: [
{
Name: 'Location',
Value: 'Outside'
},
{
Name: 'DeviceId',
Value: outsideDeviceId
}
],
Value: outsideData.humidity,
Unit: 'Percent',
Timestamp: timestamp
}
);
}
// 室内センサーのメトリクス
if (insideData) {
metricData.push(
{
MetricName: 'CO2',
Dimensions: [
{
Name: 'Location',
Value: 'Inside'
},
{
Name: 'DeviceId',
Value: insideDeviceId
}
],
Value: insideData.CO2,
Unit: 'None',
Timestamp: timestamp
},
{
MetricName: 'Temperature',
Dimensions: [
{
Name: 'Location',
Value: 'Inside'
},
{
Name: 'DeviceId',
Value: insideDeviceId
}
],
Value: insideData.temperature,
Unit: 'None',
Timestamp: timestamp
},
{
MetricName: 'Humidity',
Dimensions: [
{
Name: 'Location',
Value: 'Inside'
},
{
Name: 'DeviceId',
Value: insideDeviceId
}
],
Value: insideData.humidity,
Unit: 'Percent',
Timestamp: timestamp
}
);
}
// CloudWatchにメトリクスを送信
await putMetricData('SwitchBot/Sensors', metricData);
console.log('Metrics collection completed successfully');
return {
statusCode: 200,
body: JSON.stringify({
message: 'Metrics sent successfully',
metricsCount: metricData.length,
outsideMetrics: {
temperature: outsideData?.temperature,
humidity: outsideData?.humidity,
battery: outsideData?.battery
},
insideMetrics: {
temperature: insideData?.temperature,
humidity: insideData?.humidity,
co2: insideData?.CO2
}
})
};
} catch (error) {
console.error('Error in handler:', error);
return {
statusCode: 500,
body: JSON.stringify({
message: 'Error processing metrics',
error: error.message
})
};
}
};
あとは、 Lambda 関数に Secrets Manager シークレットからの値取得と CloudWatch メトリクスへのメトリクスプッシュを許可するため、 secretsmanager:GetSecretValue
と cloudwatch:PutMetricData
の許可を追加します。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "logs:CreateLogGroup",
"Resource": "arn:aws:logs:ap-northeast-1:111111111111:*"
},
{
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": [
"arn:aws:logs:ap-northeast-1:111111111111:log-group:/aws/lambda/switchbot-metrics-retriever:*"
]
},
{
"Effect": "Allow",
"Action": [
"cloudwatch:PutMetricData"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"secretsmanager:GetSecretValue"
],
"Resource": "arn:aws:secretsmanager:ap-northeast-1:111111111111:secret:dev/SwitchbotMetricsRetriever-*"
}
]
}
EventBridge スケジューラで Lambda 関数の定期実行設定をする
最後に、 1 分間に 1 回、 CloudWatch メトリクスへのプッシュを行いたいので、 EventBridge スケジューラに Lambda 関数の実行を登録します。
GUI ベースでスケジュール登録ができるので非常に楽ですね。スケジューラ用の実行ロールも勝手に作ってくれます。
ちなみに、 SwitchBot OpenAPI は 1 日あたり 10,000 件までの実行制限がありますのでご注意ください。今回の場合は毎分実行なので 1 デバイスあたり毎日60 * 24 = 1,440 回、2 デバイスなので 2,880 回の実行になり、実行制限にかかることはありません。
理論上、 6 デバイスまでなら毎分実行が可能ですが、それ以上増やす場合には 5 分間隔に伸ばして実行する等の調整が必要です。
動作確認
EventBridge スケジューラを設定すると、 Lambda 関数が 1 分間隔で自動的に実行されるようになります。
試しに CloudWatch メトリクスを確認すると、ご覧の通り温度計の温度や湿度が正しく記録されていることが確認できました。
記事を執筆した日は天気予報での最低気温が 12 度、 最高気温が 32 度の予想だったのですが、ほぼ実際の計測値も一致しているように見えますね。(寒暖差が激しすぎて身体が追いつかないです…)
さいごに
SwitchBot OpenAPI を利用して、 SwitchBot 温度計の計測値を CloudWatch メトリクスにプッシュするまでの流れをご紹介しました。
SwitchBot のアプリも十分な機能を持っていますが、一度 AWS の世界に値を持ち出すことができれば、様々なツールとの連携を考えることができるので面白いかなと思います。
SwitchBot 標準のオートメーション機能でも十分な連携機能がありますが、それでは実現できない何かを作りたい方は、上記の内容が参考になりましたら幸いです。
アノテーション株式会社について
アノテーション株式会社はクラスメソッドグループのオペレーション専門特化企業です。サポート・運用・開発保守・情シス・バックオフィスの専門チームが、最新 IT テクノロジー、高い技術力、蓄積されたノウハウをフル活用し、お客様の課題解決を行っています。当社は様々な職種でメンバーを募集しています。「オペレーション・エクセレンス」と「らしく働く、らしく生きる」を共に実現するカルチャー・しくみ・働き方にご興味がある方は、アノテーション株式会社 採用サイトをぜひご覧ください。