以下、実はうまくいかずこちらに別案を書きました。
今朝、電車で以下を書きまして。昨晩から朝にかけて頑張ったけど何もできんかった。。。という残念ログだったのです。
Macbook蓋を閉じてもスリープするがネットワークは生きている問題 - memorandums
ふと「もう自分でやればよいのでは?」と思ったわけです。それから1時間くらいかけてChatGPTくんとあれこれやって作ったのが以下です。
まず、仕様を書きます(問題感や上記のエントリーに書いていますので 参考まで)。もし必要な方はどうぞ使って下さい。責任は持てませんが。
仕様
仕様を入力してChatGPTに整理してもらったのが以下の表です。状態抜けはないようです。最初、単純に蓋の開閉でWifiをオンオフしてみたのですが、そういえば研究室ではクラムシェルモードで使うやん。。。と思い出したのです。そうすると蓋を閉じた状 態でUSB-Cを接続するとWifiがオフのままになりまして。。。そりゃあかんといことで修正しました。ちょっと複雑になったわけです。なので1時間かかりました。何やってんだか。。。
| 蓋の状態 | 外部ディスプレイ | Wi-Fi状態 | 説明 |
|---|---|---|---|
| 開いてる | なし | ON | 通常使用 |
| 開いてる | あり | ON | 通常使用+拡張ディスプレイ |
| 閉じてる | なし | OFF | 本当にスリープ前提、不要な通信を防ぐ |
| 閉じてる | あり | ON | クラムシェルモードでの使用 |
スクリプト
以下、スクリプトの配置場所は自分で変えてください。私は面倒なのでホームの直下に置きました。
~/lid_monitor.sh
#!/bin/bash # 初期状態を空に last_lid_state="" last_display_state="" while true; do # 蓋の状態を取得(Yes = 閉じている) lid_closed=$(ioreg -r -k AppleClamshellState | grep -i "AppleClamshellState" | awk '{print $NF}') lid_state=$([ "$lid_closed" == "Yes" ] && echo "closed" || echo "open") # 外部ディスプレイ(JAPANNEXT.MNT)の接続確認 external_display_connected=$(system_profiler SPDisplaysDataType | grep -c "JAPANNEXT.MNT") display_state=$([ "$external_display_connected" -ge 1 ] && echo "connected" || echo "disconnected") # 状態に変化があった場合のみ処理 if [[ "$lid_state" != "$last_lid_state" || "$display_state" != "$last_display_state" ]]; then echo "State changed: Lid=$lid_state, Display=$display_state" if [[ "$lid_state" == "closed" && "$display_state" == "disconnected" ]]; then echo "-> Turning Wi-Fi OFF" networksetup -setairportpower en0 off else echo "-> Turning Wi-Fi ON" networksetup -setairportpower en0 on fi # 状態を更新 last_lid_state=$lid_state last_display_state=$display_state fi sleep 60 done
あと、せっかくなので自動起動するようにしました(してもらいました)。
~/Library/LaunchAgents/com.user.lidmonitor.plist
IDは変えるべきでしょうけど面倒なのでChatGPTのままにしています。
<?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"> <dict> <key>Label</key> <string>com.user.lidmonitor</string> <key>ProgramArguments</key> <array> <string>/Users/ktakahas/lid_monitor.sh</string> </array> <key>RunAtLoad</key> <true/> <key>KeepAlive</key> <true/> <key>StandardOutPath</key> <string>/tmp/lidmonitor.log</string> <key>StandardErrorPath</key> <string>/tmp/lidmonitor.err</string> </dict> </plist>
Macを起動したときにはこれを読み込んでくれるでしょうけど、手動でロード・アンロードするには以下のコマンドになります。
launchctl load ~/Library/LaunchAgents/com.user.lidmonitor.plist
launchctl unload ~/Library/LaunchAgents/com.user.lidmonitor.plist
起動(load)すると、直後から監視が始まります。ログは/tmp/lidmonitor.logに吐き出されるので見てみるとよいです。
注意点
外部ディスプレイの接続状態は上記は僕の環境専用になっています。接続状態を取る方法がいくつかあったのですがどれもうまく行きませんでした。しかたがなく(ChatGPTも折れて最後の手段的なことを言ってきまして同じ考えだなと思ったり、まさにコパイロットですなぁ)、クラムシェルモードで接続する私の環境のディスプレイ名が出たら「外部ディスプレイが接続された」と認識するようにしています。
なので、ご自身で上記スクリプトを利用する場合は、以下のコマンドをターミナルで実行してみてください。
system_profiler SPDisplaysDataType
私の場合は以下のように表示されました。クラムシェルにした状態なので内蔵ディスプレイは表示されていません。この状態で外部ディスプレイ固有の何か(メーカー名?)を入れればいいと思います。上記のスクリプトに「JAPANNEXT.MNT」と書かれている場所がありますので、そこをご自身のものに書き換えれば動作するはずです。
Graphics/Displays:
Apple M4:
Chipset Model: Apple M4
Type: GPU
Bus: Built-In
Total Number of Cores: 10
Vendor: Apple (0x106b)
Metal Support: Metal 3
Displays:
JAPANNEXT.MNT:
Resolution: 2560 x 1440 (QHD/WQHD - Wide Quad High Definition)
UI Looks like: 2560 x 1440 @ 75.00Hz
Main Display: Yes
Mirror: Off
Online: Yes
Rotation: Supported
面倒ですが、実際にloadする前に、lid_monitor.shを動かしてみて(そのときは確認に時間がかかるのでsleep 60をsleep 3とかに書き換えるとよいと思います。
それでちゃんと動作するかを確認してからloadしてください。
とりあえず気になってた問題が1つ解消したという件でした。
おしまい。
■2025/8/2 追記
1日使ってみた。学内でMacbookを研究室から持ち出して会議とかで開くとWifiが切断されたままになって。。。上記だと1分経つとやっとWifiがオンになる。。。これはちょっと嫌。
で、色々対策を考えてみたんだけど、結局、やりたかったことは、電車内でモバイルルータを使用していたときにルータを切り忘れてMacbookを閉じてもWifiが接続されたままになりモバイルルータがスリープしないという問題だった。この特定された条件だけ使用できればよいはずだということがわかってきた。
さらにいうと、電車から降りてMacbookを開いたときにWifiをオンにすると監視時間のラグがあるのでどうしてもsleep分待たなければならない。それもなんか変。じゃ、Wifiをオン・オフするのではなく、APから切断できればいいはずだと気付いた。
MacでWifiのAP名を取得するコマンドはairportコマンドが使えるとChatGPTが示したがairportは廃止予定だとは知っていたので指摘すると別のコマンドを教えてくれたけど結局動作しなかった。Geminiに聞くと他のいくつか案を提示してくれた。1つがsudoも不要でよかったので採用(ipconfigを使う)。
あと、Wifiの接続を切断することは結構難しいらしいので、Geminiの提案では切入すればいいのでは?だった。なるほどだ。モバイルルータへの自動接続は切っているのでMac側でWifiを切入すれば切断されたあとモバイルルータへの接続は維持されない(手動で再度接続する必要はあるけどそれはかなりレアケース)。
蓋を閉じたときだけ切入する。なので蓋を開けたときには何もしなくてよくなるためコードとしては管理(アクション)すべき状態数が減ったのでよい判断だった(コード生成はGenAIが有効だけどやはり仕様をどうすべきは人が考える必要がある、ここはこれからのプログラミング教育の要点になるのだろうと改めて思った、要件抽出・要件定義ができる人材は企業でも少ないと元上司がこぼしていた、GenAIの助力は受けても物事を考え整理する力・経験は必要だと思う)。
ということで、lid_monitor.shを以下のように書き換えた。まあいい感じに動作したのでこれでしばらく使ってみる。
#!/bin/bash # 初期状態を空に last_lid_state="" while true; do # 現在の時刻(時)を取得(00〜23の形式) HOUR=$(date +%H) # 夜中の時間帯(0〜7時)なら処理しない if [ "$HOUR" -le 7 ]; then echo "Skip($HOUR)" sleep 3600 continue fi # SSID取得(Sequoia以降じゃないと動作しないとか?) ssid=$(ipconfig getsummary en0 | awk -F' SSID : ' '/ SSID : / {print $2}') # SSIDがモバイルWifi? if [ "$ssid" == "xxxxxxxx" ]; then # 蓋の状態を取得(Yes = 閉じている) lid_closed=$(ioreg -r -k AppleClamshellState | grep -i "AppleClamshellState" | awk '{print $NF}') lid_state=$([ "$lid_closed" == "Yes" ] && echo "closed" || echo "open") # 状態に変化があった場合のみ処理 if [ "$lid_state" != "$last_lid_state" ]; then if [ "$lid_state" == "closed" ]; then networksetup -setairportpower en0 off sleep 2 networksetup -setairportpower en0 on echo "-> Disconnected from the access point" fi # 状態を更新 last_lid_state=$lid_state fi fi sleep 60 done