Androidアプリ開発

SwitchBot温湿度計プロの気温と湿度を
アプリ連携する

この記事は約22分で読めます。
記事内に広告が含まれています。
スポンサーリンク

この記事はAndroidスマホ用のアプリ開発の中で、
今後の開発で再使用性が高いと思われるコーディングをまとめたものです。
Javaでの開発経験、XML構文規則、Androidのアプリ開発経験がある方を対象としています。
Androidのアプリ開発でお役にたててれば、嬉しいです。
(これからAndroidのアプリ開発やJavaでの開発を始めたい方への案内は、
記事の最後で紹介します)

この記事のテーマ


SwitchBot温湿度計プロの気温と湿度をアプリに連携する機能を実装する

2025年現在、SwitchBot温湿度計は3種類存在しています。
以下で紹介する実装方法はSwitchBot温湿度計プロが対象です。

ポイント

2025年現在、SwitchBot温湿度計は3種類存在しています。
BLE(BluetoothLowEnergy)を使って、気温と湿度をインタフェースしている点と、温度と湿度をBLEアドバタイズに含めてデータ送信している点は共通ですが、BLEアドバタイズの内容が異なります。
今回は、SwitchBot温湿度計ProBLEアドバタイズから気温と湿度取得する実装を紹介します。

SwitchBot温湿度計及び、SwitchBot温湿度計プラスはコチラ↓↓↓

BLEコントローラ

Bluetoothデバイスと通信する場合、マニフェストファイルへの権限指定が必要です。
さらに Android12以降では、アプリ側でユーザー承認をリクエストする必要があります。
マニフェストファイルへの権限指定と権限チェックの実装については、以下の参考記事を参照ください。

BLEコントローラクラス

BLE MACBLEアドバタイズのタイプを指定して、受信するBLEアドバタイズのフィルタ処理を行い、目的のサービスデータを取得します。
スキャン(受信待ち状態)処理では、Handlerを使用して、タイムアウト処理を実装します。
また、インタフェースを実装して、受信したサービスデータの受け渡しを行います。

