GoogleHomeにFelicaカードの残高を教えてもらおう

libpafeをインストール

sudo apt install libavahi-compat-libdnssd-dev 
mkdir tmp
cd tmp

# libpafeをインストール
sudo apt install libusb-dev
wget http://hito.music.coocan.jp/pasori/libpafe-0.0.8.tar.gz
tar zxvf libpafe-0.0.8.tar.gz
cd libpafe-0.0.8/

## 以下3行は不要かもしれない
./configure
make
sudo make install

sudo apt install debhelper autotools-dev
dpkg-buildpackage -us -uc
sudo dpkg -i ../libpafe_0.0.8-1_armhf.deb

ls /lib/udev/rules.d/ | grep libpafe # udevの設定ができているか確認


# libpafe-devをインストール
wget http://hito.music.coocan.jp/pasori/libpafe-dev_0.0.8-1_all.deb
sudo dpkg -i libpafe-dev_0.0.8-1_all.deb

# libpafe-rubyをインストール
wget http://hito.music.coocan.jp/pasori/libpafe-ruby-0.0.8.tar.gz
tar xvzf libpafe-ruby-0.0.8.tar.gz 
cd libpafe-ruby/

sudo apt install autoconf bison build-essential libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libncurses5-dev libffi-dev libgdbm-dev
sudo apt install ruby ruby2.3-dev

ruby extconf.rb
make
make install

google-home-voicetext のインストール

cd ~/prog
git clone https://github.com/sikkimtemi/google-home-voicetext.git
cd google-home-voicetext
npm install

patch -u -R node_modules/mdns/lib/browser.js < mdns_patch/browser.js.patch

onServiceUpが1度しか動作しないようにする

