0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SwitchBox APIテスト

0
Posted at

SwitchBot防水温湿度計のBluetoothデータ読み込みを試しましたが、SwitchBox APIのデータ読み込みも試しました。#自分の忘備録44

結果から

SwitchBot防水温湿度計だけで、SwitchBox APIを使用して温度湿度のデータが読み込めると思っていませんでしたが、それは出来なかった。

  • SwitchBot防水温湿度計の温度湿度データはSwitchBoxアプリで確認出来るので、SwitchBox APIを使用しても読み込めると勘違いしていた
    • それは、Fitbitトラッカーの各種データはFitbitアプリで確認出来て、Fitbit APIでも読み込みが出来るので、それと同様かと思っていた

各デバイスのデータの流れ

Fitbit Trackerの場合

Fitbit  ---(ble)--> Fitbit ---(web)--> Fitbit --(webapi)--> [PC]
Tracker             App                Cloud

※今は、Fitbit App → Google Health App、Fitbit Cloud → Google Cloud、昔のTrackerはFitbitのままだが新しいのはGoogle Fitbit

  • データの流れ
    • TrackerからAppにBLEでデータが転送される
      • 通常はリアルタイム的に表示されるデータのみが転送される模様
    • 〇Appの同期操作で全データがAppに転送され、Cloudにも転送されている模様
  • データ読み込み
    • 〇WebAPIでCloudに転送されたデータを読み出せる

SwitchBot防水温湿度計の場合

SwitchBot --+--(ble)--> SwitchBot ---(web)--> SwitchBot ---(webapi)--> [PC]
Sensor      |           App                   Cloud
            |
            +--(ble)--> [PC]
  • データの流れ
    • SensorからAppにBLEでデータが転送される
      • 通常はリアルタイム的に表示されるデータのみが転送される模様
    • △Appの同期操作で全データがAppに転送され、ここでCloudにも転送されている?と思った(これが違ったみたい)
  • データ読み込み
    • △WebAPIでCloudからデータが読み出せるが、どうもデバイスタイプ情報だけ(温度湿度は0で格納されていないことかな)
    • 〇BLEアドバタイズデータで直接Sensorからデータを読み出せる

SwitchBot防水温湿度計の場合はこうするみたい

どうも温度湿度データをWebAPIで読み出すには、SwitchBot Hubも使わないといけないみたい。
今度、SwitchBot Hubを購入することがあったら試してみることにする。

            +--(ble)--> SwitchBot ---(web)--> --+
            |           Hub                     |
            |                                   v
SwitchBot --+--(ble)--> SwitchBot ---(web)--> SwitchBot ---(webapi)--> [PC]
Sensor      |           App                   Cloud
            |
            +--(ble)--> [PC]
  • データの流れ
    • SensorからAppにBLEでデータが転送される
      • 通常はリアルタイム的に表示されるデータのみが転送される模様
    • 恐らく、Appからはデバイスタイプ情報だけがCloudに登録されて、データはHubによりリアルタイム的にCloudにも転送されてのではないかと
  • データ読み込み
    • たぶん、WebAPIでCloudからデータが読み出せるようになるものかと
    • BLEアドバタイズデータで直接Sensorからデータを読み出せる

SwitchBox APIでデータ取得プログラム

以下のGetting Startedのページを参考にして、テスト用のpython、JavaScript、C#のプログラムを作成した。テストプログラムなので、エラー処理なしで同期的動作にしている。
WebAPIなのでどの言語でプログラムを作成しても結果は同じですが、暇だったので試す:sweat_smile:

OpenWonderLabs / SwitchBotAPI - SwitchBot API v1.1

Getting Started

Pythonテストプログラム

テストコード
SwitchBotAPI.py
import json
import time
import hashlib
import hmac
import base64
import uuid
import requests

# Declare empty header dictionary
apiHeader = {}
# open token
token = 'tttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt' # copy and paste from the SwitchBot app V6.14 or later
# secret key
secret = 'ssssssssssssssssssssssssssssssss' # copy and paste from the SwitchBot app V6.14 or later
nonce = uuid.uuid4()
t = int(round(time.time() * 1000))
string_to_sign = '{}{}{}'.format(token, t, nonce)

