Mac
テザリング
bluetooth
シェルスクリプト
BitBar

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

はじめに

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

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

 bitbarというステータスバーにスクリプトを常駐させるアプリを使って、スクリプトを実行すれば、スマホだけで常時接続なmacのできあがり。ありがとう、bitbar!

[bitbarからスクリプトを実行]
dtz_image.jpeg

[メニュー画面]
btz_menu_image.jpeg

できること

  • wifiに接続できない場合、bluetoothテザリングに接続。wifiに接続できる場合、bluetoothテザリングを切断。bitbarからこのスクリプトを起動することで、常駐してこの作業を定期的に実行できるようになるはず。
  • 安全のため、1回の最大連続通信量(300MB)を超えると自動的に切断するようにしてみた。(再度使用可能にするには、メニューから Reset DATA OVER を選択)
  • 監視間隔はファイル名の30sの部分で設定(30sで30秒毎に起動。40sなら40秒毎に起動。基本デフォルトでOK)。

必要な環境

  • MacBook Air (13-inch, 2017)〜たぶんmacbookでも動くと思う
  • macOS High Sierra 10.13.3
  • bitbarがインストール済み https://getbitbar.com
  • macがスマホとbluetooth接続でテザリングできるように設定されている
  • データ通信の定額プランに加入している(問答無用で勝手に接続しに行くスクリプトなので、これとっても重要!従量制の人は決して使ってはいけない)
  • スマホはandroidスマホ Nexus 5X で確認(iphoneは持っていないので試せなかった)

スクリプトの使い方

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)

3. bitbarの設定

  • bitbarのメニューのPreferences -> Change Plugin Folder...で先程作成したフォルダを指定。
  • 画面最上段のステータスバーに青色で[BT tethering OFF]が表示されたら、WI-Fiをオフにする。30秒以内にbluetoothテザリングが開始されたら成功。

シェルスクリプト

  • 2018年05月07日 関数名を訂正しました (bt_swith_onoff → bt_switch_onoff) khskさんありがとう。
btz.30s.sh
#!/bin/bash

# 必須設定(必ず自分で設定してください)
#############################################################################
#   テザリングするときに使用するBluetoothデバイス名 (例 BT_DEVICE="Nexus 5X")
BT_DEVICE="xxxxxxxx"
#############################################################################

#############################################################################
# Btz.30s.sh - bluetoth tethering auto connect.
# 2018/04/30. 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"
#############################################################################

# 以下、スクリプト本体
#############################################################################
# 実行ファイルのディレクトリをカレントディレクトリに
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
                set str to name of (menu item 2 of menu 1 of menu item "${BT_DEVICE}" of menu 1)
                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/