root, 非rootwを意識する必要がある

http://www.yunabe.jp/docs/python_package_management.html

 

# pip install paho-mqtt

-> /usr/local/lib/python2.7/dist-packages/paho

 

パスに権限がない場合

$ pip install paho-mqtt

-> /home/pi/.local/lib/python2.7/site-packages/paho

 

パスの違いに注意

slackのOutgoing Webhooksから受信する

Proxy統合ではなく、Lambda側のインタフェースを決めてしまう

 

/raspberrypi/{proxy+} を作成し、POSTのみ作成

/raspberrypi/speak で呼び出し、speakの場合、音声再生要求を投げる

Lambda関数を作成

実装はシンプル

https://github.com/nilesflow/Slack2RaspberryPi

下記等参考に

https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html

query paramaterのtokenの値の検証のみ

tokenの値はコンソール上の環境変数に設定

Lambdaを登録しておく。

 

フォーマットは下記の形式

https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-output-format

 

Lambdaオーソライザー

ロールはLambda実行権限含むものを作成しておく

 

クエリパラメータにtokenを設定して認証させたいので、認証でLambdaリクエストオーソライザーを設定。

~検証とURLクエリ文字列パラメータを設定。

リクエストパスのproxyを入力しておく

・作成したLambdaを指定

・プロキシ統合は使わずに、呼び出し側ロジックはAPI Gateway側で吸収

・リクエストパラメータのproxyをマッピング

 

・Content-Type=application/x-www-form-urlencoded で送信される

・それ以外は厳しめに通過禁止に設定しておく

 

・マッピングテンプレートは次の通り

## slackのOutgoing Webhooksから下記の形式で送信される
## token=viK8ruCmEfBjd9u84MOEeTWo
## team_id=T0001
## team_domain=例
## channel_id=C2147483705
## channel_name=テスト
## timestamp=1355517523.000005
## user_id=U2147483697
## user_name=スティーブ
## text=googlebot: 身軽なツバメの対気速度はどのくらい?
## trigger_word=googlebot:

## POSTパラメータの分割(A=x&B=y)
#set($aParams = $input.body.split(“&”))

## path変数の分割
#set($aPaths = $input.params(‘proxy’).split(“/”))

## 出力されるJSONデータの生成
{
##pathの次階層をactionとする
“action” : “$aPaths[0]”,

## 必要なPOSTパラメータのみをマッピング
#foreach( $param in $aParams)
#set($hParam = $param.split(“=”))
#if( $hParam[0] == “text”)
“text” : “$util.urlDecode($hParam[1])”,
#end
#if( $hParam[0] == “user_name”)
“user” : “$util.urlDecode($hParam[1])”,
#end
#end

## カンマ制御のためのダミー
“dummy” : null
}

 

Lambda側で定義される次の変数をマッピングしている

action

text

user

 

・統合レスポンスは200のみ設定

slackへ応答を返すには、200でレスポンスパラメータを返す必要があるため

マッピングテンプレートは次の通り

## for slack Outgoing Webhooks
## 全て200の単一フォーマットで返却
#set ($inputRoot = $input.path(‘$’))

## Lambdaで正常に処理された場合
#if ($input.path(‘$.statusCode’) > 0)

## 200の場合のフォーマット
#if ($input.path(‘$.statusCode’) == 200)
#set ($body = $util.parseJson($input.path(‘$.body’)))
{
“text” : “$body.message”
}

## それ以外の場合のフォーマット
#else ## 200
#set ($body = $util.parseJson($input.path(‘$.body’)))
{
“text” : “$body.error: $body.error_description”
}
#end ## not 200

## 例外等
#else ## statusCode
{
“text” : “$input.path(‘$.errorMessage’)”
}
#end ## not statusCode

Lambdaの応答の次のケース全てを200として返却する

・ステータスコード200:正常

・ステータスコード200以外:異常(Lambda実装コード中で認識したエラー)

・例外等それ以外:異常(Lambda実装コード中以外のエラーも含む)

 

・ゲートウェイのレスポンスで、API Gatewayで検出したエラーのレスポンスフォーマットを定義する

