GoとGoogle Homeでガチでメリーさんの電話を実装してみました。


24日目で思ったより位置情報で遊べなかったので、位置情報を使ったネタを考えてみました

24日目
https://qiita.com/usk81/items/3b1357de73d99bb13781

つくったもの

ネタ

位置情報を使ったもの...
メリークリスマス...
メリー...
メリー...

( ゚д゚)ハッ! メリーさん!!

というわけで、メリーさんの電話をリアル実装してみました。

仕様

  1. 最寄り駅からスタート
  2. Google Home経由で連絡を取る
  3. 途中で途中経過を伝える
  4. 家についたらワンコール
  5. 次の瞬間、「貴方の後ろにいるの」
  6. 「ジョークですよw」で終わり

距離に関わらず同じ感覚でGoogle Homeが話すと面白くないので、人間の走る速度で最寄り駅から現在地を移動させて、距離に応じて話す内容を変えるようにしました。

人間が歩く速度(約2〜4.8km)だとゆっくりすぎるので、ウサインボルトが走る速さにしました。

最寄り駅の取得

駅データ.jpで日本の駅の位置情報を取得しました
http://www.ekidata.jp/

参考:
https://qiita.com/dorarep/items/af8c168a24a3ba2c468a

コード

メイン処理は至って簡単です。
1秒間隔で時速に応じて距離を縮めていくだけです。

googlehome関数で5秒待っているのは、
待たないとGoogle Homeにリクエストが渡ってから話し終える前に次のリクエストが渡って中断してしまうのを防ぐためです。

main.go
package main

import (
    "context"
    "errors"
    "fmt"
    "os"
    "strconv"
    "strings"
    "time"

    "github.com/ikasamah/homecast"
)

// km/h
const speed = 44.6

func main() {
    l, err := env2Location()
    if err != nil {
        panic(err)
    }
    ss, err := getStationsFromCSV("station.csv")
    if err != nil {
        panic(err)
    }

    _, dist, err := nearestStation(l, ss)
    if err != nil {
        panic(err)
    }

    ms := speed * 1000.0 / (60.0 * 60.0)

    googlehome("私、メリーさん。今、近くの駅にいるの", "ja")
    time.Sleep(5 * time.Second)
    var flg bool
    for {
        switch {
        case dist <= 0:
            googlehome("私、メリーさん。今、あなたのお家の前にいるの", "ja")
        case dist <= 50 && !flg:
            googlehome("私、メリーさん。今、あなたのお家の近くにいるの。", "ja")
            flg = true
        }
        if dist <= 0 {
            break
        }
        time.Sleep(1 * time.Second)
        dist -= ms
        fmt.Println(dist)
    }
    googlehome("私、メリーさん。今、あなたの後ろにいるの。", "ja")
    googlehome("Hahaha, It's joke!!", "en")
}

func env2Location() (loc Location, err error) {
    strLoc := os.Getenv("Location")
    if strLoc == "" {
        err = errors.New("Can't get location")
        return
    }
    ls := strings.Split(strLoc, ",")
    if len(ls) != 2 {
        err = errors.New(strLoc + " is invalid location")
    }
    lat, err := strconv.ParseFloat(ls[0], 64)
    if err != nil {
        return
    }
    lng, err := strconv.ParseFloat(ls[1], 64)
    if err != nil {
        return
    }
    return Location{
        Lat: lat,
        Lng: lng,
    }, nil
}

func googlehome(msg, lang string) {
    fmt.Println(msg)
    ctx := context.Background()
    devices := homecast.LookupAndConnect(ctx)
    for _, device := range devices {
        device.Speak(ctx, msg, lang)
    }
    time.Sleep(5 * time.Second)
}

距離の取得は球面三角法を使用しました。

参考:
https://qiita.com/chiyoyo/items/b10bd3864f3ce5c56291

distance.go
package main

import (
    "math"
)

const radius = 6378137.0

type Location struct {
    Lat float64 `json:"lat"`
    Lng float64 `json:"lng"`
}

func distance(from, to Location) float64 {
    fx := deg2rad(from.Lat)
    fy := deg2rad(from.Lng)

    tx := deg2rad(to.Lat)
    ty := deg2rad(to.Lng)

    averageLat := (fx - tx) / 2
    averageLon := (fy - ty) / 2

    return radius * 2 * math.Asin(math.Sqrt(math.Pow(math.Sin(averageLat), 2)+(math.Cos(fx)*math.Cos(tx)*math.Pow(math.Sin(averageLon), 2))))
}

// deg2rad transforms radical value
func deg2rad(r float64) float64 {
    return (r * math.Pi) / 180.0
}