macbook airでWi-Fiに繋がらない時、自動的にbluetoothテザリングに接続するシェルスクリプト

# はじめに
　macbook airを持ち歩いて、スマホのbluetooth経由でテザリングしていたのだけれども、**スリープしたり、macから離れる度に接続が切れて、切れて、切れまくる！**

　毎回、ステータスバーのbluetoothのアイコンをクリックして接続し直していたんだけれども、あまりにも頻繁なので、wifiに接続できない時は**自動的にbluetoothテザリングに接続するシェルスクリプト**を書いてみた。

　[bitbar](https://getbitbar.com)というステータスバーにスクリプトを常駐させるアプリを使って、スクリプトを実行すれば、スマホだけで常時接続なmacのできあがり。ありがとう、bitbar！

[bitbarからスクリプトを実行]
![dtz_image.jpeg](https://qiita-image-store.s3.amazonaws.com/0/254003/df260f3f-ec79-88f8-db03-8f0c3a3015d6.jpeg)

[メニュー画面]
![btz_menu_image.jpeg](https://qiita-image-store.s3.amazonaws.com/0/254003/f540856c-d353-8419-9d77-ddce8ddc8d40.jpeg)



# できること
 * wifiに接続できない場合、bluetoothテザリングに接続。wifiに接続できるようになったら、bluetoothテザリングを切断。bitbarからこのスクリプトを起動することで、常駐してこの作業を定期的に実行できるようになる。
 * 安全のため、１回の最大連続通信量(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でも動作を確認。

```bash: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/