string_to_sign = bytes(string_to_sign, 'utf-8')
secret = bytes(secret, 'utf-8')

sign = base64.b64encode(hmac.new(secret, msg=string_to_sign, digestmod=hashlib.sha256).digest())
##print ('Authorization: {}'.format(token))
##print ('t: {}'.format(t))
##print ('sign: {}'.format(str(sign, 'utf-8')))
##print ('nonce: {}'.format(nonce))

#Build api header JSON
apiHeader['Authorization']=token
apiHeader['Content-Type']='application/json'
apiHeader['charset']='utf8'
apiHeader['t']=str(t)
apiHeader['sign']=str(sign, 'utf-8')
apiHeader['nonce']=str(nonce)


url = 'https://api.switch-bot.com/v1.0/devices'
rsp = requests.get(url, headers=apiHeader)
print(f'status_code={rsp.status_code}')
print(rsp.json())

data = json.loads(rsp.content)
deviceId = data['body']['deviceList'][0]['deviceId']
print(f'deviceId={deviceId}')

url = f'https://api.switch-bot.com/v1.0/devices/{deviceId}/status'
rsp = requests.get(url, headers=apiHeader)
print(f'status_code={rsp.status_code}')
print(rsp.json())
(実行例)
>cd C:\TEST
>python -m venv switchbotapi
>cd switchbotapi
>Scripts\activate.bat
(switchbotapi) >pip install requests
(switchbotapi) >python -V
Python 3.14.6

(switchbotapi) >python SwitchBotAPI.py
status_code=200
{'statusCode': 100, 'body': {'deviceList': [{'deviceId': 'XXXXXXXXXXXX', 'deviceName': '防水温湿度計 3C', 'deviceType': 'WoIOSensor', 'enableCloudService': False, 'hubDeviceId': ''}], 'infraredRemoteList': []}, 'message': 'success'}
deviceId=XXXXXXXXXXXX
status_code=200
{'statusCode': 100, 'body': {'version': 'V0.9', 'temperature': 0, 'battery': 0, 'humidity': 0, 'deviceId': 'XXXXXXXXXXXX', 'deviceType': 'WoIOSensor', 'hubDeviceId': '000000000000'}, 'message': 'success'}

(switchbotapi) >

JavaScriptテストプログラム

テストコード
SwitchBotAPI.js
const crypto = require("crypto");
const https = require("https");

const token = "tttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt";
const secret = "ssssssssssssssssssssssssssssssss";


function getHeaders() {
  const t = Date.now();
  const nonce = "requestID";
  const data = token + t + nonce;
  const sign = crypto.createHmac("sha256", secret).update(data).digest("base64");

  return {
    Authorization: token,
    sign: sign,
    nonce: nonce,
    t: t,
    "Content-Type": "application/json",
  };
}


function makeRequest(path) {
  return new Promise((resolve, reject) => {
    const options = {
      hostname: "api.switch-bot.com",
      port: 443,
      path: path,
      method: "GET",
      headers: getHeaders(),
    };

    const req = https.request(options, (res) => {
      let data = "";
      res.on("data", (d) => {
        data += d;
      });
      res.on("end", () => {
        console.log(`statusCode=${res.statusCode}`);
        console.log(data);
        resolve(JSON.parse(data));
      });
    });

    req.on("error", (error) => {
      reject(error);
    });

    req.end();
  });
}


async function main() {
    const response = await makeRequest("/v1.1/devices");

    const deviceId = response.body.deviceList[0].deviceId;
    console.log(`deviceId=${deviceId}`);

    await makeRequest(`/v1.1/devices/${deviceId}/status`);
}


main();
(実行例)
>cd C:\TEST\switchbotapi
>node -v
v24.15.0

