Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Swiftで我が家を より便利に、安全に!

u5-03
September 25, 2022

Swiftで我が家を より便利に、安全に!

この記事は、iOSDC2022の「Swiftで我が家をより便利に、安全に」の発表(https://fortee.jp/iosdc-japan-2022/proposal/841f38a0-f3e4-4b39-b14c-de6f80521eee)の内容をまとめた記事になります

u5-03

September 25, 2022
Tweet

More Decks by u5-03

Other Decks in Technology

Transcript

  1. 2 0. 導入 すぎー/杉山 優悟 "  !  

     @u5_0! iOSDC初登壇です!登壇用に、Macを新調m 身長は188cm(よく大きいと驚かれるd 前職(2018~)から、iOSエンジニア。元々中高の社会科教員志I 趣味: 美味しいものを食べる、料理、アニメ/漫画、ガジェット収† 最近買った物: Mac(M1 Pro)、ハンモック(ハンモックはよいぞ!) README
  2. 3 0. 導入 ラズパイやHerokuでのSwiftの実装についての発P 53 既存の 機能を強化す! 43 我が家の状態を監視す! @3

    経由でiPhone、 を連携させる スマートホーム Heroku ラズパイ このセッションの内容
  3. 4 0. 導入 ラズパイやHerokuでのSwiftの実装についての発F R 既存の 機能を強化す! R 我が家の状態を監視す! R

    Heroku経由でiPhone、ラズパイを連携させる スマートホーム このセッションの内容 Project SwiftHome と命名
  4. 5 0. 導入 sw スマートホームとは„ uw ネットワーク経由で家電を操作できるようにすU †w GoogleHomeでリビングの照明をつけるなq )w

    ラズパイとは„ uw が正式名! ‡w 小さなシングルボードコンピューター bw OSを書き込んだSDカードを入れて使う Raspberry Pi 用語解説①
  5. 6 0. 導入 sw スマートホームとは„ uw ネットワーク経由で家電を操作できるようにすU †w GoogleHomeでリビングの照明をつけるなq )w

    ラズパイとは„ uw が正式名! ‡w 小さなシングルボードコンピューター bw OSを書き込んだSDカードを入れて使う Raspberry Pi 用語解説① 要は。。
 はよいぞ! という発表です!笑 スマートホーム
  6. 7 0. 導入 ˆQ Herokuとはt AQ アプリケーションを実行するために必要なプラットフォームを提 供するサービス(PaaS8 'Q 便利で使いやすいレンタルサーバーのようなイメー

    wQ 今回は (ServerSideSwiftのフレームワークの1つ)で 実装したサーバーをデプロイするのに利用 Vapor 用語解説②
  7. 8 0. 導入 ◦話すこ „y ラズパイやHerokuでのSwiftやVaporの実C ‚y 実現する機能の中で、コアな実装の手順(詳細はブログに記載% $y 実装でつまづいた点や個人的に試行錯誤した方法

    ×話さないこ „y ラズパイのセットアップ方t ‚y 主にハード周りの技術要素の詳細や原理 このセッションで話すこと、話さないこと
  8. 9 0. 導入 …„ 発表者は、 でe a„ 発表情報は不正確、もしくは情報が不足している場合がありまe F„ 今回実装しているコードやリポジトリは、個人開発なので、アーキテ

    クチャやセキュリティが適当考慮できてない箇所が多くありまe „ のため、コードや情報を全て 発表/記載できないので、 を確認してくださI a„ 気になる箇所は個別に確認してください( ) ハード周りの技術に精通しているわけではない 発表の時間やスライドのスペースの制約 詳細はブログ記事(後述) かなり飛ばします! 注意事項 ️
  9. Use Load cell weighing sensor Use from Google Home Deploy

    on Play warning sound Communicate through GPIO Communicate using Vapor SwiftHome Read Suica ID using card reader Use iPhone Sensors Collaborate with Home appliance Run Bash from Swift Run Python from Swift Run Swift on Raspberry Pi
  10. 11 0. 導入 ③SwiftHomePi ①SwiftHomeApp Postgress Vapor ②SwiftHomeServer Vapor Rest

    API Web Socket セキュリティを考慮して、 直接通信はしない SwiftHome構成
  11. 12 0. 導入 ①SwiftHomeApR IG 位置情報、 をHerokuに送9 HG をスキャンし、Herokuに送9 5G

    ユーザーが家にいるかの判断材料にするため ②SwiftHomeServe IG を実装して、データの仲) HG にデータを保存 標高データ SuicaのID API/WebSocket Postgres SwiftHome実装①
  12. 13 0. 導入 ③SwiftHomePW DB を使用したLEDの操作、センサーのデータ読み取S CB の実 VB のAPI実行(家電の操作を行う

    ÉB Swiftから のコードの実行 ラズパイのGPIO Swift/Vapor SwitchBot Python/Bash SwiftHome実装②
  13. 14 0. 導入 ・ GPIOとは? 1. ‘General Purpose Input/Output’の 

    汎用I/Oポートの意味 2. →のように電源、GND(マイナス)、I/ Oなどポートごとに役割が分かれている 3. このピンにLEDやセンサーなどを繋げ て、回路を作ることでそれらを操作できる 用語解説③ https://deviceplus.jp/raspberrypi/raspberrypi- gpio/
  14. 16 0. 導入 今回の発表は1~5の5部構成です 0. 導入 - 発表やSwiftHomeの概要紹q ˜ˆ ラズパイでSwiftを動かす

    - GPIOを使って、LEDを点滅させ… 1ˆ Vaporを動かす - iPhoneとラズパイ間をVaporで通a "ˆ ラズパイでセンサーの値を読み取る - 重量センサーの値を取得す… fˆ カードリーダーと連携する - SuicaのIDを読み取… dˆ 人感センサーを使う - こちらから攻める 目次
  15. 17 0. 導入 1.ラズパイでSwiftを動かす - GPIOを使って、LEDを点滅させる 2.Vaporを動かす - iPhoneとラズパイ間をVaporで通信 3.ラズパイでセンサーの値を読み取る

    - 重量センサーの値を取得する 4.カードリーダーと連携する - SuicaのIDを読み取る 5.人感センサーを使う - こちらから攻める 目次 ①ラズパイでのSwift、Vaporの基本実装の紹介 ②家主が在宅中かどうかのチェック機能を実装の紹介 ③不審者が侵入した時の実装の紹介
  16. 19  ラズパイでSwiftを動かす  LED点滅(通称Lチカ)は、電子工作の’Hello world’みたいなもV „ SwiftでGPIOを操作するには、 を使C a

    Ref: @ 今回は↓のような回路を作成すI a ラズパイのGPIO16ピン(プラス) -> LEDのプラス(長い方f & LEDのマイナス(短い方) -> 抵 S 抵抗 -> ラズパイのGPIOのGND(マイナス)ピン SwiftyGPIO https://github.com/uraimo/SwiftyGPIt SwiftyGPIOでLEDを点滅させる
  17. 22  ラズパイでSwiftを動かす ラズパイの開発環境 $ cat /proc/device-tree/model > Raspberry Pi

    4 Model B Rev 1.2 $ cat /etc/os-release > PRETTY_NAME="Ubuntu 22.04 LTS" > NAME="Ubuntu" > VERSION_ID="22.04" > VERSION="22.04 (Jammy Jellyfish)" > VERSION_CODENAME=jammy $ swift --version > Swift version 5.6.1 (swift-5.6.1-RELEASE) > Target: aarch64-unknown-linux-gnu # ラズパイのハード情報 # ラズパイで使用しているOSの情報
  18. 23  ラズパイでSwiftを動かす  ラズパイに のOSをインストールしているのはなぜa Y 今回は を使う想定だが、標準的に使われている だと、

    の設定やビルドが通らなƒ w 参考サイトでも、同じ理由で を使用する例が多かっX 9 執筆時点では でSwift を使用できるが、 だと、Swift までしか利用できなƒ " 開発のしやすさも考慮して、 で進めることにしました Ubuntu Vapor Raspberry Pi OS Vapor Ubuntu Ubuntu 5.6 Raspberry Pi OS 5.1.5 Ubuntu ラズパイの開発環境
  19. 24  ラズパイでSwiftを動かす # Step 1 - System update and

    install curl # Step 2 - Run the quick install script # Step 3 - Choose which version of Swift to install # Choose swift version # Step 4 - Install Swift # Ref: $ sudo apt update sudo apt upgrade $ sudo apt install curl $ curl -s https://archive.swiftlang.xyz/install.sh sudo bash $ sudo apt install swiftlang $ swift --version && | https://swift-arm.com/installSwift/ ラズパイにSwiftをインストールする
  20. 25  ラズパイでSwiftを動かす 今回は というパッケージを作成する SwiftHomeLED $ mkdir SwiftHomeLED $

    cd SwiftHomeLED $ swift package init --name SwiftHomeLED --type executable サンプルプロジェクトを作成する
  21. 26  ラズパイでSwiftを動かす Package.swiftに のdependenciesを設定する SwiftyGPIO Package.swiftにライブラリを追加する // (省略) //

    (省略) name: , dependencies: [ . (url: , from: ), ], targets: [ dependencies: [ ]), "SwiftHomeLED" "https://github.com/uraimo/SwiftyGPIO.git" "1.0.0" "SwiftyGPIO" package Package.swift
  22. 27  ラズパイでSwiftを動かす 0.2秒おきに 処理を実装する LEDを点滅させる 1 2 3 4

    5 6 7 8 9 10 import import let = let = ! = while = == ? : gpios SwiftyGPIO. ( : .RaspberryPi4) ledGpio gpios[.P16] ledGpio.direction .OUT ( ) { Thread. ( : ) ledGpio. ledGpio. } Foundation SwiftyGPIO GPIOs for true sleep forTimeInterval 0.2 value value 0 1 0 // ①ラズパイ4用のgpioのインスタンス // ②GPIO16ピンを操作するためのインスタンス // ③今回は電流を流すので、.INではなく、.OUTにする // ④GPIO16の値を0.2秒ごとに更新 // ⑤1ならLEDが点灯、0なら消える main.swift main.swiftを編集する
  23. 32 2. Vaporを動かす g— とはf d— ServerSideSwiftのフレームワークの1r — フロントとサーバー側をSwiftで書くことで、Codableのレス ポンスやモデルなどのコードを共通化できるメリットがあ

    v— DBやWebSocketも簡単に利用でき '— 執筆時はMacとLinuxで利用可 s— ( ) Vapor ドキュメントが充実している https://vapor.codes 用語解説⑤
  24. 33 2. Vaporを動かす ③SwiftHomePi ①SwiftHomeApp Postgress Vapor ②SwiftHomeServer Vapor Rest

    API Web Socket e 通信内d ˜f iPhoneの位置・高度情y xf SuicaのI €f 各種フラグ SwiftHome構成(再掲)
  25. 34 2. Vaporを動かす ”… というVaporのプロジェクトをMacで作` ‚… API, WebSocketなどを実装して、動作確I q… に

    をデプロイすB ˆ… , から に接 続して、動作確認 ※ SwiftHomeでは、パスワード、Tokenやエンドポイントなどは、 privateリポジトリの を参照しています SwiftHomeServer Heroku SwiftHomeServer SwiftHomeApp SwiftHomePi SwiftHomeServer SwiftHomeCredentials Vapor実装の流れ
  26. 35 2. Vaporを動かす ` MacBook Pro(14インチ、2021、Apple M1 ProD ` macO6

    ` Monterey 12.2.P ` Xcode 13.4.P ` Vapor4 (執筆時の最新版" ` toolbox: 18.5.P ` Vaporで利用するCLIツール 開発環境
  27. 36 2. Vaporを動かす MacでVaporをインストールし、プロジェクトを作成する $ brew install vapor $ vapor

    --help $ vapor new SwiftHomeServer > Would you like to use Fluent? (--fluent/--no-fluent)  > Which database would you like to use? (--fluent.db)  > Would you like to use Leaf? (--leaf/--no-leaf) # ① Select using Fluent or not. (ORM framework for Swift.) #→ y # ②Select database(If using in Heroku, ‘Postgres’ is required.) #→ y # ③Select using Leaf or not.(a powerful templating language with Swift-inspired syntax.) #→ y or n(this time, n) Vaporのセットアップ
  28. 37 2. Vaporを動かす 生成されたVaporのプロジェクトをビルドし、アクセスしてみる $ cd SwiftHomeServer $ swift run

    # Access to ‘http://localhost:8080/hello’ ビルド後に、上記にブラウザなどでアクセスすると、 と表示される! Ref: Hello, world! https://docs.vapor.codes/getting-started/hello-world/ Vaporの動作確認
  29. 38 2. Vaporを動かす デフォルトプロジェクトの を確認する ルーティングの処理 1 2 3 4

    5 6 7 8 9 10 // (省略) // ①エンドポイントの指定がない場合に呼ばれる // ②async/awaitも使用することができる // ③ここでエンドポイントとメソッドタイプを指定 // (省略) func throws in -> in ( app: Application) { app.get { req async } app. ( ) { req async } routes _ "It works!" "hello" "Hello, world!" get String routes.swift Vaporのコードを見てみる
  30. 39 2. Vaporを動かす ③SwiftHomePi ①SwiftHomeApp Postgress Vapor ②SwiftHomeServer Vapor Rest

    API Web Socket  通信内† „‘ ˆ‘ SuicaのI€ v‘ 各種フラグ iPhoneの位置・高度情– SwiftHome構成(再掲)
  31. 40 2. Vaporを動かす iPhoneから位置情報と高度情報を を実装する PostするAPI 1 2 3 4

    5 6 7 8 9 10 // protectedはBasic認証を適用する実装(詳細はブログに記載) // ①postを設定することで、PostのAPIが実装できる // ②DBモデルへ // (省略: Postgresにデータを保存する処理) // ③asJsonStringはEncodableをjsonに変換 // ④ラズパイへデータ送信(詳細は後述) // ⑤Errorがthrowされなければ、okを返す protected. (. (EndPointKind.deviceInfo.endPoint)) { req async HTTPResponseStatus response req.content. (DeviceInfoModel. ).asDBModel jsonString response. () deviceInfoWebSocket . (jsonString) HTTPStatus.ok } post constant decode asJsonString send throws -> in let = try self let = try try await ? return routes.swift VaporのPostのAPIを実装する
  32. 41 2. Vaporを動かす ③SwiftHomePi ①SwiftHomeApp Postgress Vapor ②SwiftHomeServer Vapor Rest

    API Web Socket f 通信内e ™g iPhoneの位置・高度情€ yg SuicaのI‚ g 各種フラグ SwiftHome構成(再掲)
  33. 42 2. Vaporを動かす SwiftHomeServerでのWebSocketの実装 1 2 3 4 5 6

    7 8 9 10 var ? in = in try await ? deviceInfoWebSocket: WebSocket protected. (. (EndPointKind.deviceInfo.webSocketEndPoint)) { , ws deviceInfoWebSocket ws ws.onText { ws, text (text) } } deviceInfoWebSocket . (jsonString) // (省略) // ①WebSocketインスタンスの保持 // (省略) // ②PostされたデータをWebSocketで送信 webSocket constant _ print send routes.swift SwiftHomeでのWebSocketの実装①
  34. 43 2. Vaporを動かす SwiftHomePiでのWebSocketの実装(Serverとの接続) 1 2 3 4 5 6

    7 8 private var ? = weak in guard let = else return = deviceInfoWebSocket: WebSocket WebSocket. ( : , : headers, : eventLoopGroup) { [ ] ws { } .deviceInfoWebSocket ws } // (省略) // ①headerはBasic認証用のheader情報を設定(詳細はブログ) // ②接続後に、WebSocketインスタンスを保持する // (省略: データ保存処理) _ connect to headers on self self self self "\(WebSoketURL)" WebSocketManager.swift SwiftHomeでのWebSocketの実装②
  35. 48 2. Vaporを動かす ‡’ にある というclassを使うと、高度情 報を取得でき… e’ 今回は絶対高度と相対高度を取得する(Postするのは絶対高度のみd h’

    : 海抜から何メートル8 2’ : データ取得開始時から何メートル変化した8 "’ 利用するには、Info.plistに↓の設定追加が必7 h’ ‘ ’ CoreMotion CMAltimeter 絶対高度 相対高度 Privacy - Motion Usage Description CoreMotionを使って、高度を取得する①
  36. 49 2. Vaporを動かす SwiftHomeAppの を取得するコードを抜粋して、記載します 絶対高度 1 2 3 4

    5 6 7 8 9 10 import private let = if in if != return ! altimeterMeter () CMAltimeter. () { altimeterMeter. ( : OperationQueue.main) { data, error error { } (data .altitude) } } CoreMotion CMAltimeter isAbsoluteAltitudeAvailable startAbsoluteAltitudeUpdates to nil print // ①インスタンスの用意 // ②CMAltimeterが使用できるかの確認 // ③一定タイミングで呼ばれ、データを取得できる // 25.007457757686876868(e.g, 単位はm) AltimeterManager.swift CoreMotionを使って、高度を取得する②
  37. 51 2. Vaporを動かす `V HerokuにPostするパラメータを用意す9 HV ModelはSwiftHomeServerでも共通で使用した SwiftHomeCoreのDeviceInfoModelを使用する 1 2

    3 4 5 6 // ①共通ModuleのSwiftHomeCoreに定義したModelを使用 // ②Dictionaryに変換するextension let = ! let = try! model ( : UIDevice.current.identifierForVendor .uuidString, : userCoordinate.latitude, : userCoordinate.longitude, : absoluteAltimeterValue) parameters model. ( : encoder) DeviceInfoModel deviceId deviceLatitude deviceLongitude absoluteAltimeter asDictionary using DeviceInfoViewModel アプリの情報をHerokuに送信する①
  38. 52 2. Vaporを動かす Alamofireを使用して、データをHeroku経由でラズパイにPostする 1 2 3 4 5 6

    7 8 9 AF. (urlString, : .post, : parameters, : headers) . ( : Empty. , : [ ]) { response response.result { .success ( ) } } request method parameters headers responseDecodable of emptyResponseCodes 200 print // ①DeviceInfoModelのパラメータを設定 // ②WebSocketのheaderと同じ設定 // (省略: エラー処理) self in switch case : "Success" DeviceInfoViewModel アプリの情報をHerokuに送信する②
  39. 58 3. ラズパイでセンサーの値を読み取る A: status = 家にいる! B: status =

    外出中! キーケース 重量センサーで実現したいこと
  40. 59 3. ラズパイでセンサーの値を読み取る i 家にいるかどうかの判断基準の1つにすH Ti 我が家は ⇩を使っていて、自動施錠で鍵 が閉まることをトリガーに家の照明などをOFFにする設定をしていH fi

    しかしゴミ捨てや配達受け取り時の施錠でも起動してしまう..i 0i した— )i この3部ではSwiftを使った重量センサーとの連携を紹介 SwitchBot スマートロック 外出時のキーケースを持ち出す時だけ起動 重量センサーの実装によってできること
  41. 62 3. ラズパイでセンサーの値を読み取る 外出中 or 在宅 更新 If 外出中 通知

    施錠 通知 通知 Webhookで連携 ⑤ 家電操作 通知 ① ③ ④ ②SwiftHomeServer 今回の実装イメージ(After)
  42. 63 3. ラズパイでセンサーの値を読み取る ˜‚ とは?( † „‚ トリガーとイベントをレシピとして登録して、 できるサービ ˆ‚

    GoogleHomeで話す→Merossのライトを消b ˆˆ‚ 指定時刻になる→SwitchBotでカーテンをあけu ˆˆˆ‚ 家に近づく→NatureRemoでエアコンをつける IFTTT 様々な サービス同士を連携 https://ifttt.com 用語解説⑦
  43. 64 3. ラズパイでセンサーの値を読み取る – 今回の重量センサーは、24ビットの信号を出力する という ADコンバーターを使用しています… V– Reference guilde:

    ‘– データは、 という形式で取得する必要があ‚ V– 通常は、GPIOピンからは0 or 1の値しか取得できなd 1– GPIO5(データ取得用), GPIO6(クロック信号用)を使用すること で、重量のアナログ情報を の単位で取得できる HX711 SPI 24bit https://cdn.sparkfun.com/datasheets/Sensors/ForceFlex/ hx711_english.pdŽ 重量センサーのデータを読み取る①
  44. 65 3. ラズパイでセンサーの値を読み取る GPIOのインスタンスを用意する 1 2 3 4 5 6

    7 let = let = ! let = ! = = gpios SwiftyGPIO. ( : .RaspberryPi4) dataGpio gpios[.P5] sckGpio gpios[.P6] dataGpio.direction .IN sckGpio.direction .OUT GPIOs for // Load sensor // serial clock GpioManager.swift 重量センサーのデータを読み取る②
  45. 66 3. ラズパイでセンサーの値を読み取る センサーから24bitのデータを取得する 1 2 3 4 5 6

    7 8 9 10 sckGpio. dataIn: { sckGpio. dataIn (dataIn ) (dataGpio. ) sckGpio. } maxValue: minValue: value 0 UInt32 0 _ 0 24 value 1 1 UInt32 value value 0 UInt32 16777215 UInt32 8388608 = var = for in ..< = = << | = let = let = // ①24bitのデータを1bitずつ取得 // ②左ビットシフト→新しいbitをORで反映 // 0x7fffff(16777215) is Max value, 0x800000(8388608) is Min value // ③HX711のリファレンス記載の最大データ // ④HX711のリファレンス記載の最小データ GpioManager.swift 重量センサーのデータを読み取る③
  46. 67 3. ラズパイでセンサーの値を読み取る 校正した重量の値を返す 1 2 3 4 5 6

    7 8 9 10 if == || == return var = if & == = ^ + else = return - / (dataIn maxValue dataIn minValue) { } signedData: ( (dataIn minValue) ) { signedData (((dataIn maxValue) )) } { signedData (dataIn) } ( (signedData) defaultLoadValue) refernceUnit 0 Int32 0 Int32 1 Int32 1 Int32 Double // ②補数の処理を実行?(この辺り理解が正確でないです。。) // ②元々の初期値を引き、キャリブレーション(校正)する値(refernceUnit)で処理した値を返す GpioManager.swift 重量センサーのデータを読み取る④
  47. 68 3. ラズパイでセンサーの値を読み取る d” キャリブレーション(校正)とは˜ U” 正確な値を測るため、 するこb y” →のように重りを使い、複数の計測データの平均値を元に計算す

    る(本当は分銅などがいいが、無いので硬貨で代用ƒ (” 今回はrefernceUnitを校正値として利q "” 計算したら、 センサー個体ごとに誤差を調整 refernceUnit = 465.375 用語解説⑧
  48. 69 3. ラズパイでセンサーの値を読み取る 重量センサーから物体が取り除かれたことを監視 1 2 3 4 5 6

    7 8 9 10 func -> while if > return break ( : (() )) { ( ) { ( ) () { } () observeLoadPlaced {} } } observeLoadRemoved completion Void true sleep 1 readWeight 30 completion // ①1秒ごとに確認 // ②30gより重い場合は、そのまま // ③(省略: DBのステータス変更処理 / LEDの消灯処理) // ④物体が取り除かれたら、次は物体が置かれることを監視する GpioManager.swift 重量センサーのデータを読み取る⑤
  49. 73 今回の発表は1~5の5部構成です 0. 導入 - 発表やSwiftHomeの概要紹f “ƒ ラズパイでSwiftを動かす - GPIOを使って、LEDを点滅させ€

    &ƒ Vaporを動かす - iPhoneとラズパイ間をVaporで通V ƒ ラズパイでセンサーの値を読み取る - 重量センサーの値を取得す€ aƒ カードリーダーと連携する - SuicaのIDを読み取€ •ƒ 人感センサーを使う - こちらから攻める 目次
  50. 76 4. カードリーダーと連携する ’‘ ラズパイとPasori(RC-S380)を使って、SuicaのIDを読み取Y p‘ Ref: P‘ 事前にSwiftHomeAppで読み取って を確

    認することに利用すY p‘ SuicaにはIDが割り振られていY !‘ 今回は余ってたSuicaを使用すY T‘ この認証結果を の判定に利用 https://www.sony.co.jp/Products/felica/consumerp 登録したIDと一致するか 在宅中か Pasoriで、SuicaのIDを読み取る
  51. 78 4. カードリーダーと連携する ③SwiftHomePi SuicaのIDを通知 IDが一致するか 確認! ①SwiftHomeApp Postgress Vapor

    ②SwiftHomeServer Vapor Rest API Web Socket SuicaのIDをPost 認証用SuicaのIDを登録 Pasori連携の実装イメージ
  52. 79 4. カードリーダーと連携する EV 今回はPasoriでのデータを読み取るために、 という のモジュールを使用します1 HV Ref: DV

    を使って、SuicaのIDを読み込むPythonのコードを から呼び出します nfcpy Python nfcpy Swift https://github.com/nfcpy/nfcp’ nfcpy経由 Pasori連携の実装詳細
  53. 80 4. カードリーダーと連携する ラズパイに をインストールし、 との接続を確認する nfcpy Pasori # nfcpyのインストール

    # USBデバイスの一覧の中で、Pasoriが認識されているかを確認(IDはマスクしてます) # No such deviceと表示された場合 # Ref: https://www.out48.com/archives/5396/ $ sudo pip install -U nfcpy $ lsusb grep Sony Bus 001 Device 008: ID Sony Corp. RC-S380/S $ python -m nfc $ sudo reboot | > ******** Pasori連携の実装①
  54. 81 4. カードリーダーと連携する PythonでPasoriのデータを読みとる処理を実装する 1 2 3 4 5 6

    7 8 9 10 import import : = = = - : : = return def () ( ) clf nfc. ( ) tag clf. (rdwr {'on connect' lambda tag False}) tag_id binascii. (tag._nfcid). () clf. () tag_id nfc binascii // Ref: https://qiita.com/h_tyokinuhata/items/2733d3c5bc126d5d4445 read_id print ContactlessFrontend connect hexlify decode close "Touch!" "usb" read_nfc.py Pasori連携の実装②
  55. 82 4. カードリーダーと連携する PythonKitとPathKitをPackage.swiftのdependenciesに追加 // (省略) // (省略) dependencies [

    . ( : , : ), . ( : , : ), ], : package url branch package url from "https://github.com/pvieito/PythonKit" "master" "https://github.com/kylef/PathKit" "1.0.0" Package.swift Pasori連携の実装③
  56. 83 4. カードリーダーと連携する PythonKit Python Swift を使って、 の処理を から呼び出す 1

    2 3 4 5 6 7 8 9 import import func -> let = let = let = return () { sys Python. ( ) path Path.current sys.path. (path) readNfc Python. ( ) readNfc. (). } PythonKit PathKit readNfcId // ①Pythonコードの呼び出しを記述しやすくするライブラリ // ②Pythonのファイルのパスを指定 // ③Pythonファイルのread_idを呼ぶ // ↑④Dynamic Member Lookup(コンパイル時にないプロパティへアクセス) Ref: https:// github.com/apple/swift-evolution/blob/master/proposals/0195-dynamic-member-lookup.md String import append import read_id description "sys" "\( )/Sources/SwiftHomePi" "read_nfc" PythonCall.swift Pasori連携の実装④
  57. 84 4. カードリーダーと連携する 登録したIDと読み取ったIDが一致するかを確認する 1 2 3 4 5 6

    7 8 9 10 let = guard let = try await else return if == else id (). () savedNfcId DataStore.shared. () { ( ) } id savedNfcId.nfcId { ( ) } { ( ) } PythonCall readNfcId fetchNfcId print print print // ①登録済みID取得 "Cannot find NfcId in sqlite DB" "This card is registered!" "This card is not registered" WebSocketManager.swift Pasori連携の実装⑤
  58. 87 5. 人感センサーを使う - こちらから攻める - Run Bash from Swift

    Play warning sound Collaborate with Home appliance Communicate through GPIO
  59. 88 5. 人感センサーを使う ~u ラズパイと連携した が作動した時、↓を実行すw hu スピーカーで警告音を流b u SwitchBotでデバイスを動かb

    tu Push通知を送る(発表では省略9 7u カメラで侵入者を撮影し、Twitterに画像を投稿(今後実装予定9 Tu (①緯度経度や高度情報が家と一致しない、②重量セン サーに物が無い、③Suicaで認証されていない)場合のみ実行する 人感センサー 在宅中でない Ref: https://amzn.to/3QSQSei 人感センサーをトリガーに処理を実行する
  60. 91 5. 人感センサーを使う GPIOインスタンスを用意する 1 2 3 4 5 6

    let = ! let = ! = = = humanSensorGpio gpios[.P25] humanSensorLedGpio gpios[.P16] humanSensorGpio.direction .IN humanSensorLedGpio.direction .OUT humanSensorLedGpio. value 0 GpioManager.swift 人感センサーの連携の実装②
  61. 92 5. 人感センサーを使う 人感センサーの値の変化を監視する 1 2 3 4 5 6

    7 8 9 10 humanSensorGpio.onChange { gpio gpio. { .humanSensorLedGpio. } { .humanSensorLedGpio. } } in if == = else = // ①値が変化した時に呼ばれる // Human detected // ②LEDを点灯する // (省略: 変更を通知) //③ LEDを消灯する // (省略: 変更を通知) value 1 self value 1 self value 0 GpioManager.swift 人感センサーの連携の実装③
  62. 93 5. 人感センサーを使う 人感センサーの値の変化の通知によって、処理を実行する 1 2 3 4 5 6

    7 8 9 // ①監視用のメソッド // (省略: 人を検知した時の処理を実行) GpioManager.shared.observeHumanMoved { didDetected didDetected { ( ) } { ( ) } } in if else print print "Human detected!" "Human Not detected!" GpioManager.swift 人感センサーの連携の実装④
  63. 96 5. 人感センサーを使う g… 侵入者に対して、侵入を検知していることを警告する音声を再Y W… サンプル用のmp3を のコマンドで再Y ’… Ref:

    f… Swiftから上記のサンプル音声を再生するShellスクリプトを実# F… 実行すると、ラズパイのイヤホンジャックに接続したスピーカーから 警告音声が流れる mpg123 https://www.mpg123.df mpg123 警告音を再生する実装の詳細
  64. 97 5. 人感センサーを使う SwiftからShellスクリプトを実行する共通処理を実装 1 2 3 4 5 6

    7 8 9 10 // Ref: https://ja.stackoverflow.com/q/65654 @discardableResult func ... throws -> let = = = try return ( args: ) { task () task.executableURL ( : ) task.arguments args task. () task. () task.terminationStatus } shell _ String Int32 Process URL fileURLWithPath run waitUntilExit "/usr/bin/env" ShellCall.swift 警告音を再生する実装①
  65. 98 5. 人感センサーを使う Macの場合、Package.swiftに音声ファイルをリソースとして指定 resources [ . ( ), ]

    : // Copy resource file to bundle // Macの場合、ビルドの成果物はDerivedDataのディレクトリに生成される // Linuxの場合、cloneしたディレクトリにある音声ファイルを直接参照できる process "warning_voice.mp3" Package.swift 警告音を再生する実装②
  66. 99 5. 人感センサーを使う Mac/Linuxごとに、コマンドと音声ファイルのパスを指定する 1 2 3 4 5 6

    7 8 9 10 let let if os = = else = = endif command: path: # ( ) command path Path.current # command path Path.current # String String Linux "/usr/bin/mpg123" "\( )/Sources/SwiftHomePi" "/opt/homebrew/bin/mpg123" "\( )/SwiftHomePi_SwiftHomePi.bundle/Contents/Resources" // If linux, returns repository path // If Mac, returns DerivedData path ShellCall.swift 警告音を再生する実装③
  67. 100 5. 人感センサーを使う Shellスクリプトを実行する関数に実行するコマンドを指定して、実行 1 2 3 4 5 6

    7 let = do try catch voiceFilePath path { (command, voiceFilePath) } { (error) } "\( )/warning_voice.mp3" // ①コマンドとパラメーターは別々に指定する shell print ShellCall.swift 警告音を再生する実装④
  68. 101 5. 人感センサーを使う ag SwitchBotはAPIが公開されていて、一部のデバイスを除いて、 APIから状態の取得、操作ができc "g Ref: g 今回は↓の2つのデバイスを操作する

    https://github.com/OpenWonderLabs/SwitchBotAPp ←電源プラグ ネットワーク経由で電源のON/OFFを操作できる テープライト→ ネットワーク経由でライトのON/OFFを操作できる SwitchBotのデバイスを動かす実装詳細
  69. 104 5. 人感センサーを使う ‡ SwitchBotのAPIを実行するには、トークンが必E 9‡ SwitchBotアプリの 画面の を連2 a‡

    をタップして、トークンをコピR €‡ 上記TokenをHeaderにセッˆ @‡ ↓のAPIを実行して、SwitchBotのデバイス一覧から操作したいデ バイスの を確認す‚ 9‡ (GET) 設定 アプリバージョン 開発者向けオプション deviceId https://api.switch-bot.com/v1.0/devices 太陽拳システムの実装準備
  70. 105 5. 人感センサーを使う 4B 今回は電源プラグの を実行する方法を紹介す% 5B ↓のEndpointのAPIでJSONをPostで実行する ON/OFF //

    EndPoint→ (POST)https://api.switch-bot.com/v1.0/devices/${DeviceId}/commands // Headers→ Authorization: ${SwitchBot-Token} // OFFにする場合、”turnOff” { : , : , : } "command" "parameter" "commandType" "turnOn" "default" "command" SwitchBotのAPIを実行してみる
  71. 109 まとめ S1 普段書いている でもラズパイの開発ができH T1 を使うことで、Swiftで や を簡単に実装で きH

    V1 実装を追加することで、アイデア次第で一般販売されている 機能を拡張して、より便利に、幅広いことができるようになH 1 や など、 も必ƒ p1 様々な部品や道具などハードを追加で用意することも必要 Swift Vapor API Socket通信 スマート ホーム ラズパイ GPIO ハード面の技術キャッチアップ まとめ
  72. 110 まとめ ‚k 40分の発表は大変...笑(WWDCの人たちすごい!h k Swiftでラズパイの開発ができるのは楽し4 ik ハードを触るのはお金 がかかる...g Xk

    ラズパイでのSwiftの開発はMacのXcodeでの開発より制約が多4 lk Raspberry Pi OSだと、古いSwiftのバージョンしか使えな4 0k UbuntuOSだと、カメラの実装などハード周りの設定の実装が Raspberry Pi OSと比べて、ハードルが高い 感想
  73. 111 まとめ d˜ 人感センサーで検知した時に、カメラで写真を撮影し、TweeX ™˜ 二酸化炭素センサーの情報をSwiftUIのChartsでアプリに表5 7˜ 観葉植物に自動で or 定期的に水をあげi

    x˜ 土壌湿度センサーなども使ってみi e˜ 玄関の解錠・施錠をもっと便利にすi ɘ モーターを使って、アクションを起こす 今後やりたいこと
  74. 112 まとめ スマートホーム化してない人におすすめの初期セット!(約1万円) SwitchBotハブミニ スマホ スマートスピーカー エアコンやシーリングライト
 など、赤外線リコモンで操作 できる家電を操作! Œ{

    寝る前にベッドから、声でシーリングライトを消す(快適!!˜ ‹{ 家に着く前に、スマホからエアコンをつけて、帰る頃に涼しい or暖かい部屋にしておく!(夏と冬は至高!!) QOL爆上げ リモコンの赤外線の信号 を記憶&発進できる トリガー ちなみに。。。
  75. 113 まとめ d˜ 今回のスライドは を使用して、作成していまh T˜ 一般的なスライドツールより、UI作成の感覚に近W g˜ AutoLayoutやフラグの切り替え用の変数などが使え& gg˜

    色、フォントなどをアセットとして管理でき& ggg˜ 作成したレイアウトをコンポーネントとして、共通化でき& g‚˜ プラグインが充実してい& p˜ デザインへの理解を深めると、よりよいプロダクト開発へ繋がる Figma 今回の資料作成方法(おまけ)
  76. 114 まとめ hQ 『これ1冊でできる! ラズベリー・パイ 超入門 改訂第6版」(福田 和宏C @Q Vapor

    DocA )Q Swift on Raspberry P% eQ ラズベリーパイのレシ Q ラズパイにパソリを繋いでNFCタッチÆ IQ SwitchBot AP €Q https://amzn.to/3Azks3 €Q https://docs.vapor.codeA €Q https://www.slideshare.net/mostgood/swift-on-raspberry-pi-23009473’ €Q https://zenn.dev/kotaproj/books/raspberrypi-tipA €Q https://monomonotech.jp/kurage/raspberrypi/nfc.htmh €Q https://github.com/OpenWonderLabs/SwitchBotAPI 主な参考資料・情報