STORES Product Blog

こだわりを持ったお商売を支える「STORES」のテクノロジー部門のメンバーによるブログです。

IT 管理者が MacBook のバッテリー状態を知るためにやったこと

こんにちは、 STORES でコーポレートエンジニアをしている佐々木です。
コーポレートエンジニア が所属する IT 本部は、人事や労務といったいわゆるバックオフィスの部署と同じ PX 本部の中にあります。
PX は People Experience の略で、プロダクト開発がユーザー体験を重視するように、 STORES で働いている人は自分たちにとってのユーザーであるという考え方を表しています。

私が所属する IT本部 コーポレートエンジニアリンググループは、デバイス管理、オフィスネットワーク、SaaSの活用、社内ツールの開発など、全社横断的な課題をエンジニアリングで改善する事を目標にしています。

ノート PC のバッテリー不調に早めに気付きたい

お仕事でノート PC を利用することは多いと思うのですが、ノート PC の宿命としてバッテリーの不調によるトラブルは割と多いのではないかと思っています。

バッテリーの最大充電容量が低下している警告 macOS 14.7.1

使い方や個体差によって修理推奨が出る時期はバラつくためユーザーが気づかないうちにバッテリーが劣化していることも多いですが、管理者側でバッテリーの状態を把握できることでPCの不調に早めに気づいたり、交換の声掛けを早めに行うなどの対処も可能になります。

STORES では MacBook Pro/Air を利用 している割合が高く、デバイス管理に Jamf Pro を使っています。
Jamf Pro のコンピュータインベントリには Battery Capacity (バッテリー容量)という項目があるのですが、これは Intel CPU が搭載された機種のみ対応しており、Apple Silicon 搭載の機種では誤った値が返ってきていました。
Jamf Pro の拡張属性という機能を使うことでデバイスの状態を定期的に取得してインベントリに表示することができるため、その機能を使って Mac のバッテリー状態をモニタリングする方法について書いていこうと思います。

前提

最近 Jamf Pro の 11.11.0 がリリースされ 、コンピュータインベントリに Battery Health (バッテリーヘルス)という項目が追加されました。
これはバッテリーの充電可能容量ではなく 、macOS のシステム設定やシステム情報で確認できるバッテリーの正常性を確認できる項目と同じ情報になります。
Jamf Pro リリースノート 11.11.0 | learn.jamf.com

コンピュータ インベントリ ハードウェアペイロード Jamf Pro 11.11.2

*11月27日現在、検証環境の 11.11.2 では正しく機能しているが、本番環境の 11.11.1 ではunknown と表示されている。

Apple Silicon 搭載の macOS 14.4 以降のデバイスのみがサポート対象の機能ですが、Intel Mac が今後増えることはなく、また OS もアップデートを行う事を考えると、今回の記事の内容がそのまま役に立つケースは無いかもしれません。
ただ今回取り上げる system_profilerplutilplutilを使って情報を取得する方法は他にも応用できるかなと思い、記事の最後の方でも例をあげています。

Mac のバッテリー状態の確認方法

前述の通り、MacBookはバッテリーの性能が低下すると修理推奨の表示が出ます。これは主にバッテリーの充電可能容量が設計された最大容量の80%を下回っている場合に表示されます。
macOS のシステム設定やシステム情報など、GUI 上でバッテリーの状態を確認することができますが、 Jamf Pro の拡張属性として値を取得するためには スクリプト として実行する必要があります。

システム設定 バッテリー
システム情報 電源 バッテリー情報

バッテリーの状態

system_profiler コマンドで取得することができます。

system_profiler SPPowerDataType

を実行すると、中に Health Information という項目があります。
以下の例は Health Information 以外を省略しています。

・Apple Silicon 搭載 バッテリー正常

Health Information:
  Cycle Count: 88
  Condition: Normal
  Maximum Capacity: 83%


・Intel 搭載 バッテリー修理推奨

Health Information:
  Cycle Count: 745
  Condition: Service Recommended

バッテリーが修理推奨のステータスの場合、ConditionService Recommended になります。
また、Apple Silicon と Intel で表示される内容に差異があり、Health Information の場合は Apple Silicon のデバイスのみ Maximum Capacity がリストされます。

現在の最大バッテリー容量

前述の通り system_profiler で取得した情報の中にある Maximum Capacity という値は Apple Silicon 搭載の MacBook でのみ取得可能です。
Jamf Pro のコンピュータインベントリ内の Hardware に Battery Capacity (バッテリー容量)という項目もあるのですが、こちらも前述の通り Intel Mac では正しく表示されているものの、Apple Silicon だと誤った値になっています。

Jamf Pro コンピュータインベントリ ハードウェア Intel CPU 搭載 Mac
Jamf Pro コンピュータインベントリ ハードウェア Apple Silicon 搭載 Mac

状態さえ取れていれば現在の最大バッテリー容量を取る必要性は低いですが、Jamf Pro のスマートコンピュータグループで Intel と Apple Silicon で 同じ値を参照したり、Apple Silicon でもバッテリー容量が 80% を切るよりも前に気付けるというメリットがあります。