public class BluetoothLowEnergyController {
    public static final String          SWITCHBOT = "01:ff:07:16";
    public static final String          SWITCHBOTPRO = "01:ff:16";
    private static final long           SCAN_PERIOD = 30000;    // スキャンタイムアウト(30秒)
    private final Handler               handler;
    private final BluetoothAdapter      bluetoothAdapter;
    private final BleScanCallback       bleScanCallback;
    private final BluetoothLeScanner    bluetoothLeScanner;
    private OnChangeListener            onChangeListener;
    private final Set<ScanResult>       results = new HashSet<>();
    private final List<ScanResult>      batchScanResults = new ArrayList<>();
    private byte[]                      scanData = new byte[]{0};
    private byte[]                      preValue = new byte[]{0};
    private Runnable                    runnable;
    private String                      macAddress = "00:00:5E:00:53:00";
    private Long                        updated;
    private long                        timer;
    private Byte                        type = null;
    private final Context               context;
    private final ArrayList<String>     deviceList = new ArrayList<>();
    private final ArrayList<String>     macAddresses = new ArrayList<>();
    private byte[]                      typeValue = new byte[]{0};
    public BluetoothLowEnergyController(Context context) {
        this.context = context;
        this.handler = new Handler(Looper.getMainLooper());
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            BluetoothManager bluetoothManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
            this.bluetoothAdapter = bluetoothManager.getAdapter();
        } else {
            this.bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        }
        this.bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();
        this.bleScanCallback = new BleScanCallback();
    }

    // インタフェース //
    public interface OnChangeListener {
        void onValueChanged(byte[] value);
    }
    public void setOnChangeListener(final OnChangeListener onChangeListener) {
        this.onChangeListener = onChangeListener;
    }

    // SCAN //
    @SuppressLint("MissingPermission")
    public void scan() {
       timer = System.currentTimeMillis();
       runnable = new Runnable() {
           @Override
           public void run() {
               if (results.isEmpty() && System.currentTimeMillis() > timer + SCAN_PERIOD) {
                   handler.removeCallbacks(runnable);
                   // BLEスキャン停止
                    if (bluetoothAdapter.isEnabled())
                       bluetoothLeScanner.stopScan(bleScanCallback);
               }
               handler.postDelayed(this, SCAN_PERIOD);
           }
       };
       bleScanCallback.clear();
       // BLEスキャン開始
       if (bluetoothAdapter.isEnabled())
           bluetoothLeScanner.startScan(buildScanFilters(), buildScanSettings(), bleScanCallback);
       handler.post(runnable);
    }

    // PAUSE //
    @SuppressLint("MissingPermission")
    public void pause() {
        handler.removeCallbacks(runnable);
        if (bluetoothAdapter.isEnabled()) bluetoothLeScanner.stopScan(bleScanCallback);
        bleScanCallback.clear();
    }

    private List<ScanFilter> buildScanFilters() {
        List<ScanFilter> scanFilters = new ArrayList<>();
        ScanFilter.Builder builder = new ScanFilter.Builder();
        builder.setDeviceAddress(macAddress);
        scanFilters.add(builder.build());
        return scanFilters;
    }

    private ScanSettings buildScanSettings() {
        ScanSettings.Builder builder = new ScanSettings.Builder();
        builder.setScanMode(ScanSettings.SCAN_MODE_LOW_POWER);
        return builder.build();
    }

    private class BleScanCallback extends ScanCallback {
        @SuppressLint("MissingPermission")
        @Override
        public void onScanResult(int callbackType, ScanResult result) {
           if (callbackType == ScanSettings.CALLBACK_TYPE_ALL_MATCHES && result.getScanRecord() != null) {
               results.add(result);
               BluetoothDevice bluetoothDevice = result.getDevice();
               scanData = result.getScanRecord().getBytes();
               updated = System.currentTimeMillis();
               if (type != null) {
                   int length;
                   int position = 0;
                   byte newType;
                   while (position < scanData.length) {
                       length = scanData[position];
                       if (length == 0) {
                           break;
                       }
                       position++;
                       newType = scanData[position];
                       if (newType == type) {
                           byte[] value = Arrays.copyOfRange(scanData, position + 1, position + length);
                           // 受信データ通知
                           onChangeListener.onValueChanged(value);
                           if (!Arrays.equals(preValue, value)) {
                               preValue = value.clone();
                           }
                       }
                       position = position + length;
                   }
               } else {
                   handler.removeCallbacks(runnable);
                   //BLEスキャン停止
                   if (bluetoothAdapter.isEnabled()) {
                       bluetoothLeScanner.stopScan(bleScanCallback);
                   }
               }
           }
        }
        @Override
        public void onBatchScanResults(List<ScanResult> results) {
            batchScanResults.addAll(results);
        }
        @Override
        public void onScanFailed(int errorCode) {
        }
        synchronized void clear() {
            results.clear();
            batchScanResults.clear();
            Arrays.fill(scanData,(byte)0);
        }
    }

    // Android端末のBluetooth機能の有効化判定 //
    public boolean requestBluetoothFeature() {
        return bluetoothAdapter.isEnabled();
    }

    // デバイスのMACアドレス設定 //
    public void setMacAddress(String mac) { macAddress = (!mac.isEmpty() && !mac.equals("none") && isMACAddress(mac)? mac : "00:00:5E:00:53:00"); }

    /** @noinspection unused*/
    public byte[] getTypeValue() { return typeValue; }

    /** @noinspection unused*/ 
    // BLEアドバタイズパケット取得 //
    public byte[] getScanData() { return scanData; }

    /** @noinspection unused*/ 
    // BLEアドバタイズパケット取得時刻 //
    public String getUpdated() {
        SimpleDateFormat HHmm = new SimpleDateFormat("HH:mm", Locale.JAPAN);
        Date date = new Date(updated);
        return HHmm.format(date);
    }

    // タイプ設定 //
    public void setType(Byte newType) { type = newType; }

    /** @noinspection unused*/
    // 値取得 //
    public Set<ScanResult> getScanResult() { return results; }
    /** @noinspection unused*/
    public List<ScanResult> getBatchScanResults() { return batchScanResults; }

    // MACアドレスチェック //
    public static Boolean isMACAddress(String macAddress) {
        if (macAddress.isEmpty() || macAddress.equals("none")) return false;
        String reg = "^([0-9A-Fa-f]{2}[:-]){5}[0-9A-Fa-f]{2}$";
        Pattern p = Pattern.compile(reg);
        Matcher m = p.matcher(macAddress);
        return m.find();
    }
}

