AppleScript Automator Mac シェルコマンド シェルスクリプト プログラミング

MacBook Proのバッテリー残量を定期的に監視して過充電と電池切れを防いでくれるシェルスクリプトを作ったらcronで動かずに困った話

コンセントタイマーで過充電は防げるがしばしばバッテリー切れになる

 

ウチにある2台のMacBook Proが相次いで入院。どちらもバッテリー膨張でした。

前にもやった故障なので、できればもう遭いたくない。ということで対策を考えました。

ひとつは、充電器とACの間にコンセントタイマーを入れること。常に一定時間で充電が終わるようにすることで、満タンになったあとも充電しつづける時間を短くしました。

MacBook Proバッテリー膨張の再発防止にデジタルコンセントタイマーCT24Dを導入

MacBook Proの過充電によるバッテリー膨張の再発を防止するため、デジタルコンセントタイマーCT24Dを導入しました。ちょっと使い方に癖があるので備忘録として残します

続きを見る

 

しばらくこれで使っていてそれなりに良かったのですが、作業に夢中になっていると、Macが表示する残量警告に気づかずに、バッテリーを使い切って強制スリープしてしまうことがよくありました。

Macのバッテリー残量警告は、通知機能でウインドウの右上に比較的おしとやかに表示されるので、気がつかないことも多いんですよね。

あと、残り10%くらいで警告するので、もうちょっと早く警告して欲しい。

そんなわけで、自分のニーズと性格にあった警告プログラムをシェルスクリプトで作りました。

要件は

  • 過充電、残量不足の両方で警告を出せる
  • 任意の警告残量を設定できる
  • 通知ではなく、無視できないアラートダイアログで警告する
  • 解決しない間は一定時間ごとに警告を繰り返す

といった感じです。

 

バッテリーを監視して過充電と残量不足の両方を警告するシェルスクリプト

 

このスクリプトを実行して、警告の条件に一致すると以下のようなダイアログがでます。

 

モーダルダイアログなので無視できません。Youtubeを全画面表示で見ていても画面の真ん中に警告が表示されます。

指示に従ってACアダプタを接続したり外したりしないと一定時間ごとに警告が出続けます。

 

スクリプトの中身はこんな感じです。

Check_MBP_Battery.sh

 

#!/bin/bash

#pmsetコマンドでバッテリーの状態を取得
#sedで 不要な行をスキップ、残量%、AC or Battery  を取得して変数に入れる
#残量21%未満でバッテリー駆動ならAC接続を促す
#残量80%以上でAC接続中ならACを外すことを促す

pmset -g batt >> battery_log.txt
batterry_rest=`pmset -g batt |  sed -E "/Now/d" |  sed -E "/Warning/d" |  sed -E "s/\(no estimate\)//" | sed  -E "s/^.*\)//g" | sed  -E "s/\%;.*$//g"`
ac_or_batt=`pmset -g batt |  sed -nE '/Now/p' | sed -E  's/Now drawing from //' | sed -E "s/'//g"`

if [ $(( batterry_rest  )) -lt 21 -a "$ac_or_batt" = "Battery Power" ]; 
then osascript -e 'display dialog "バッテリー残量僅少。ACアダプタを繋いで下さい" with icon caution buttons("繋ぎます")'
else
if [ $(( batterry_rest  )) -gt 79 -a "$ac_or_batt" = "AC Power" ]; 
then osascript -e 'display dialog "腹八分目。電源を外して下さい" with icon caution buttons("外します")'
fi
fi

 

pmsetコマンドで電源の状態を取得

pmsetは、power management settings、電源管理設定コマンドです。

pmset -g batt で、現在のバッテリーの状態をレポートしてくれます。

バッテリーの状態とACアダプタの接続有無により、以下のような結果が返ってきます。

●AC接続時

 pmset -g batt
Now drawing from 'AC Power'
-InternalBattery-0 (id=xxxxxx) 7%; charging; 3:51 remaining present: true

 

●バッテリー駆動時

 pmset -g batt
Now drawing from 'Battery Power'
-InternalBattery-0 (id=4718691) 20%; discharging; 0:20 remaining present: true

 

●バッテリー駆動時(空に近いとき)

'Battery Warning'の行が追加される

 pmset -g batt
Now drawing from 'Battery Power'
-InternalBattery-0 (id=4718691) 7%; discharging; 0:07 remaining present: true
Battery Warning: Final

●残り時間が計算できないとき

(no estimate) という記述になります。

最初はこれに気づかずに誤動作が起きていました。

ログから原因が分かったので、これに対処するためのsed記述も追加しました。

 pmset -g batt
-InternalBattery-0 (id=4718691) 27%; discharging; (no estimate) present: true

 

今回のスクリプトでは上記のようなpmsetの結果からsedを使って不要な部分を削除し、'Battery Power','AC Power'というワードと残量の数値だけを抜き出しています。