これも、SlackのOutgoing Webhooksのフォーマットに合わせておく。

400, 500も200で返すようにしておく。

 

メソッドレスポンスモデルは不要なのか。

https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/api-gateway-method-settings-method-response.html

統合レスポンス、ゲートウェイのレスポンスでまかなえているようだった。

slack → RaspberryPiを実現するため

python3.6で実装

下記を参考に

【AWS Lambdaの基本コードその4】 AWS IoTにメッセージをPublish

事前に作っておいたエンドポイント raspberrypi/speak へ送信

RaspberryPi側でsubscribeできることを確認しておく

 

Lambda応答

return {
‘isBase64Encoded’: False,
‘statusCode’: statusCode,
‘body’: json.dumps(dict),
‘headers’: {
‘Content-Type’: ‘application/json’,
},
}

 

Lambdaの注意点

API Gateway側でプロキシ統合を選んだ場合は、Lambda側で実装しなくてはならない

API Gateway側で吸収するなら、決まった形式で返す必要がある

こことか参考にさせてもらった

https://qiita.com/taknuki/items/dd47d1c6d4190b52df9a#%E3%83%AC%E3%82%B9%E3%83%9D%E3%83%B3%E3%82%B9

 

テスト送信しておく

AWS IoT

raspberry pi用のthingsを作成

ダウンロードした証明書とroot証明書を配置、エンドポイントを控えておく

 

Raspberry Pi

AWS IoTのMQTTクライアントを導入。Pahoを使用。

下記にまとめている

https://github.com/nilesflow/AWSIoTSpeaker/blob/master/README.md

 

AWS IoT Device SDK for C 等もあるが、pythonクライアントとした

下記ソースを参考にさせて貰った

MQTT with AWS IoT Platform using Python and Paho

から参照して下記Git Repository

https://github.com/pradeesi/AWS-IoT-with-Python-Paho

 

準備

pip install paho-mqtt

pip install boto3

git clone https://github.com/nilesflow/AWSIoTSpeaker/blob/master/README.md

証明書を配置

vi config.ini

  • AWS IoTのHost、証明書情報を入力
  • トピック名を指定

今回は、raspberrypi/#とした

 

起動(バックグラウンド起動しておく)

python index.py &

 

Amazon polly でテキスト→音声変換し、mp3ファイルをローカルに保存

mp3ファイルを再生

音声ファイルの生成処理は下記を参照

AWS SDK for Python (boto3)でAmazon Pollyの声を使い分けてみる

 

mp3の再生は下記を参考に

第12回「Raspberry Piで音遊び!」

 

コマンドラインからの再生テスト

apt-get install mpg321

mpg321 polly.mp3

 

pythonからの再生は下記を参照

https://qiita.com/Nyanpy/items/cb4ea8dc4dc01fe56918

pygameで再生

 

上記ソースでは、raspberrypi/speak トピックで指定音声を再生するようにしている

AWS IoTのテスト画面からトピックを発行して確認しておく

HW: Raspberry pi model B+

Raspbian GNU/Linux 9.1 (stretch)

 

下記記事を参考に

http://d.hatena.ne.jp/embedded/20151025/p1

インストール

$ sudo apt-get install ipheth-utils libimobiledevice-utils ifuse usbmuxd
$ sudo mkdir /media/iphone

$ sudo ifuse /media/iphone

認識しなかったので、USBを抜き差し

iPhoneで信頼するデバイスかどうかの確認が表示される

信頼すると、USBテザリングが繋がる

 

 

HW: Raspberry pi model B+

Raspbian GNU/Linux 9.1 (stretch)

 

バージョンの確認

https://qiita.com/fujit33/items/16ce964c228a63a9d8dd

 

音の確認は下記を参考に。

https://qiita.com/plsplsme/items/57b8d79d3725497fd69b

※そのままの設定で全部鳴った

 

ノイズ対策

そのままだと気になるくらいバックグラウンドノイズが酷い。

下記記事中のファームアップデートとコンフィグ設定で殆ど気にならないくらいまで改善。

 

ファームの更新

$ sudo rpi-update

コンフィグ設定