>node SwitchBotAPI.js
statusCode=200
{"statusCode":100,"body":{"deviceList":[{"deviceId":"XXXXXXXXXXXX","deviceName":"防水温湿度計 3C","deviceType":"WoIOSensor","enableCloudService":false,"hubDeviceId":""}],"infraredRemoteList":[]},"message":"success"}
deviceId=XXXXXXXXXXXX
statusCode=200
{"statusCode":100,"body":{"version":"V0.9","temperature":0,"battery":0,"humidity":0,"deviceId":"XXXXXXXXXXXX","deviceType":"WoIOSensor","hubDeviceId":"000000000000"},"message":"success"}

>

C#テストプログラム

テストコード
Prgoram.cs
using System;
using System.Text;
using System.Text.Json;
using System.Security.Cryptography;
using System.Net.Http;
using System.Threading.Tasks;


class Program
{
    private static HttpClient client = new HttpClient();

    private static string token = "tttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt";
    private static string secret = "ssssssssssssssssssssssssssssssss";

    private static DateTime dt1970 = new DateTime(1970, 1, 1);
    private static DateTime current = DateTime.Now;
    private static TimeSpan span = current - dt1970;
    private static long time = Convert.ToInt64(span.TotalMilliseconds);
    private static string nonce = Guid.NewGuid().ToString();
    private static string data = token + time.ToString() + nonce;
    private static Encoding utf8 = Encoding.UTF8;
    private static HMACSHA256 hmac = new HMACSHA256(utf8.GetBytes(secret));
    private static string signature = Convert.ToBase64String(hmac.ComputeHash(utf8.GetBytes(data)));


    static void Main(string[] args)
    {
        string content = Req("/v1.1/devices");

        var devices = JsonSerializer.Deserialize<Devices>(content);
        string deviceId = devices.body.deviceList[0].deviceId;
        Console.WriteLine($"deviceId={deviceId}");

        Req($"/v1.1/devices/{deviceId}/status");
    }

    public static string Req(string path)
    {
        var request = new HttpRequestMessage(HttpMethod.Get, $"https://api.switch-bot.com{path}");
        request.Headers.TryAddWithoutValidation("Authorization", token);
        request.Headers.TryAddWithoutValidation("sign", signature);
        request.Headers.TryAddWithoutValidation("nonce", nonce);
        request.Headers.TryAddWithoutValidation("t", time.ToString());

        var res = client.SendAsync(request).Result;
        string content = res.Content.ReadAsStringAsync().Result;

        Console.WriteLine($"StatusCode={res.StatusCode}");
        Console.WriteLine(content);

        return content;
    }


    //{"statusCode":100,"body":{"deviceList":[{"deviceId":"XXXXXXXXXXXX","deviceName":"防水温湿度計","deviceType":"WoIOSensor","enableCloudService":false,"hubDeviceId":""}],"infraredRemoteList":[]},"message":"success"}
    public class DeviceList
    {
        public string deviceId { get; set; }
        public string deviceName { get; set; }
        public string deviceType { get; set; }
        public bool enableCloudService { get; set; }
        public string hubDeviceId { get; set; }
    }

    public class Body
    {
        public List<DeviceList> deviceList { get; set; }
        public List<object> infraredRemoteList { get; set; }
    }

    public class Devices
    {
        public int statusCode { get; set; }
        public Body body { get; set; }
        public string message { get; set; }
    }
}
(実行例)
>cd C:\TEST\switchbotapi
>dotnet new console -n sb
>cd sb
>dotnet build
>bin\Debug\net10.0\sb.exe
StatusCode=OK
{"statusCode":100,"body":{"deviceList":[{"deviceId":"XXXXXXXXXXXX","deviceName":"防水温湿度計","deviceType":"WoIOSensor","enableCloudService":false,"hubDeviceId":""}],"infraredRemoteList":[]},"message":"success"}
deviceId=XXXXXXXXXXXX
StatusCode=OK
{"statusCode":100,"body":{"version":"V0.9","temperature":0,"battery":0,"humidity":0,"deviceId":"XXXXXXXXXXXX","deviceType":"WoIOSensor","hubDeviceId":"000000000000"},"message":"success"}

>
0
0
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
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?