HomeKitからMacをスリープ解除する

非対応品を使う
M4 Mac mini

Macはスリープで使う人が多いらしいです。スリープならばすぐに復帰するので便利です。ただし、外出先から家のLANにVPN接続して、スリープ中のMacを使おうとすると受け付けられません。そこで、HomeKitに「MacのスリープをOn/Off」するボタンを作りました。これで世界中のどこからでも、家のMacのスリープをOn/Offできます。

M4 Mac miniはスリープ運用

M4 Mac miniを買いました。今までは、M1のMacbookとIntelのデスクトップを使ってました。AppleのApple Silicon移行が進捗して、Intelで使えない機能が増えてきたので、そろそろデスクトップもApple Siliconにすべきかと思いました。

iMac以外のヘッドレスデスクトップMacの現行機種は、Mac mini, Mac Studio, Mac Proの3機種です。Proは流石に高価すぎて無理でした。Studioは、適度なサイズで、SSDがソケットで拡張性があって良いのですが、 全部バラさないと、ファンの埃を掃除できない設計が嫌かと思いました。筐体上面板が外れるようになっていれば掃除が簡単なのですが、見た目重視のデザインなので無理なのでしょう。miniは、長らく筐体デザインが昔のままでした。画期的で美しいデザインなのですが、DVDドライブ内蔵を前提とした設計なので、最近のminiでは持て余して、中がスカスカだったりしました。底の開口部が丸いのもオシャレですが、そのおかげでパーツの取り出し・取り付けが、知恵の輪パズルのように困難でした。ところが、新型M4 Mac miniはこうした不満を一掃してくれました。コンパクトになって、裏蓋は全て外れて、さらにSSDがソケットになって拡張可能になりました。

唯一の問題点は、巷で話題になっているように、電源スイッチが底にあって押しにくいことです。ただ、いまどきの省電力なコンピュータは、電源On/Offでなくてスリープ解除/開始で運用するので、電源スイッチは使いにくくても構わないという意見もあります。iPhoneはスリープしか使わないので、同じ部品を使っているMacもスリープ運用で良いのかと思います。スリープなら、一瞬で復帰するので、使いやすいです。

HomeKitでスリープ制御

私の家のルーターはYAMAHA RTX 1200の中古を使っていて、これでL2TP over IPSec方式のVPNサーバを動かしています。なので外出先から家のLANに入って、Raspberry PiやMacを操作できます。でもスリープ中のMacには、ssh接続(リモートログイン)や、VNC接続(画面共有)ができません。

Macがスリープしている時は、ターミナルからsshコマンドを打っても返答がありません。画面共有 (VNC) を試みても、しばらく待たされた後、下のようにエラーダイアログが出ます。

こういうサーバー的な用途に使いたい場合は、本来ならばスリープさせない設定で使うべきです。「システム環境設定」「エネルギー」から、「ディスプレイがオフの時に自動でスリープさせない」の項目をOnにしておくと、スリープしない設定になります。ただ、外出先から接続することは滅多にないので、電気代がもったいない気がします。

そこでHomeKitからスリープ解除できれば、リモートアクセスが必要な場合に、どこからでも対応できるかと考えました。

スリープ解除する

Macのスリープは、WOL (Wake On LAN) の仕組みでスリープ解除できます。以前、WindowsマシンをHomeKitからWOL起動する記事を書きました。

「へいSiri, Windowsを点けて」でパソコンをWOL起動する
Wake On LANを使って、HomeKitからWindowsマシンを起動できるように設定しました。WindowsのWOLを有効化し、HomebridgeのWOLプラグインを設定します。ヘッドレス運用のWindowsHomeKitをメイン...

Windowsマシンの場合は、電源Offの状態からの起動ができました。Macでは、電源OffからのWOLをサポートしていません。スリープからの復帰のみ可能です。そこで前回記事と同様に、Homebridgeに、Homebridge WoLプラグインを導入して設定します。HomebridgeはRaspberry Piで動かしているので、以下のような構成になります。

Homebridgeの設定ファイルのaccessoriesの項目には、以下の内容を追記します。hostはMacのIPアドレス、macはMACアドレスです。

"accessories": [
  {
    "accessory": "NetworkDevice",
    "name": "MacMini",
    "host": "192.168.1.66",
    "mac": "aa:bb:cc:dd:ee:ff"
  } 
]

これでHomebridgeを再起動すると、MacやiPhoneのホーム.appに以下のようなスイッチが現れます。

Macがスリープしている時に、このスイッチをOnにすれば、WOLのマジックパケットがMacに送られて、スリープ解除します。Macの画面は復帰しませんが、sshやVCNを受け付けてくれるようになります。

スリープ状態を検知する

上記の設定では、Macがスリープ状態になっても、HomeKitの表示はいつもオンのままでした。

この原因は、WOLプラグインのデフォルト動作では、pingへの反応を見て、コンピュータのOn/Offを検知しているからです。M4 Mac miniで調べた限り、Macはスリープ状態でもpingには応答します。なので、表示がオンのままなのです。(WOLプラグインは、電源On/Offさせるのが目的なので、pingでOn/Off判定するのが妥当でした。)

