Androidで充電80%で停止させるアプリを作る

Androidで充電80%で停止させるアプリを作る Android

はじめに

スマホやタブレットで充電を80%までに制限したい場合、システムが充電制限に対応していれば簡単にできるのですが、対応してない場合は外部のアプリをインストールして、SwitchbotのプラグなどをON/OFFして充電を制御するやり方があります。

上のやり方ですと、時々IFTTTが通知を受け取らないことがあったり、FireタブレットではIFTTTが正常に動かない(通知を受け取らない?)ことが多発していたりで、他のやり方を模索してみました。

私が見つけたやり方は以下の通りです。スウェーデン製のAutomateという自動化アプリを使います。

このやり方では、充電が40%になったらプラグミニをON、充電が80%になったらプラグミニをOFFにするなど、電源つなぎっぱなしで充電を完全自動化することもできます。自作Androidアプリは思ったより簡単に作成できました。

では、以下にやり方を解説していきます。

スマホ/タブレットの準備

SwitchBotアプリを開いて、プロフィールを選択します。

基本データを選択します。

アプリバージョンを10回タップします。

開発者オプションが出現しますので、押します。

トークンとクライアントシークレットを控えておきます。

SwitchBotアプリからプラグミニを選択し、設定ボタンを押します。

デバイス情報を押します。

BLE MACのアドレスを控えておきます。これがデバイスIDになります。例えば、D0:05:71:E9:5F:62だったとすると、デバイスIDはD00571E95F62となります。

PCの準備

AndroidStudioのインストール

AndroidStudioをダウンロードします。

ダウンロードしたファイルを実行します。

Nextを押します。

Nextを押します。

Nextを押します。

Installボタンを押します。

Nextボタンを押します。

Finishボタンを押すとAndroidStudioが起動します。

改善のためのデータ送信は、Don’t sendで問題ありません。

Nextを押します。

Standardが選択されていますので、そのままNextを押します。

Nextを押します。

Acceptを選択してNextを押します。

Finishを押します。

Androidアプリの作成

コード入力

NewProjectを押します。

EmptyViewsActivityを選択してNextボタンを押します。

アプリの名前を入力し、(ここではSwitchBotPlugONとしました)SDKのバージョンを選択してFinishを押します。私の場合、FireOSでも使いたかったのでAndroid10を選択しています。

左側のツリーでAndroidManifest.xmlをダブルクリックします。

<application>タグの上に下記のコードを追加します。

<uses-permission android:name="android.permission.INTERNET" />

左側のツリーで、build.gradle.ktsをダブルクリックします。
※(Module:app)のほうです。

dependencies { … }ブロックに以下の2行を追記します。

    implementation("com.squareup.okhttp3:okhttp:4.12.0")
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.1")

Sync Project with Gradle filesボタンを押します。

MainActivity.ktのタブに切り替えます。

import以下の行をコメントアウト、もしくは削除します。行を選択するとダイアログが出ますので、//のボタンを押すとコメントアウトできます。

1行目のPackage行以下に下記のコードを追加します。

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody

class MainActivity : AppCompatActivity() {

    // ★ OFF版と同じ値をそのまま使ってください
    private val switchBotToken = "YOUR_SWITCHBOT_TOKEN"
    private val switchBotSecret = "YOUR_SWITCHBOT_SECRET" // 署名を使うなら利用
    private val plugDeviceId = "YOUR_DEVICE_ID"

    private val client = OkHttpClient()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // アプリ起動直後に ON コマンドを送信
        lifecycleScope.launch {
            val success = withContext(Dispatchers.IO) {
                sendSwitchBotOnCommand()
            }

            if (success) {
                Toast.makeText(this@MainActivity, "ONコマンド送信成功", Toast.LENGTH_SHORT).show()
                finish()  // 成功したらアプリを閉じる
            } else {
                Toast.makeText(this@MainActivity, "送信に失敗しました", Toast.LENGTH_SHORT).show()
            }
        }
    }

    /**
     * SwitchBot プラグミニを ON にする API 呼び出し
     * URL・ヘッダ・JSONは公式ドキュメントに合わせて調整してください。
     */
    private fun sendSwitchBotOnCommand(): Boolean {
        // エンドポイントURL(OFF版と同じ)
        val url = "https://api.switch-bot.com/v1.1/devices/$plugDeviceId/commands"

        val jsonBody = """
            {
              "command": "turnOn",
              "parameter": "default",
              "commandType": "command"
            }
        """.trimIndent()

        val mediaType = "application/json; charset=utf-8".toMediaType()
        val body = jsonBody.toRequestBody(mediaType)

        val requestBuilder = Request.Builder()
            .url(url)
            .post(body)
            .addHeader("Content-Type", "application/json")
            .addHeader("Authorization", switchBotToken)
        // ここに sign / t / nonce など、
        // SwitchBot公式クラウドAPI仕様どおりのヘッダを追加してください。
        // .addHeader("sign", sign)
        // .addHeader("t", timestamp)
        // .addHeader("nonce", nonce)

        val request = requestBuilder.build()

        return try {
            client.newCall(request).execute().use { response ->
                response.isSuccessful
            }
        } catch (e: Exception) {
            e.printStackTrace()
            false
        }
    }
}