もし任意の残量で警告を出す必要がなければ、「'Battery Warning:'で始まる行があるかどうか」で警告を出す方法もありますね。

 

osascriptでApplescriptを使ってダイアログを出す

警告ダイアログはAppleScriptのdisplay dialogを使って出しています。

シェルからAppleScriptを使うためにosascriptというコマンドを使っています。

 shell script
osascript -e 'display dialog "腹八分目。電源を外して下さい" with icon caution buttons("外します")'

osascript -e 'apple script code'

で任意のApplescriptが実行できます。 -e オプションはその後に続く文字列をapplescriptと解釈して実行します。

 

シェルスクリプトはchmod 755で実行権を設定

エディタでコードを保存したら、ターミナルで

 terminal
chmod 755 Check_MBP_Battery.sh

 

として実行権を付与。

 

 terminal
./Check_MBP_Battery.sh

 

で実行してみて、battery_log.txt というファイルにバッテリーの状態が記録されていれば、とりあえず動いていると思います。

このスクリプトを cron で定期的に実行してチェックします。cron はlinuxとかにある任意の時間や間隔でプログラムを自動実行させる機能です。

 

cronからosacriptが呼び出せない!?

というつもりだったのですが、ここで思わぬトラブルが発生。

このスクリプト、コマンドラインからだと動くのですがcronだと動かないのです。

パスの設定とか実行権限とかターミナルのフルディスクアクセスとか、いろいろ調べてやってみましたが、どうにも動きません。

分割してみると、pmsetは動くのですが、osascriptが動かないことが分かりました。

条件が揃って警告ダイアログを出そうとしてもでない。

エラーは

 
「0:74: execution error: ユーザの操作は許可されていません。 (-1713)」

となっています。

ググっても同じトラブルは見つからない。

そこで、シェルスクリプトをそっくりそのままAutomatorに移してみたら動きました。

 

Automatorで新規ワークフローを作成。上記のシェルスクリプトをそっくりそのまま貼り付けて保存しただけ

 

実はプロトタイプの段階ではAutomatorで作っていました。

pmsetによる情報の取得をシェルスクリプトで行い、その結果を受けた警告の表示をApplescriptで行うという、二段階のAutomatorになっていました。

その後、シェルスクリプトからApplescriptを動かせるosascriptというコマンドがあることを知り、全体をシェルスクリプトに一本化したところ、動かなかったというトホホ展開でした。

他の人のところでも同じ状況になるかどうかは分かりませんが、とりあえずcronでシェルスクリプトが動かないときにAutomator化する、という選択肢は覚えておいて損はなさそうです。

 

cronでAutomatorワークフローを自動実行

crontab -e

として、cronの設定を編集。

私の場合は以下のように設定しました。

 

 terminal
*/5  * * * *  open ~/${PATH} /Check_MBP_Battery.app 2 >> mbp_batt_check_error.txt

 

最初の */5 は5分おきに実行を意味します。

Automatorワークフローを起動するときは open hoge.app とします。

${PATH}は、自分の環境に合わせてパスを設定します。FinderからCheck_MBP_Battery.appファイルをターミナルのウインドウにドラッグ&ドロップすると、ターミナルにフルパスが入力されるのでそれをコピって使うと楽です。

2 >> mbp_batt_check_error.txt

はエラー出力です。コマンドにエラーがあると、mbp_batt_check_error.txtに出力されます。

crontab設定を保存してエディタを終了すると、図のようなセキュリティ確認ダイアログが出ます。OKをクリックして許可します。

 

 

これで5分おきにチェックしてくれます。3Dとかでハードな作業をしていると、ぐんぐん電池が減るのでチェック間隔を短めにしています。軽い用途ならもっと長くても良いかもしれません。

 

今後の課題:放っておいてほしい時の対応

集中して作業をしているときや、ストリーミングで映画やライブを見ているとき、スクリーンキャプチャをしているときなどは、過充電の警告を一時的に切っておきたいことがあります。

今のままだとcrontabを外すしかないので、アプリ内で警告の一時停止ができるようにするのが今後の開発課題です。

 

 

【追記】OSのバージョンアップで使わなくなった

その後、OSをBig Surにアップグレード、OS標準でバッテリー保護機能が備わりました。この機能か使えるMacBook Pro(2018)ではコンセントタイマーを使わなくなりました。

MacOS Big Surにしたらバッテリー保護用のコンセントタイマーや監視アプリがいらなくなった

Macbook ProのOSをBig Surにアップグレードしたらバッテリー管理機能が有効になったので、バッテリー監視の自作アプリやコンセントタイマーが不要になりました

続きを見る

 

-AppleScript, Automator, Mac, シェルコマンド, シェルスクリプト, プログラミング