今回、スリープ状態で使えなくて困っている機能は、sshとVNCです。なので、これらに反応する・しないを調べて、HomeKitの表示に反映させることにします。実際にはsshへの応答でスリープ検知することにしました。そこで、Raspberry Piからncコマンドを使うことにしました。

nc -z 192.168.1.66 22

ncコマンドは、上記の書式で、指定したIPアドレス(Macのアドレス)、sshのポート番号22が応答しているかどうかを教えてくれます。Homebridge設定ファイルの中で、pingコマンドの代わりにこれを使うことを、以下のようにして指定しました。

"accessories": [
  {
    "accessory": "NetworkDevice",
    "name": "MacMini",
    "host": "192.168.1.66",
    "mac": "aa:bb:cc:dd:ee:ff",
    "wakeGraceTime": 20, 
    "pingCommand": "nc -z 192.168.1.66 22",
    "pingCommandTimeout": 2,
    "pingInterval": 10
  } 
]

wakeGraceTimeは、WOLのマジックパケットを送った後、pingコマンドを使うまでの時間です。wakeに時間がかかる場合、ボタンのOn/Off表示が安定しないことを避けるための設定です。デフォルトは45秒ですが、スリープ解除は高速なので、20秒に設定しました。pingCommandTimeoutは、pingコマンド失敗判定の時間です。デフォルトは0秒なのですが、余裕を見て2秒にしました。pingIntervalは、pingコマンド間隔です。Mac動作の邪魔にならないように、デフォルト2秒より長くしておきました。

この結果、sshが受け付けられない状態になると、HomeKitのボタンがOff表示になりました。これで、Macがsshに応答することを、HomeKitボタンの状態で知ることができました。Macのスリープメニューを操作しても、ボタン状態が変化します。

スリープ状態の変化

このボタンを観察していると、WOL操作やMacのスリープメニュー操作に関わらず、スリープ状態が勝手に変化する現象に気づきました。

例えば、Macのメニューからスリープさせた後、しばらくするとボタンがOn状態になることがあります。おそらくは、「システム環境設定」「エネルギー」で、

「ネットワークアクセスによるスリープ解除」をOnにしていることが原因です。これにより、定期的にスリープ解除して、ネットワーク機能を動作させているものと思われます。この期間中は、sshもVCNも接続可能です。

逆に、HomeKitのボタンからスリープ解除してOn状態にした後、最短で30秒くらい放置すると、ボタンがOff状態になります。スリープ解除後に、すぐにssh接続すれば、ssh接続中はスリープ解除状態が保持されます。ネットワークアクセスが無い場合は、30秒程度でスリープ状態に戻るようです。

このように、スリープ状態で放置したMacが、リモートアクセス可能状態なのかどうかは、色々な条件で変化するようです。HomeKitのボタンからそれが検知できるのは、便利だと思いました。

スリープ開始する

ここまでで、

  • ボタンOnでスリープを解除すること
  • スリープ状態をボタンOn/Offに反映すること

ができましたので、最後に

  • ボタンOffでスリープ開始すること

も作り込むことにしました。時間が経てば自動的にスリープするので、必須な機能ではありません。でも、HomeKitボタンのOff操作に対応する機能も作っておきたかったです。

Raspberry Piから手動でMacをスリープさせるためには、sshで接続して、

sudo shutdown -s now

などすれば良いようでした。-sでスリープを指定します。ただこのためには、sshコマンドを打って、パスワードを入れて、shutdownコマンドを打って、パスワードを入れるなどの操作が必要です。

そういう操作を自動化するために、expectというツールがあるらしいです。そこで、Raspberry Piにexpectをインストールしました。

sudo apt install expect

そして、以下のようなシェルスクリプトファイルを作って、

#!/bin/bash

expect -c "
    spawn ssh 192.168.1.66
    expect \"Password:\"
    send \"hogehoge\n\"
    expect \"~ %\"
    send \"sudo shutdown -s +3s\n\"
    expect \"Password:\"
    send \"hogehoge\n\"
    expect \"~ %\"
    send \"exit\n\"
"

これを実行すれば、上記の手順を自動化できました。expectの行は、画面に表示されるプロンプトで、それが表示されたらsendの行を入力するという仕組みのようです。ここではshutdownでスリープするまでの時間を3秒にしてます。このショルスクリプトファイル名を、例えばsleep_macとして、ホームディレクトリに置いておきます。Homebridgeの設定ファイルに、以下のように追記すれば、HomeKitボタンをOffにした場合に、このファイルが実行されて、Mac側でshutdownコマンドを発行できるようになります。

"accessories": [
  {
    "accessory": "NetworkDevice",
    "name": "MacMini",
    "host": "192.168.1.66",
    "mac": "aa:bb:cc:dd:ee:ff",
    "wakeGraceTime": 20, 
    "pingCommand": "nc -z 192.168.1.66 22",
    "pingCommandTimeout": 2,
    "pingInterval": 10,
    "shutdownCommand": "/home/hoge/sleep_mac",
    "shutdownGraceTime": 10
  } 
]

まとめ

スリープ状態のMacをWOLでスリープ解除し、動作状態のMacをshutdownコマンドでスリープさせるボタンを、HomeKit上に作成しました。Macがssh受け入れ可能かどうかを検知して、ボタンのOn/Off状態に反映しています。これで、自宅でスリープしているMacに、外出先からアクセスできるようになりました。

コメント

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