Tuya API (Smart Life)とスマートプラグでバッテリーにやさしい充電をする仕掛け

スポンサーリンク

はじめに

ノートパソコンのバッテリは消耗品で
使っているとだんだん弱っていきます。

バッテリーがすぐ切れると困るので、
できるだけ長持ちさせたいですよね。

ノートパソコンのバッテリを
長持ちさせる方法は
様々なサイトで紹介されています。

この記事では、
MacのApple Scriptを使って、
自動的にバッテリーにやさしい
充電をする方法を紹介します。

過去の記事で、IFTTTを利用してWebhookからSmart Lifeのスマートプラグを操作する方法を紹介したのですが、2020年5月26日からSmart LifeがIFTTTに対応しなくなったため、Tuya APIを使用する方法に切り替えました。

Liイオンバッテリについて

ノートパソコンのバッテリとして
現在主流のLiイオンバッテリを
長持ちさせるには、

  • 電源つなぎっ放しはダメ
  • 20%〜80%の間で使う
  • 充電度合いが少ない状態で使う
  • 充電度合いの変動幅は小さく

バッテリーの専門家ではないため、間違った情報があるかもしれません。
また、ニッケル水素バッテリーなど、他のバッテリー種には当てはまりません。

以上を考慮して、
50%〜55%の充電状態を
キープするようにしました。

構成一覧

構成要素バージョン
macOSCatalina 10.15.5
Node.jsv12.18.0
npm6.14.4
スマートプラグ中国製Smart Life対応
型番: XS-A16

スマートプラグの設定

スマートプラグは、
家庭のコンセントにつないで
インターネット経由で
電源のON/OFFを切り替えられる機器です。

Smart Plug

Amazon EchoやGoogle Homeなどの
スマートスピーカから制御できるもの、
IFTTT経由で制御できるものなどがあります。

以下のようなSmart Homeアプリ対応の
スマートプラグであれば、
本記事と同じことができます。

スマートプラグ WiFi スマートコンセント Echo Alexa Google ホーム対応

スマートプラグの説明書に載っている
2次元バーコードから
「Smart Life」のアプリをインストールし、
このページなどを参考に、
メールアドレス登録→家族(自分)を作成
→デバイス追加の順に設定します。

デバイスの追加はアプリの右上にある
+マークをタップし、
家電製品(Electrical Engeneerings)
→コンセント(Socket)へ
と進み、Wifiのパスワードを登録します。

クラウド(Tuya IoT)の設定