Apple Silicon の場合はバッテリーの状態を取得したときに Maximum Capacity が取れているので例は割愛します。

Intelの場合は割とめんどうで、 ioreg コマンドを使って 現在の最大容量と設計時の最大容量を取得し、その値から計算しました。各オプションについてはここでは割愛します。
ioreg -rlc AppleSmartBattery -w 0の実行結果から、MaxCapacity(またはAppleRawMaxCapacity)とDesignCapacityを探します

"MaxCapacity" = 3424
"AppleRawMaxCapacity" = 3424
"DesignCapacity" = 5088

3424 / 5088 * 100 = 67.2955974843

ということで、Jamf Pro で表示されている数値とほぼ一致しました。
Apple Silicon だと MaxCapacity が 100 となっていたり、 AppleRawMaxCapacity で計算しても system_profiler や GUI 上で確認した値と少しずれているため今回は使いません。
また、 AppleRaw〜という key は他にもあるのですがこれはあとから追加されたもので、CPU アーキテクチャや OS のバージョンによって使えない事があるため使わなかったと記憶しています。

PermanentFailureStatus

充電できず、電源アダプタを抜くと即落ちるみたいな状態の Mac でシステム情報を見ると表示されていることがあります。今まで 1000 台以上 の Mac を管理してきましたが、2台だけ見たことがあります。
状況から考えてバッテリーのコントローラーが壊れたとかそういうときに出てるようです。非常に稀なので気にしなくても良いかもしれません。

Intel Mac のバッテリー容量を計算したときと同じioreg -rlc AppleSmartBattery -w 0の実行結果から、 PermanentFailureStatus の項目を確認します。

"PermanentFailureStatus" = 0

この値は bool で返ってくるはずなのですが、私が見たケースは整数が返ってきていました(4096とか)。

余談:当時 Apple Open Source で該当しそうな部分(AppleSmartBattery.cpp)を読んでみたのですが、バッテリーから返ってきた値をそのまま出してそうな雰囲気でした。 最近のもの を見たらだいぶ変わっていたので今は挙動が違うかもしれません(ちゃんと読んでないです)。

コマンドの結果を扱いやすくする

system_profilerioreg はオプションで JSON や XML で出力できます。
どちらもmacOSに標準で入っている plutil で扱えるため、IT 管理者がリモートから実行するスクリプトとして使いやすく、grepawk などを使うより読みやすくなると思います。

system_profiler

system_profiler は オプションをつけることで XML(plist) や JSON で出力することができます。
今回は JSON で出力してみました。結果は省略しています。

system_profiler SPPowerDataType -json

・ Apple Silicon

{
  "SPPowerDataType" : [
    {
      "sppower_battery_health_info" : {
        "sppower_battery_cycle_count" : 88,
        "sppower_battery_health" : "Good",
        "sppower_battery_health_maximum_capacity" : "83%"
      }
    }
  ]
}   

・ Intel バッテリー修理推奨

{
  "SPPowerDataType" : [
    {
      "sppower_battery_health_info" : {
        "sppower_battery_cycle_count" : 745,
        "sppower_battery_health" : "Check Battery",
      }
    }
  ]
}   

JSON や XML で出力すると、key や value の表記が変わります。
例えばバッテリーの状態はオプションをつけない場合は項目が Condition、 値が NormalService Recommended でしたが、JSON や XML の場合は key がsppower_battery_health, value がGoodCheck Batteryのようになっています。

ioreg

ioreg-a オプションをつけることで XML(plist)で出力することができます。
結果は省略しています。

ioreg -arlc AppleSmartBattery -w 0
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
  <dict>
    <key>DesignCapacity</key>
    <integer>5088</integer>
    <key>MaxCapacity</key>
    <integer>3424</integer>
    <key>PermanentFailureStatus</key>
    <integer>0</integer>
  </dict>
</array>
</plist>

plutil で目的の値を取得する

JSON や XML で出力した結果と目的の key のパスを plutil に渡すことで value を取得します。
plutil はオプションに -をつけることで標準入力を受け取ります。

・ system_profiler

% system_profiler SPPowerDataType -json |
  plutil -extract SPPowerDataType.0.sppower_battery_health_info.sppower_battery_health raw - 

Good

・ ioreg

% ioreg -arlc AppleSmartBattery -w 0 |
  plutil -extract 0.PermanentFailureStatus  raw -

0

Jamf Pro に拡張属性として追加する

拡張属性に登録するシェルスクリプトの例になります。
Jamf Pro の拡張属性についてはドキュメントをご参照下さい。

learn.jamf.com

バッテリーの状態

PermanentFailureStatus0 のときはバッテリーの状態のみを、それ以外のときはPermanentFailureStatusとバッテリーの状態を両方表示するようにしています。

permanentFailureStatus=$(ioreg -arlc AppleSmartBattery -w 0 |
                         plutil -extract 0.PermanentFailureStatus raw -  )
batteryHealth=$(system_profiler SPPowerDataType -json |
                plutil -extract SPPowerDataType.0.sppower_battery_health_info.sppower_battery_health raw -  )