貼り付けると下記のようになります。

今貼り付けたMainActivity.ktの3か所、トークン、シークレット、デバイスIDを入力します。スマホ/タブレットの準備で控えた3つの情報を入力してください。

動作テスト

右上のRunボタンを押します。

右側にスマホの画面が出て、アプリが走ります。問題なくできていれば、指定したSwitchBotプラグミニがONになります。

配布用APKを作成する

アプリ名を確認します。

左側のツリーからapp > res > values > strings.xmlを開く

アプリ名を変更する場合はここを修正します。修正不要であればそのままでOKです。

メニューバーのBuild⇒Generate App Bundles or APKs⇒Generate APKsを選択します。

右下のlocateを押します。

フォルダが開き、apkファイルが確認できます。このapkをスマホやタブレットに転送してインストールすればOKです。

OFF版のコード

OFF版のコードは下記の通りです。一部違いますが、ほぼON版と同じです。

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody

class MainActivity : AppCompatActivity() {

    // ★ あなたの SwitchBot の情報をここに入れる
    private val switchBotToken = "YOUR_SWITCHBOT_TOKEN"
    private val switchBotSecret = "YOUR_SWITCHBOT_SECRET" // 署名が必要なら使う
    private val plugDeviceId = "YOUR_DEVICE_ID"

    private val client = OkHttpClient()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // ★ アプリ起動直後にOFFコマンドを送る
        lifecycleScope.launch {
            val success = withContext(Dispatchers.IO) {
                sendSwitchBotOffCommand()
            }

            if (success) {
                Toast.makeText(this@MainActivity, "OFFコマンド送信成功", Toast.LENGTH_SHORT).show()
            } else {
                Toast.makeText(this@MainActivity, "送信に失敗しました", Toast.LENGTH_SHORT).show()
            }

            // 必要なら、終わったらアプリを閉じることも可能
             finish()
        }
    }

    /**
     * SwitchBotのプラグミニをOFFにするAPIを呼び出す関数
     * URL・ヘッダ・JSONは公式ドキュメントに合わせて調整してください。
     */
    private fun sendSwitchBotOffCommand(): Boolean {
        // 例:エンドポイントURL(※正確なパスは公式を確認)
        val url = "https://api.switch-bot.com/v1.1/devices/$plugDeviceId/commands"

        // 例:OFFコマンドのJSON(※実際のコマンド名等は公式を確認)
        val jsonBody = """
            {
              "command": "turnOff",
              "parameter": "default",
              "commandType": "command"
            }
        """.trimIndent()

        val mediaType = "application/json; charset=utf-8".toMediaType()
        val body = jsonBody.toRequestBody(mediaType)

        val requestBuilder = Request.Builder()
            .url(url)
            .post(body)
            .addHeader("Content-Type", "application/json")
            .addHeader("Authorization", switchBotToken)
        // ここで、署名が必要なら sign, t, nonce などをヘッダに追加
        // .addHeader("sign", sign)
        // .addHeader("t", timestamp)
        // .addHeader("nonce", nonce)

        val request = requestBuilder.build()

        return try {
            client.newCall(request).execute().use { response ->
                response.isSuccessful
            }
        } catch (e: Exception) {
            e.printStackTrace()
            false
        }
    }
}

Automateのセットアップ

Automateをスマホ/タブレットにインストールします。

アプリを起動して、右下の+ボタンから新しいフローを作成します。

右下の+ボタンでブロックを追加します。

Battery&powerのグループにある、Battery level?を選択します。

配置したタイルを選択します。

Maximum levelの欄に40を入力してSaveを押します。

次は、AppsグループのApp startを押してブロックを追加します。

Packageを押すとアプリ一覧が表示されます。

作成してインストールしたアプリを選択します。(絵では末尾にFire8をつけています)

SAVEを押して閉じます。

次はDate & timeのDelayブロックを追加します。

Durationに1分を入力してSAVEを押します。

ブロック同士を下記のように線でつなぎます。

同様にバッテリー80%以上のフローも作成します。

まとめ

これで40%以下になるとSwitchbotプラグミニがONになり、80%になるとOFFにすることができます。バッテリーの数値は任意に指定できますので、50%~60%の間で充電制御することなども可能です。Androidアプリも思ったより簡単に作成できました。タブレットなどを自動で充電制御する際などにご活用ください。

コメント

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