OpenWrt の 5GHz チャネルを監視し、動作していない場合は強制的に回復させる

## 今回のコードで何ができるか

OpenWrt の 5GHz チャネルを監視し、動作していない場合は強制的に回復させる。

<details>
<summary>もっと詳しく</summary>

OpenWrt にも [DFS (Dynamic Frequency Selection)](https://licensecounter.jp/engineer-voice/blog/articles/20200120_dfs.html) 機能があり、気象レーダーや航空レーダーの電波を受信すると、一定時間そのチャネルが使えなくなる。

ただ DFS 以外でも、5GHz の電波が止まり、そのまま Wi-Fi が回復しないことがある。

今回のコードでは Wi-Fi での出力を監視し、出力されていない場合には Wi-Fi の再起動を行い、それでも出力が回復しない場合は OpenWrt デバイスを再起動する。

👉 本来は、[レーダー波を検知した後 30 分間そのチャンネルが利用停止になります](https://licensecounter.jp/engineer-voice/blog/articles/20200120_dfs.html)が、今回のコードは `false positive` で DFS が 動作してしまう我が家のような環境を想定しています。

</details>

## コードで何をしているか

1. 5GHz のチャネルが落ちている場合は Wi-Fi を再起動
1. それでも回復しない場合はデバイスを再起動

<details>
<summary>もっと詳しく</summary>

- ステップ 1. 動作中のチャンネル数と動作しているはずのチャンネル数を比較する
  - チャンネル数が一致しない場合はステップ 2. へ
  - チャンネル数が一致する(=問題がない)場合は `/var/log/wifi_rebooted.log` を削除
- ステップ 2.

  - `/var/log/wifi_rebooted.log` が存在する場合は、以下の 1. と 2. を実行

    1. 再起動の内容を `/etc/config/reboot.log` に書き込む
    1. デバイスを `reboot`  
       (👉 デバイスを `reboot` すると、`/var/log` 内のファイルは全て削除されるため `/var/log/wifi_rebooted.log` も削除される。)

  - そうでない場合は、ステップ 3. へ

- ステップ 3.
  1. 再起動の内容を `/etc/config/reboot.log` に書き込む
  1. 再起動の日時を `/var/log/wifi_rebooted.log` に書き込む
  1. Wi-Fi を再起動 (`wifi down` して `wifi up`)

</details>

## 使用するコード

```shell:/etc/config/wifi_channel_monitor.sh
#!/bin/sh

# Set country code for wireless radios
configure_country_code() {
  local country_code="JP"
  uci set wireless.radio0.country="$country_code"
  uci set wireless.radio1.country="$country_code"
  uci commit wireless
}

# Initialize constants for log file locations
initialize_constants() {
  readonly REBOOT_LOG="/etc/config/reboot.log"
  readonly WIFI_REBOOTED_LOG="/var/log/wifi_rebooted.log"
}

# Get the current date and time in a specific format
get_current_time() {
  echo $(date "+%F %T")
}

# Count the number of access point interfaces
get_expected_channel_count() {
  echo $(iw dev | grep "type AP" | wc -l)
}

# Validate the channel count and take necessary actions
validate_channel_count() {
  local valid_channel_count=$(iw dev | grep channel | wc -l)

  if [[ "$valid_channel_count" -eq "$expected_channel_count" ]]; then
    echo "Wireless LAN channel count is as expected (${expected_channel_count})."
    [[ -f "$WIFI_REBOOTED_LOG" ]] && rm "$WIFI_REBOOTED_LOG"
    exit 0
  fi

  handle_unexpected_channel_count "$valid_channel_count"
}

# Handle unexpected channel count scenario
handle_unexpected_channel_count() {
  local valid_channel_count=$1
  echo "Wireless LAN channel count is not as expected (${valid_channel_count})."

  if [[ -f "$WIFI_REBOOTED_LOG" ]]; then
    log_and_reboot "system_reboot"
    exit 0
  fi

  log_wifi_reboot
  reboot_wifi
}

# Log the reboot event and perform system reboot
log_and_reboot() {
  local action=$1
  echo -e "$current_time\t$action" >> "$REBOOT_LOG"
  echo "Now rebooting..."
  reboot
}

# Log the wifi reboot event
log_wifi_reboot() {
  echo -e "$current_time\twifi_reboot\t$(get_unexpected_channels)" >> "$REBOOT_LOG"
  echo "$current_time" > "$WIFI_REBOOTED_LOG"
}

# Get the list of unexpected channels
get_unexpected_channels() {
  iwinfo | awk '/Channel: [0-9]+/ { if ($4 >= 36) print $4 "ch"}'
}

# Perform Wi-Fi reboot
reboot_wifi() {
  echo "Rebooting Wireless LAN."
  wifi down
  sleep 5
  wifi up
}

# Main script execution
main() {
  configure_country_code
  initialize_constants
  current_time=$(get_current_time)
  expected_channel_count=$(get_expected_channel_count)
  validate_channel_count
}

main "$@"
```

## コードの使用方法

1. `SSH` で OpenWrt デバイスにログインし、
   - 上記コードを `/etc/config/wifi_channel_monitor.sh` に保存
   - `chmod +x /etc/config/wifi_channel_monitor.sh` で実行権限を付与
1. ブラウザで OpenWrt にログインし、`System` > `Scheduled Tasks` に以下を追加  
   (日本語画面の場合は、「システム > スケジュールされたタスク」で追加)  
   `*/1 * * * * /etc/config/wifi_channel_monitor.sh`

   👉 `/etc/crontabs/root` に直接書き込んでも良いです。

👉 いつ再起動されたかは、`cat /etc/config/reboot.log` で確認できます。

👉 コードを `/etc/config/` 内に保存する理由は、`System` > `Backup` で設定のバックアップ(archive)を作成する際、 `/etc/config/` は確実にバックアップファイル内に含まれるためです。

## 参考ページ

https://gihyo.jp/admin/serial/01/ubuntu-recipe/0371

https://google.github.io/styleguide/shellguide.html

## OpenWrt の豆知識 (`iwinfo` と `iw dev`)

`iwinfo` と `iw dev` では得られる情報が少し異なる。

- `iwinfo`

  取得できる値から Wi-Fi の稼働状況を確実に確認する方法がない

- `iw dev`

  取得できる値から Wi-Fi の稼働状況を確実に確認できる

そのため、 Wi-Fi の出力が停止しているかどうかは `iw dev` で確認する。