vi /boot/config.txt

HTTP1.1

RFC 7231

https://triple-underscore.github.io/RFC7231-ja.html

https://triple-underscore.github.io/RFC7231-ja.html#section-5.3.5

 

RFC 4647

言語タグの照合

“言語範囲” (“language-range”) の構文定義

言語範囲と言語タグ (language tags) を比較し照合するためのメカニズム

September 2006

http://ofmind.net/doc/rfc/ja/4647#SEC2.1

言語タグと言語範囲は大文字小文字を区別して扱われるべきではない: いくつかのサブタグを大文字にする慣習が存在するが,それが何か意味を持ってはならない。 言語タグと言語範囲との照合は大文字小文字を区別しないやり方で行われなければならない。

language-range = (1*8ALPHA *(“-” 1*8alphanum)) / “*”
alphanum = ALPHA / DIGIT

ALPHA
= %x41-5A / %x61-7A
; 英字( A-Z / a-z )

言語タグに類似している

 

RFC4646

http://pentan.info/doc/rfc/j4646.html

region = 2ALPHA ; ISO 3166 code
/ 3DIGIT ; UN M.49 code

私用と拡大を含むタグとそれらの「副-タグ」は大文字と小文字を区別しないとして扱われることになっています: いくらかの「副-タグ」の資源化のためのコンベンションは存在しますが、意味を運ぶためにこれらを取ってはいけません。

o [ISO3166-1] recommends that country codes be capitalized (‘MN’
Mongolia).
o [ISO3166-1]は、国名略号が大文字で書かれることを(‘ミネソタ’モンゴル)勧めます。

 

“基本言語範囲” (basic language range) は [RFC3066] の言語タグと同じ構文である。

 

https://www.ietf.org/rfc/rfc3066.txt

2.1 Language tag syntax

The language tag is composed of one or more parts: A primary language
subtag and a (possibly empty) series of subsequent subtags.

The syntax of this tag in ABNF [RFC 2234] is:

Language-Tag = Primary-subtag *( “-” Subtag )

Primary-subtag = 1*8ALPHA

Subtag = 1*8(ALPHA / DIGIT)

The productions ALPHA and DIGIT are imported from RFC 2234; they
denote respectively the characters A to Z in upper or lower case and
the digits from 0 to 9. The character “-” is HYPHEN-MINUS (ABNF:
%x2D).

All tags are to be treated as case insensitive; there exist
conventions for capitalization of some of them, but these should not
be taken to carry meaning. For instance, [ISO 3166] recommends that
country codes are capitalized (MN Mongolia), while [ISO 639]
recommends that language codes are written in lower case (mn
Mongolian).

 

– All 2-letter subtags are interpreted as ISO 3166 alpha-2 country
codes from [ISO 3166], or subsequently assigned by the ISO 3166
maintenance agency or governing standardization bodies, denoting
the area to which this language variant relates.

# nodeの準備

cd /usr/local/src

wget https://nodejs.org/dist/v6.11.2/node-v6.11.2-linux-x64.tar.xz

xz -dc node-v6.11.2-linux-x64.tar.xz | tar xfv –

cp node-v6.11.2-linux-x64/bin/node /usr/local/bin/node-stable

 

# npm の準備

#npm が存在する環境の場合、そのnpmで良いか確認。

#npm が無い環境の場合、npmをdownload。★環境のnpmが上書きされるので注意

curl -L https://www.npmjs.com/install.sh | sh

#必要に応じてバージョン指定

curl -L https://www.npmjs.com/install.sh | env npm_install=2.11.3 sh

 

#i18next-conv , i18next-scanner の場合

# インストール

npm -g install i18next-conv

# -> /usr/local/bin/i18next-conv -> ../lib/node_modules/i18next-conv/bin/index.js

npm -g install i18next-scanner

# -> /usr/local/lib/node_modules/i18next-scanner/

 

# 実行

/usr/local/bin/node-stable /usr/local/bin/i18next-conv -h

export NODE_PATH=$(npm root -g) && /usr/local/bin/node-stable index.js

# -> include “var Parser = require(i18next-scanner).Parser;”