クラウド側の設定をします。(参考

まず、https://iot.tuya.com から
アカウント作成してログインし、
ページ上部のCloud Developmentを
押すと以下のページが表示されます。

Tuya login

プロジェクトの作成

Createボタンを押すと
下図のように表示されるので、
記入していきます。

内容は何でも良いようです。
私は次のようにしました。

Project Name: Toggle Plug
Description: Operate the smart plug remotely.
industry: Smart Home

下図のようにAccess IDとAccess Secretが
表示されます。

これらは後で使うので、
Copyを押してコピーし、
テキストエディタに貼り付ける
などしてメモを取っておきます。

Tuya project

アプリの作成

次にページ上部の
App Service→左メニューApp SDK
を押して下の画面に移行します。

Tuya App SDK

画面中央のObtain SDKボタンを押すと
下のポップアップが出るので、
Wi-Fi device solutionを選択して
Nextを押します。

以下のポップアップが出るので
アプリ情報を記入してConfirmを押します。

私は以下のようにしました。

App Name: Plug
iOS Bundle ID: com.smartlife.plug
Android Package Name: com.smartlife.plug
Channel ID: smartlifeplug

iOSやAndroidは使用しませんが、記入は必須のようです。

ここで入力したChannel IDも
後で使うのでメモしておきます。

プロジェクトにアプリをリンク

上部メニューの
Cloud Development
→左メニューLinked Devices
→Linked devices by Apps
で以下のように表示されるので、
Add Appsを押してアプリを追加します。

Tuya link devices

作成したアプリにチェックを入れ、
OKを押します。

Tuya Choose App

Tuya API環境構築

ここでの作業を行う環境して、
以下である必要があるとのことです。

  • 2.4GHz 帯のWi-Fiがあること。
  • PCが、スマートプラグと同じWi-Fiにつながっていること。
  • そのネットワークでクライアント間通信ができること。
  • PCに他のネットワークインタフェイス(有線LANなど)がないこと。
    複数のインターフェイスがあると、優先順位によってはパケットが正しく送られない場合あり。

CLIのインストール

npmがインストールしていない方は
こちらからNode.jsの推奨版を
インストールしてから以下を実行します。

$ sudo npm i -g @tuyapi/cli

ペアリング

スマートプラグを電源に接続し、
ボタンを長押しして
ペアリングモードにします。

ペアリングモードには2種類あり、
LED点滅が速い方にします。

私の場合、
電源を入れて単に長押しすると
このモードになりました。

Macのターミナルから
次のコマンドを実行します。

$ tuya-cli link --api-key "Access ID" --api-secret "Access Secret" --schema "Channel ID" --ssid "ネットワークのSSID" --password "ネットワークのパスワード" --region us

以下のように表示されれば成功です。

✔ Device(s) registered!
[
  {
    id: 'xxxxxxxxxxxxxxxxxxxx',
    ip: 'xxx.xxx.xxx.xxx',
    localKey: 'xxxxxxxxxxxxxxxx',
    name: '智能插座'
  }
]

表示されたidとlocalKeyは
API呼び出しの際に使うので
メモしておきます。

APIのダウンロード

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

$ npm i -S codetheweb/tuyapi

codetheweb/tuyapiのreadmeには非同期版と同期版の例があり、非同期版が推奨となっていますが、非同期版を使うと時々”Unhandled promise error”が発生し、その後APIにアクセスできなくなる問題が発生したので、同期版を採用しました。

同期版を使った場合でも、
時々”Socket error”が発生するため、
3回はトライするようにしました。

これで、大抵2回目には
接続に成功するようになりました。

以下をplug_sync.jsなどの
ファイル名で保存します。

id, keyには
ペアリングの際に表示された
idとlocalKeyを入れます。

const TuyAPI = require('tuyapi');

const device = new TuyAPI({
  id: 'xxxxxxxxxxxxxxxxxxxx',
  key: 'xxxxxxxxxxxxxxxx'});

let powStat = process.argv[2] == "on" ? true : false;

(async () => {
  await device.find();
  await device.connect();

  let status = await device.get();
  console.log('Set:', status, '=>', powStat);
  await device.set({set: powStat});

  device.disconnect();
})();

引数をonにすると電源ON、
それ以外(offなど)は電源OFF
にするプログラムになっています。

ここでAPIの動作確認を行います。
以下のコマンドで
ON/OFFできるか試します。

$ node plug_sync.js on
Set: false => true
$ node plug_sync.js off
Set: true => false

これで正常にON/OFFできればよいです。

次にApple Scriptで充電の度合いに応じて
このコマンドを実行するようにします。

次の章へ進んでください。

以下、当初使っていた
非同期版のスクリプトを
参考までに掲載します。

const TuyAPI = require('tuyapi');

const device = new TuyAPI({
  id: 'xxxxxxxxxxxxxxxxxxxx',
  key: 'xxxxxxxxxxxxxxxx'});

let powStat = process.argv[2] == "on" ? true : false;

// Find device on network
device.find().then(() => {
  // Connect to device
  device.connect();
});

// Add event listeners
device.on('connected', () => {
  console.log('Connected to device!');
});

device.on('disconnected', () => {
  console.log('Disconnected from device.');
});

device.on('error', error => {
  console.log('Error!', error);
});

device.on('data', data => {
  //console.log('Data from device:', data);

  // Plug on/off
  if (data.dps['1'] != powStat) {
    console.log('Set:', data.dps['1'], '=>', powStat);
    device.set({set: powStat});
  }
});

// Disconnect after 3 seconds
setTimeout(() => { device.disconnect(); }, 3000);

Apple Scriptの作成

画面右上(メニューバー上)の
虫眼鏡マーク(Sporlight Search)から
Script Editor.appを検索して
ダブルクリックで起動します。

以下のコードをScript Editorに貼り付けます。

plug_async.jsのファイルパスは
お使いの環境に合わせて変えてください。

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use framework "AppKit"
use scripting additions

global MIN, MAX, AWAKE

set MIN to 50
set MAX to 55

set AWAKE to true

set aCenter to current application's NSWorkspace's sharedWorkspace()'s notificationCenter()
aCenter's addObserver:me selector:"notifSleep:" |name|:"NSWorkspaceWillSleepNotification" object:(missing value)
aCenter's addObserver:me selector:"notifWaked:" |name|:"NSWorkspaceDidWakeNotification" object:(missing value)

on exeAPI(message)
    -- say "The AWAKE value is " & AWAKE
    if AWAKE then
        try
            -- say "Start API"
            do shell script "/usr/local/bin/node /Users/user/plug_sync.js " & message & "> /Users/user/plug_sync.log 2>&1"
            -- say "Normal end"
        on error
            -- say "Error occurred"
            -- display dialog "Error occurred"
            return 1
        end try
    end if
    return 0
end exeAPI

on idle
    set per to (do shell script "pmset -g batt | grep % | sed 's/[%;]/ /' | awk '{print $3}'") as number
    set ret to 1
    set cnt to 0
    repeat while cnt < 3 and ret /= 0
        -- say "The count is " & cnt
        if per ≤ MIN then
            set ret to my exeAPI("on")
        else if MAX ≤ per then
            set ret to my exeAPI("off")
        end if
        set cnt to cnt + 1
    end repeat
    return 60
end idle

on quit
    exeAPI("off")
    continue quit
end quit

on notifSleep:aNotif
    exeAPI("off")
    -- display notification "System will sleep"
    -- say "System will sleep"
    set AWAKE to false
end notifSleep:

on notifWaked:aNotif
    -- display notification "System did wake"
    -- say "System did wake"
    set AWAKE to true
end notifWaked:

スクリプトの解説

MIN, MAXの値を変えれば
お好みの充電範囲にできます。

/usr/local/bin, /Users/userなどのパスは
お使いの環境に合わせて変更してください。

on idle, return 60 として
60秒毎にバッテリーの充電度合いを確認し、
MIN以下なら充電開始、
MAX以上なら充電停止しています。

nodeコマンドは3回トライしています。

当初は、on idle部のみで
充電を制御しようとしていました。

しかし、電源ON状態でシャットダウンすると
そのまま充電され続けてしまうため、
on quitでシャットダウン時の
Application停止を検知して
充電を停止するようにしました。

その後、
電源ON時にスリープモードに入った場合も
充電され続けてしまうことに気づきました。

notificationCenterの記述と
on notifSleep:aNotifを追加し、
スリープモードに入る通知を取得して
充電を止めるようにしました。

ところが、スリープモードでも
スクリプトは動作しているようで
MIN以下になるとon idleの機能が働いて
電源がONになります。

そのまま少し経つと
ディープスリープモードに移行して
スクリプトが停止して充電し続けます。

これをさけるために、
スリープと復帰の両方の通知を検知して、
現在スリープモード中かどうかを確認し、
スリープモード中は電源ON/OFFを
変更しないようにしました。

これで、
スリープ状態に入るときに電源をOFFにし、
スリープから復帰するまで電源OFFを
キープするようにしました。

スクリプトの保存

Script Editor上部の
ツルハシの絵が付いているボタンを押すと
キーワードに色が付き、
見やすくなります。

File→Saveから保存します。

File FormatをApplicationにし、
オプション:ハンドラの実行後に終了しない
(Stay open after run handler)に
チェックを入れます。

今回、PowerManagerAPI.app
という名前で保存しました。

ScriptEditor save

続いて、このApple Script Applicationを
Mac起動時に立ち上がるように設定します。

Appleマークから
システム環境設定(System Preferences)
→ユーザーとグループ(Users & Groups)
→ログイン項目(Login Items)
から+マークを押すと選択画面が出るので、
作成したPowerManagerAPI.appを選択します。

User Login Items

Macを再起動して、
充電度合い50〜55%をキープするように
自動的にスマートプラグがON/OFFされ、
シャットダウン時やスリープモード移行時に
スマートプラグがOFFになれば成功です。

追加情報

本Apple Scriptを
Dockに表示させたくない場合は、
以下を実行するとよいです。
(以下はPowerManagerAPI.appの保存場所がDocuments/scriptsの場合の例)

$ cd Documents/scripts
$ plutil -insert 'LSUIElement' -bool true PowerManagerAPI.app/Contents/Info.plist

インターネットにつながらない場所では、
1分おきにコマンドに失敗したという
ポップアップが出てしまいますが、
Editを押してScript Editorを立ち上げておくと
出なくなります。

このページのblife.shは、
バッテリーに関する情報を
まとめて表示してくれるので便利です。

$ blife.sh 
[2019-06-15 00:08:05]
Your MacBook's Battery status is
Charge Remaining 2127(mAh)
State of Charge  50%
Cycle Count  21
Cycle Count Remaining 979
Full Charge Capacity 4175(mAh)
State of Health  96.8%

お断り

充電度合いの測定には誤差があるのか、
設定した充電範囲を
多少越えることがあります。

別の観点として、
常に充電状態にしておく方が充放電回数
(Cycle Count)が進みづらいと
書いているサイトもあるようなので、
バッテリー劣化より充放電回数を重視する
(売却時に参考されるため)場合、
本サイトの内容は適さない可能性があります。

本サイトの内容に従って、
何らかの損害が発生しても
責任を負いかねますので、
あくまで自己責任でお願いします。

参考文献

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