if [[ $permanentFailureStatus != "0" ]]; then
    result="Battery Permanent Failure, Code: ${permanentFailureStatus}\nBattery Health: ${batteryHealth}"
else
    result="$batteryHealth"
fi
echo "<result>$result</result>"

バッテリーの最大容量

CPU によって処理を変えています。
拡張属性の Data Type を Integer にすることでスマートコンピュータグループなどで (例えば値が85未満のデバイスをリストするなど) 扱いやすくなります。
ただ、拡張属性の Data Type が Integerでも、% がついたままだと String として扱われてしまうため、Apple Silicon の場合は % を削除しています。

arch=$(/usr/bin/arch)
if [[ "$arch" == "arm64" ]]; then
    result=$(system_profiler SPPowerDataType -json |
             plutil -extract SPPowerDataType.0.sppower_battery_health_info.sppower_battery_health_maximum_capacity raw - |
             tr -d '%' )

elif [[ "$arch" == "i386" ]]; then
    resutlsBatteryInfo=$(ioreg -arlc AppleSmartBattery -w 0)
    maxCapacity=$(plutil -extract 0.MaxCapacity raw - <<< $resutlsBatteryInfo)
    designCapacity=$(plutil -extract 0.DesignCapacity raw - <<< $resutlsBatteryInfo)
    result=$(echo "scale=2; $maxCapacity/$designCapacity*100" | bc | awk '{printf("%d\n",$1)}')
fi
echo "<result>$result</result>"

デスクトップマシンの場合はバッテリーの情報が無くエラーになるため、環境にデスクトップマシンがある場合はモデル名を取得して条件分岐するなど考慮が必要です。
モデルを取得する方法は以下のような例があります。

% system_profiler SPHardwareDataType  -json  | plutil -extract SPHardwareDataType.0.machine_model raw -
MacBookPro18,4

表示例

バッテリー 正常
バッテリー 修理推奨

スマートコンピュータグループを作成する

作成した拡張属性を使用してスマートコンピュータグループを作成できます。
スマートコンピュータグループを作成することで、 Jamf Pro ダッシュボードに表示したり、グループに変更があったらメールで通知したりできます。
Jamf Pro のスマートコンピュータグループについてはドキュメントをご参照下さい。
learn.jamf.com

バッテリーの状態

正常な場合は値が Good になっているので、それ以外の場合を指定します。

バッテリー状態のスマートコンピュータグループ クライテリアの例

バッテリーの最大容量

結果を Integer で返しているため、クライテリアに不等号が使えます。例は 85 未満をリストする場合です。

バッテリー最大容量のスマートコンピュータグループクライテリアの例

バッテリー以外の情報を取得する例

先ほど Mac のモデルを取得する例を出しましたが、他にもplutil を使うことでシンプルに書くことができます。
デバイスやアプリケーションの設定や情報などを取得する例をいくつかあげてみます。

MacBook のキーボードレイアウトを取得する

iogeg コマンドを使って、 MacBook のキーボードレイアウトを取得するコマンドをシンプルに書くことができます。

・ JIS の結果

% ioreg -arln AppleHIDKeyboardEventDriverV2 | plutil -extract 0.KeyboardLanguage raw -
Japanese Keyboard

・ US の結果

% ioreg -arln AppleHIDKeyboardEventDriverV2 | plutil -extract 0.KeyboardLanguage raw -
U.S.

plist の値を参照する

plist が存在する場合、 plutil を使用して設定値や状態などを確認することができます。
例として Google Chrome のバージョンを見てみます。

% plutil -extract KSVersion raw /Applications/Google\ Chrome.app/Contents/Info.plist
131.0.6778.86

Web API のレスポンスを扱う

シェルスクリプトで Web API のレスポンスを扱う場合にも plutil が便利なケースがあります。
例として Google Chrome の VersionHistory API から macOS 版の最新バージョンを取得してみます。

% curl -s https://versionhistory.googleapis.com/v1/chrome/platforms/mac/channels/stable/versions | plutil -extract versions.0.version raw -
131.0.6778.86

まとめ

  • Jamf Pro の拡張属性を使うことで、管理者が Mac の状態や組織で必要な設定について把握しやすくなる
  • macOS の場合 plist を参照することが多いので、 plutil を使うことで読みやすくシンプルに書くことができるケースがある

ちょっと長くなってしまいましたが、この記事がどなたかのお役に立つことがあれば幸いです。

宣伝:社内IT分野のソフトウェアエンジニアを募集しています

STORES のコーポレートエンジニアは自分たちの日々の仕事はもちろん、全社での業務への AI 活用を進めるため、ソフトウェア開発ができる仲間を探しています。
今回の内容は AI と関係無いのですが、根幹にはユーザーの体験を良くしてより良い環境を作っていきたいという思いがあり、これは STORES が掲げる "Just for Fun" というミッションにも繋がっています。
私達のお仕事に興味を持ってくださった方がいらっしゃいましたら、ジョブディスクリプションのページをご覧いただければと思います。

hrmos.co