はじめに
macbook airを持ち歩いて、スマホのbluetooth経由でテザリングしていたのだけれども、スリープしたり、macから離れる度に接続が切れて、切れて、切れまくる!
毎回、ステータスバーのbluetoothのアイコンをクリックして接続し直していたんだけれども、あまりにも頻繁なので、wifiに接続できない時は自動的にbluetoothテザリングに接続するシェルスクリプトを書いてみた。
bitbarというステータスバーにスクリプトを常駐させるアプリを使って、スクリプトを実行すれば、スマホだけで常時接続なmacのできあがり。ありがとう、bitbar!
できること
- wifiに接続できない場合、bluetoothテザリングに接続。wifiに接続できるようになったら、bluetoothテザリングを切断。bitbarからこのスクリプトを起動することで、常駐してこの作業を定期的に実行できるようになる。
- 安全のため、1回の最大連続通信量(300MB)を超えると自動的に切断するようにした。(再度使用可能にするには、メニューから Reset DATA OVER を選択してください)
必要な環境
- MacBook Air (13-inch, 2017)〜たぶんmacbookでも動くと思う
- macOS High Sierra 10.13.3
- bitbarがインストール済み https://getbitbar.com
- macがスマホとbluetooth接続でテザリングできるように設定されている
- データ通信の定額プランに加入している(問答無用で勝手に接続しに行くスクリプトなので、これとっても重要!従量制の人は決して使ってはいけない)
スマホはandroidスマホ Nexus 5X で確認(iphoneは持っていないので試せなかった)- スマホはiphoneとandroid Nexus 5Xで確認
スクリプトの使い方
1. bitbarをインストール
- メニューバーからスクリプトを定期的に起動するアプリ bitbar を https://getbitbar.com からダウンロードし、インストール。
2. wifiに接続できない時は自動的にbluetoothテザリングに接続するシェルスクリプト btz.30s.sh を設定
- 下にあるシェルスクリプトをテキストエディットにコピペして、btz.30s.shというファイル名で、ホームフォルダ以下の適当なフォルダに保存。
- btz.30s.shの先頭部分にある必須設定の欄のBT_DEVICEに自分のスマホのデバイス名を記入して保存。
- BT_DEVICEは、システム環境設定 -> Bluetoothで表示されているデバイス名を大文字、小文字、スペースも含めて正しく記述してあげてください。
- btz.30s.shはシェルスクリプトなので、ファイルに実行権限を付与してあげる。(ターミナルで先程作成したディレクトリに移行後、chmod +x btz.30s.sh)
- 監視間隔はシェルスクリプトのファイル名の30sの部分で設定(30sで30秒毎に起動。40sなら40秒毎に起動。基本デフォルトでOK)。
3. bitbarの設定
- bitbarのメニューのPreferences -> Change Plugin Folder...で先程作成したフォルダを指定。
- 画面最上段のステータスバーに青色で[BT tethering OFF]が表示されたら、WI-Fiをオフにする。30秒以内にbluetoothテザリングが開始されたら成功。
シェルスクリプト
- 2018年05月07日 関数名を訂正しました (bt_swith_onoff → bt_switch_onoff) khskさんありがとう。
- 2019年02月17日 iphoneでも動作を確認。
btz.30s.sh
# !/bin/bash
# 必須設定(必ず自分で設定してください)
#############################################################################
# テザリングするときに使用するBluetoothデバイス名
# (例 BT_DEVICE="iphone" or BT_DEVICE="Nexus 5X")
BT_DEVICE="iPhone"
#############################################################################
#############################################################################
# Btz.30s.sh - bluetoth tethering auto connect.
# 2018/01/10-04/30. edited by simizut.
# for mac osx 10.13.2(high sieera)
# Usage : btz.30s.sh [-hwWbB]
# * 30s is bitbar refresh time.
# no option : no wifi. connect to bluetooth tethering.
# -h : help
# -w : check wifi is on (return 0:on 1:off)
# -W : switch wifi on off (return 0:on 1:off)
# -b : check bluetooth is on (return 0:on 1:off)
# -B : switch bluetooth on off (return 0:on 1:off)
# -n : ckeck network is on (return 0:on 1:off)
# -M : ckeck bluetooth tethering MB
#############################################################################
# その他設定
#############################################################################
# ■以下は変更しなくても大丈夫
# 1回の最大連続通信制限[MB(メガバイト)] (例 MAXDATA="300")
MAX_DATA="300"
# ログファイル名 .(ドット)で始まること
LOG_FILE=".btz.log"
# 最大通信量を超えた場合にフラグとして作成されるファイル名 .(ドット)で始まること
ERR_FILE=".btz.err"
# bluetooth接続のメニュー文字列
BT_ON_MENU_STR="ネットワークへ接続"
# bluetooth切断のメニュー文字列
BT_OFF_MENU_STR="ネットワークから接続解除"
#############################################################################
# 以下、スクリプト本体
#############################################################################
# 実行ファイルのディレクトリをカレントディレクトリに
HOME_DIR=`dirname $0`
cd ${HOME_DIR}
# Wi-Fi、Bluetoothのネットワークデバイス名の取得
WIFI_DEV_NAME=$(networksetup -listnetworkserviceorder | grep '^(Hardware Port: Wi-Fi' | grep -o 'en.')
BT_DEV_NAME=$(networksetup -listnetworkserviceorder | grep '^(Hardware Port: Bluetooth' | grep -o 'en.')
# 以下、関数()
#############################################################################
# network_is_on - check network is on
# Arguments:
# None
# Returns:
# 0:network on
# 1:network off
#############################################################################
network_is_on(){
ping yahoo.co.jp -c1 -Q > /dev/null 2>&1
if [ $? -eq 0 ]; then return 0
else return 1
fi
}
#############################################################################
# bt_is_on - check bluetooth tethering is on
# Arguments:
# None
# Returns:
# 0:bluetooth tethering on
# 1:bluetooth tethering off
#############################################################################
bt_is_on(){
networksetup -getinfo 'Bluetooth PAN' | Grep -wq '^IP address'
if [ $? -eq 0 ]; then return 0
else return 1
fi
}
#############################################################################
# wifi_is_on - check wifi is on
# Arguments:
# None
# Returns:
# 0:wifi on
# 1:wifi off
#############################################################################
wifi_is_on(){
networksetup -getinfo 'Wi-Fi' | grep -wq '^IP address:'
if [ $? -eq 0 ]; then return 0
else return 1
fi
}
#############################################################################
# bt_switch_onoff - switch bluetooth tethering on off
# Arguments:
# None
# Returns:
# 0:bluetooth tethering on
# 1:bluetooth tethering off
#############################################################################
bt_switch_onoff(){
osascript <<EOD
try
tell application "System Events"
tell application process "SystemUIServer"
tell menu bar 1
set bt to item 1 of (menu bar items whose description is "bluetooth")
end tell
tell bt
click it
click menu item "${BT_DEVICE}" of menu 1
# Nexus5X set str to name of (menu item 2 of menu 1 of menu item "${BT_DEVICE}" of menu 1)
# iPhone set str to name of (menu item 1 of menu 1 of menu item "${BT_DEVICE}" of menu 1)
try
set str to name of (menu item "${BT_ON_MENU_STR}" of menu 1 of menu item "${BT_DEVICE}" of menu 1)
on error
set str to name of (menu item "${BT_OFF_MENU_STR}" of menu 1 of menu item "${BT_DEVICE}" of menu 1)
end try
click menu item str of menu 1 of menu item "${BT_DEVICE}" of menu 1
display notification str with title "${BT_DEVICE}"
end tell
end tell
end tell
on error
display notification "error" with title "${BT_DEVICE}"
end try
EOD
sleep 8s #ネットワークが安定するまでウエイト
bt_is_on
}
#############################################################################
# wifi_switch_onoff - switch wifi on off
# Arguments:
# None
# Returns:
# 0:wifi on
# 1:wifi off
#############################################################################
wifi_switch_onoff(){
networksetup -getairportnetwork ${WIFI_DEV_NAME} | Grep -wq '^Current Wi-Fi Network'
if [ $? -eq 0 ]; then networksetup -setairportpower ${WIFI_DEV_NAME} off
else networksetup -setairportpower ${WIFI_DEV_NAME} on
fi
wifi_is_on
}
#############################################################################
# bit_menu - display bitbar menu
# Arguments:
# bitbar title text (ex. foo)
# Returns:
# None
#############################################################################
bit_menu(){
if [ ! -e ${LOG_FILE} ]; then # ログファイルがなければ、通信量は0に
today_byte=0
month_byte=0
else
today_byte="$(cat ${LOG_FILE} |grep ^$(date '+%Y/%m%d') |awk '{a+=$2} END{print a}')"
month_byte="$(cat ${LOG_FILE} |grep ^$(date '+%Y/%m') |awk '{a+=$2} END{print a}')"
fi
echo "$1"
echo '---'
echo "BT Tethering auto connect : Check now| refresh=true"
echo "Tethering Data : ${today_byte}MB/DAY ${month_byte}MB/MONTH"
if [ -e $ERR_FILE ]; then # フラグファイルが存在する場合の処理
echo "Reset DATA OVER | terminal=false bash=/bin/rm param1=${HOME_DIR}/${ERR_FILE} refresh=true"
fi
}
#############################################################################
# bt_mbyte - calculate bluetooth tethering Mbyte
# Arguments:
# None
# Returns:
# None (bletoothパケット通信量(MB)を標準出力に表示)
#############################################################################
bt_mbyte(){
ipt_byte=$(netstat -I ${BT_DEV_NAME} -b | grep '<Link' | awk '{print $7}')
out_byte=$(netstat -I ${BT_DEV_NAME} -b | grep '<Link' | awk '{print $10}')
all_mbyte=$(((ipt_byte+out_byte)/1048576)) #1048576 is 1024*1024 ( byte -> kbyte -> mbyte )
echo ${all_mbyte}
}
#############################################################################
# save_log - save bluetooth traffic log
# Arguments:
# None
# Returns:
# None
#############################################################################
save_log(){
date_str=$(date '+%Y/%m%d/%H%M' |tr -d '\n') # 記入するログデータの作成
byte_str=$(bt_mbyte |tr -d '\n')
log_line="${date_str} ${byte_str} MB"
if [ ! -e ${LOG_FILE} ]; then # ログファイルがなければ、ファイルを作成
echo "DATE traffic" >> ${LOG_FILE}
echo "----------------------" >> ${LOG_FILE}
echo "${log_line}" >> ${LOG_FILE} # ログデータの記入(これは必ず残る)
echo "${log_line}" >> ${LOG_FILE} # ログデータの記入(これは消えるかも)
fi
last_bt_byte=$(tail -n1 ${LOG_FILE} | awk '{print $2}') # ログの最終行から前回のデータ通信量を抽出
if [ $(($(bt_mbyte))) -ge $((${last_bt_byte})) ]; then # データ通信量が増加していたら、最終行を削除
sed -i '' -e '$d' ${LOG_FILE} # BSD GREPなので上書きオプション-iに''が必須
fi
echo "${log_line}" >> ${LOG_FILE} # ログデータの記入
}
# コマンド引数処理
#############################################################################
CMDNAME=`basename $0`
while getopts hwWbBpnM OPT
do
case $OPT in
"h" ) FLG_H="TRUE" ;;
"w" ) FLG_w="TRUE" ;;
"W" ) FLG_W="TRUE" ;;
"b" ) FLG_b="TRUE" ;;
"B" ) FLG_B="TRUE" ;;
"p" ) FLG_p="TRUE" ;;
"n" ) FLG_n="TRUE" ;;
"M" ) FLG_M="TRUE" ;;
* ) echo "Usage: $CMDNAME [-hwWbBpnM]" 1>&2
exit 1 ;;
esac
done
if [ "$FLG_H" = "TRUE" ]; then
echo "Usage: $CMDNAME [-hwWbBpnM]" 1>&2
echo " no option : no wi-fi. connect to bluetooth tethering" 1>&2
echo " -h : help" 1>&2
echo " -w : check wifi is on (return 0:on 1:off)" 1>&2
echo " -W : switch wifi on off (return 0:on 1:off)" 1>&2
echo " -b : check bluetooth tethering is on (return 0:on 1:off)" 1>&2
echo " -B : switch bluetooth tethering on off (return 0:on 1:off)" 1>&2
echo " -n : ckeck network is on (return 0:on 1:off)" 1>&2
echo " -M : ckeck bluetooth tethering MB" 1>&2
exit 0
fi
if [ "$FLG_w" = "TRUE" ]; then
wifi_is_on
exit $?
fi
if [ "$FLG_W" = "TRUE" ]; then
wifi_switch_onoff
exit $?
fi
if [ "$FLG_b" = "TRUE" ]; then
bt_is_on
exit $?
fi
if [ "$FLG_B" = "TRUE" ]; then
bt_switch_onoff
exit $?
fi
if [ "$FLG_n" = "TRUE" ]; then
network_is_on
exit $?
fi
if [ "$FLG_M" = "TRUE" ]; then
bt_mbyte
exit 0
fi
# 以下、メイン
#############################################################################
# wifiの接続状況に応じてbluetoothテザリングを接続、切断
# それに応じて、bitbar のメニュー表示を変更
#############################################################################
# スリープ復帰後を考えて、ネットワークが安定するまでウエイト
sleep 5s
# 1回の通信が最大連続通信量を超えた時、bluetoothテザリングを強制切断
# 今後、bluetoothテザリングできないよう、フラグとなるファイルを作成
if (($(bt_mbyte)>${MAX_DATA})); then
touch $ERR_FILE
fi
if [ -e $ERR_FILE ]; then
bit_menu "[DATA OVER]| color=red"
bt_is_on && bt_switch_onoff
exit 1
fi
# wifiの接続状況に応じてbluetoothテザリングを接続、切断
# それに応じて、bitbar のメニュー表示を変更
if wifi_is_on; then # wifi is on
bit_menu "[Tethering OFF]| color=blue"
bt_is_on && bt_switch_onoff # -bluetooth is on なら bluetoothの切断
else # wifi is off
if bt_is_on; then # -bluetooth is on
bit_menu "[Tethering $(bt_mbyte)MB]| color=red"
: # --何もしない
else # -bluetooth is off
if network_is_on; then # --Eather is on
bit_menu "[Tethering OFF]| color=blue"
: # ---何もしない
else # --Eather is off.
if bt_switch_onoff; then # ---bluetoothの接続処理をして、その結果on
bit_menu "[Tethering $(bt_mbyte)MB]| color=red"
else # ---bluetoothの接続処理をして、その結果off
bit_menu "[Tethering can't connect]| color=red"
fi
fi
fi
fi
save_log # bluetooth通信量をログに書き込む
exit 0
apple scriptでbluetooth接続するところは「Takayuki Yuasa」さんの 「【Mac】 Bluetoothテザリングの接続にショートカットキーを割当」を参考にしました。この部分はシェルスクリプトで実現できなかったので、とても感謝してます。
http://ytkyk.info/blog/2015/03/06/bluetooth_tethering_shortcut_key/