00:00:5E:00:53:00はRFC7042に規定されているユニキャスト用のMACアドレスです。
有効なMACアドレスが指定されなかった場合に誤受信を防ぐために使用します。

SwitchBot温湿度計プロSwitchBot温湿度計BLEアドバタイズのタイプ列で判別できます。
SwitchBot温湿度計プロ(01:ff:16)
SwitchBot温湿度計(01:ff:07:16)

SwichBotインタフェース

BLEコントローラクラスから受信したサービスデータを温度と湿度に分解し、それぞれのTextViewに表示します。

    private static final byte           SWITCHBOTPRO = (byte) 0xff;
    : 
    public void setBluetoothLowEnergyController(BluetoothLowEnergyController newBluetoothLowEnergyController, TextView temp, TextView hum, String mac) {
            bluetoothLowEnergyController = newBluetoothLowEnergyController;
            if (bluetoothLowEnergyController.requestBluetoothFeature()) {
                bluetoothLowEnergyController.setOnChangeListener(value -> {
                    if (value.length > base) {
                        int sign = value[11] & 0x80;
                        float decimals = (value[10] & 0x0f);
                        temperature = (sign == 0x80) ? (value[11] & 0x7f) + (decimals / 10) : (value[11] & 0x7f) + (decimals / 10) * -1;
                        temp.setText(String.format("%.1f", temperature));
                        if (value.length > 12) {
                            humidity = value[12] & 0x7f;
                            hum.setText(String.format("%.1f", humidity));
                        }
                    }
                });
                bluetoothLowEnergyController.setMacAddress(mac);
                bluetoothLowEnergyController.setType(SWITCHBOTPRO);
                bluetoothLowEnergyController.scan();
            }
    }
    :

温度と湿度が格納されているBLEアドバタイズのタイプは0xffです。
温度は10バイト目から2バイト、湿度は12バイト目に格納されています。

SwitchBotデバイスのBLE MACアドレス

SwitchBotデバイスのBLE MACは、SwitchBotアプリで確認できます。

SwitchBotアプリのマイホームから温湿度計をタップします。

温湿度計Pro DAを選択、設定アイコンをタップします。

③デバイス情報にBLE MACが表示されます。

SwitchBotアプリのインストールはこちらです↓↓↓

Google Play で手に入れよう

今回は、ここまでです。

SwitchBot温湿度計プロから気温と湿度を連携している Androidアプリです。

誤字脱字、意味不明でわかりづらい、
もっと詳しく知りたいなどのご意見は、
このページの最後にある
コメントか、
こちらから、お願いいたします♪

ポチッとして頂けると、
次のコンテンツを作成する励みになります♪

ブログランキング・にほんブログ村へ

これからAndroidのアプリ開発やJavaでの開発を始めたい方へ

初めての Android のアプリ開発では、アプリケーション開発経験がない方や、
アプリケーション開発経験がある方でも、Java や C# などのオブジェクト指向言語が初めての方は、
書籍などによる独学ではアプリ開発できるようになるには、かなりの時間がかかります。
オンラインスクールでの習得をおススメします。

未経験者からプログラマーを目指すのに最適です。まずは無料カウンセリングから♪

カリキュラムとサポートがしっかりしています。お得なキャンペーンとかいろいろやっています♪

ゲーム系に強いスクール、UnityやUnrealEngineを習得するのに最適です。まずは無料オンライン相談から♪

参考になったら、💛をポッチとしてね♪

スポンサーリンク
シェアする
msakiをフォローする
スポンサーリンク

コメント欄

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