diff --git a/google-home-voicetext.js b/google-home-voicetext.js
index a856df4..435fe46 100644
--- a/google-home-voicetext.js
+++ b/google-home-voicetext.js
@@ -22,7 +22,13 @@ const ip = function(ip) {
 const notify = function(message, callback) {
   if (!deviceAddress) {
     browser.start();
+    serviceUpCalled = false;
     browser.on("serviceUp", function(service) {
+      if(serviceUpCalled)
+        return;
+      else
+        serviceUpCalled = true;

ポート番号の変更

既にスマートリモコンプログラムが動いているからか8080番, 8888番では動かなかったのでそれぞれ8079番,8887番で動かすことにし, 適切に書き換える

Rubyスクリプトを書く

ICカード読み込み部分はあっきいさんのそのままです。

# coding: utf-8

# IC balance checker  /  2019 @y_sni
#   Licence:
#     MIT License
#   references:
#     http://homepage3.nifty.com/slokar/pasori/libpafe-ruby.html
#     http://iccard.jennychan.at-ninja.jp/format

#require 'wiringpi'
require "pasori"
require "net/http"
require 'uri'

def send_message(message)
    url = "http://localhost:8079/google-home-voicetext"
    puts message
    res = Net::HTTP.post_form( URI.parse(url), {'text'=>message} )
    puts res.body
end

pasori = Pasori.open

begin
  begin
  felica = pasori.felica_polling
    system = felica.request_system()
    card = {}
    system.each {|s|
      puts "system called"
      if (s == Felica::POLLING_SUICA || s == -31065 || s == -32546 || s == -31138 || s == -30261)
        # Suica (Generic traffic IC card) balance
        felica.foreach(Felica::SERVICE_SUICA_HISTORY) {|l|
          data = l.unpack('CCnnCCCCvN')
          card["name"] = "Suica"
          if (s == -32546)
            card["name"] = "Iruca" end
          if (s == -31138)
            card["name"] = "SAPICA" end
          if (s == -30261)
            # PASMO(TOKYU noruleage honorary station master card)
            card["name"] = "PASMO" end
          card["balance"] = data[8]
          break
        }
        # SAPICA point
        felica.foreach(0xBA4B) {|l|
          data = l.unpack('CCCNNNC')
          card["point"] = data[2] | data[1] | data[0]
        }
      elsif (s == -512)
        pasori.felica_polling(Felica::POLLING_EDY) {|felica2|
          # Edy balance
          felica2.foreach(0x1317) {|l|
            data = l.unpack('VVNnv')
            card["name"] = "Edy"
            card["balance"] = data[0]
            break
          }
          # nanaco balance
          felica2.foreach(0x5597) {|l|
            data = l.unpack('VVVV')
            card["name"] = "nanaco"
            card["balance"] = data[0]
            break
          }
          # nanaco point
          cnt = 0
          felica2.foreach(0x560B) {|l|
            if (cnt == 0)
              cnt += 1
              next
            end
            data = l.unpack('CCC')
            card["point"] = data[0] << 16 | data[1] << 8 | data[2]
            break
          }
          # waon balance
          felica2.foreach(0x6817) {|l|
            data = l.unpack('vNNNn')
            card["name"] = "WAON"
            card["balance"] = data[0]
            break
          }
          # waon point
          felica2.foreach(0x684B) {|l|
            data = l.unpack('CCC')
            card["point"] = data[0] << 16 | data[1] << 8 | data[2]
            break
          }
        }
      elsif (s == 0xA604)
        felica2.foreach(0x00CF) {|l|
          data = l.unpack('NNNCCCC')
          card["balance"] = data[4] << 16 | data[5] << 8 | data[6]
          card["name"] = "LuLuCa"
          break
        }
      elsif (s == 0x0F04)
        felica2.foreach(0x030F) {|l|
          data = l.unpack('NNNCSC')
          card["balance"] = data[4]
          card["name"] = "ナイスパス"
          break
        }
      else
        if (s != 1223 && s != -32638 && s != -31445)
          card["name"] = "Unknown"
          card["balance"] = sprintf("%d(%X)", s, s)
        end
      end
    }

    send_message "#{card["name"]}の残高は#{card["balance"]}円です。"
    #sleep 5
  rescue => ee
    puts 'error'
    send_message "ICカードがありません"
    #sleep 1
  end #while true

rescue Interrupt
  felica.close
  pasori.close
  print "\n\n"
  exit 0
end

サーバーが自動で起動するようにする

~/bin/googlehomeserver.sh

#! /bin/bash

cd /home/pi/prog/google-home-voicetext

export VOICETEXT_API_KEY=YOUR_API_KEY
export WIRELESS_IP=RASPBERRY_PI_ADDRESS

node file-server.js &
sleep 5
node api-server.js

/etc/systemd/system/googlehomevoice.service

[Unit]
Description = google home server

[Service]
ExecStart=/home/pi/bin/googlehomeserver.sh
Restart=always
Type=simple

[Install]
WantedBy=multi-user.target
sudo systemctl enable googlehomevoice.service

苦労した話

  • 間にsleepを入れないとエラーになった
  • 最後のコマンドの末尾に&を付けると実行が終了したと判断されて殺されるらしい

Slackから起動するようにする

~/homebot/bin/iccard-check.sh

#! /bin/sh
ruby /home/pi/prog/iccard-check.rb

既になにかしらのコマンドをSlackから実行できているとして,

$ cd ~/homebot
$ bin/hubot
homebot> homebot command readic bin/iccard-check.sh

Slackでhomebotにsend readic()と話しかけると~/homebot/bin/iccard-check.shが実行されるようになる。

苦労した話

  • hubotのコマンド名には英字大文字は使えない

GoogleHomeへの呼びかけで起動するようにする

IFTTTで適切に設定する

参考

格安スマートリモコンの作り方 - Qiita

初心者歓迎詐欺被害者の会: Rubyでrequire 'pasori' したい話

libpafe

libpafe-ruby

Rasberry Piの起動時にhubotも自動的に起動させる - Smart Home

ストロベリー・リナックスのSB1602BWかスイッチサイエンスのI2C LCDを使って、Felica系カードの残高を表示するスクリプト。OSCのデモとかで使用中。 · GitHub