新型コロナウイルス接触確認アプリ(COCOA)がインストールされているスマホを数える

## はじめに
2020年7月頃、Macの Swift　Playgrounds で、10秒間、周辺の新型コロナウイルス接触確認アプリ(COCOA)がインストールされているスマホを数えるコードを書きました。
iOSアプリ版も作成したのですが、iOS13以上ではCOCOAが検知できなくなっています。
今回、同様なことを javascript で書くチャレンジです。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">コロナ接触確認アプリの利用状況を調べるアプリを作ってみた <a href="https://t.co/BZYXcFm9Mo">pic.twitter.com/BZYXcFm9Mo</a></p>&mdash; Sam Akada@HomeKit (@sam_akada) <a href="https://twitter.com/sam_akada/status/1282109965482651648?ref_src=twsrc%5Etfw">July 12, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

## Swiftコード
```swift
import UIKit
import CoreBluetooth
import PlaygroundSupport

class ViewController: UIViewController, CBCentralManagerDelegate {
    
    var centralManager: CBCentralManager?
    let scanTime: Double = 10 // seconds
    let label = UILabel()
    var count = 0
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        label.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: UIScreen.main.bounds.size.height/4)
        label.center = view.center
        label.textAlignment = .left
        label.font = label.font.withSize(100)
        label.text = "-"
        self.view.addSubview(label)
        centralManager = CBCentralManager(delegate: self, queue: nil)
    }
    
    func scanStart() {
        if centralManager!.isScanning == false {
            let service = CBUUID(string: "fd6f")
            self.centralManager?.scanForPeripherals(withServices: [service], options: nil)
            DispatchQueue.main.asyncAfter(deadline: .now() + scanTime) {
                self.centralManager?.stopScan()
                self.label.text = "\(self.count)"
                self.count = 0
                self.scanStart()
            }
        }
    }
    
    func centralManagerDidUpdateState(_ central: CBCentralManager) {
        switch (central.state) {
        case .poweredOn:
            scanStart()
        case .unknown:
            break
        case .resetting:
            break
        case .unsupported:
            break
        case .unauthorized:
            break
        case .poweredOff:
            break
        @unknown default:
            break
        }
    }
    
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
        print(peripheral.identifier)
        count += 1
    }
}

PlaygroundPage.current.liveView = ViewController()
```
:::note warn
macOS Catalina, iPadOS 12のSwift Playgroundsでは、COCOAを検知できますが、macOS Monterey, iPadOS 13以上では、検知できなくなりました。
:::
## 事前調査
- node.js v8の古いバージョンでは、nobleのモジュールを使った、BLEの事例があった。
- openSSLの脆弱性を考慮すると、新しいバージョンを使いたい。
- できるだけ新しいバージョンのnode.jsでBLEを使う時、アバンダンウェアプロジェクト (Abandonware) のnobleを使うらしい。
- node.jsの公式サイトには、ARMv6のバイナリがない。

## 環境
- Raspberry Pi Zero (CPU ARMv6)
- Raspberry Pi OS Lite 32bit January 28th 2022 (bullseye)
- node.js v17.7.2

## nodejsのインストール
~/.profile に環境変数を追加します。
```bash
VERSION=v17.7.2
DISTRO=linux-armv6l
export PATH=/usr/local/lib/nodejs/node-$VERSION-$DISTRO/bin:$PATH
```
※VERSIONを書き換えることで、違うバージョンのnodejsをインストールできます。

環境変数を反映します。
```bash
. ~/.profile
```

nodejs公式サイトには、ARMv6のバイナリビルドがないため、非公式ビルドサイトからダウンロードします。
```bash
curl -OL https://unofficial-builds.nodejs.org/download/release/$VERSION/node-$VERSION-$DISTRO.tar.gz
```

ファイルを展開します。
```bash
sudo mkdir -p /usr/local/lib/nodejs
sudo tar -zxvf node-v16.14.2-linux-armv6l.tar.gz -C /usr/local/lib/nodejs
```

シンボリンクリンクを作成します。
```bash
sudo ln -s /usr/local/lib/nodejs/node-$VERSION-$DISTRO/bin/node /usr/bin/node
sudo ln -s /usr/local/lib/nodejs/node-$VERSION-$DISTRO/bin/npm /usr/bin/npm
sudo ln -s /usr/local/lib/nodejs/node-$VERSION-$DISTRO/bin/npx /usr/bin/npx
```

## 確認
```bash
$ node -v
v17.7.2
$ npm -v
8.5.2
$ npx -v
8.5.2
$ node -p process.versions
{
  node: '17.7.2',
  v8: '9.6.180.15-node.15',
  uv: '1.43.0',
  zlib: '1.2.11',
  brotli: '1.0.9',
  ares: '1.18.1',
  modules: '102',
  nghttp2: '1.47.0',
  napi: '8',
  llhttp: '6.0.4',
  openssl: '3.0.2+quic',
  cldr: '40.0',
  icu: '70.1',
  tz: '2021a3',
  unicode: '14.0',
  ngtcp2: '0.1.0-DEV',
  nghttp3: '0.1.0-DEV'
}
```

## nobleのインストール
```bash
npm i @abandonware/noble
```

## サンプルコード
10秒間、周辺の新型コロナウイルス接触確認アプリ(COCOA)がインストールされている端末数を数えます。
```javascript
// COCOA scan.js
'use strict';

const noble = require('@abandonware/noble');
const cocoaDevices = [];

//discovered BLE device
const discovered = (peripheral) => {
    const device = {
        uuid: peripheral.uuid,
        rssi: peripheral.rssi
    };
    cocoaDevices.push(device);
    console.log(`${cocoaDevices.length}:(${device.uuid}) RSSI${device.rssi}`);
}

//BLE scan start
const scanStart = () => {
    //COCOA Service UUID
    noble.startScanning(['fd6f'],false);
    noble.on('discover', discovered);
}

if(noble.state === 'poweredOn'){
    scanStart();
}else{
    noble.on('stateChange', scanStart);
}
setTimeout(function(){
  noble.stopScanning();
  process.exit();
},10000);
```

## 実行結果
COCOAアプリ入れた、iPhoneを1台を置いた状態で、試したら、1回目、スマホが1台、2回目、スマホが2台でした。近くに、もう1台、スマホがあるみたい。
![スクリーンショット 2022-03-26 17.55.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/452335/5453b8f0-9ab3-286f-2ad2-9dca0d6abb4c.png)


## 参考
[Node.jsから周辺のBLEデバイスを探すサンプル （2021年4月版）](https://zenn.dev/n0bisuke/articles/01-nodejs-ble-noble-2021)