やりたかったことと実際やったこと
pepperで撮影した画像をslackにUPしたかった。
しかし、botはfileuploadのAPIを叩けないっぽかったので、一回S3に画像をUPして、そのURLをattachmentsに指定するという形でやってみた。
slackAPIを触るのは初めてなので、もしかしたらもっとステキなやり方があるのかもしれない。
使ったもの
- boto
- slack_client
- websocket
アプリのルートに「lib」ディレクトリを作成して、上記ファイルをインポートした。
ボックス内容
Take Pictureボックス
既存のボックスに少し手を入れるだけ
ここの処理はアルデバランアトリエさんの記事を参考にして(http://qiita.com/Atelier-Akihabara/items/833e49a079788af55afb)
アプリのルートに「html」ディレクトリを作成し、ダミーのimage.jpgを設置する。
class MyClass(GeneratedClass):
def __init__(self):
GeneratedClass.__init__(self, False)
self.resolutionMap = {
'160 x 120': 0,
'320 x 240': 1,
'640 x 480': 2,
'1280 x 960': 3
}
self.cameraMap = {
'Top': 0,
'Bottom': 1
}
self.recordFolder = "/home/nao/recordings/cameras/"
def onLoad(self):
self.framemanager = ALProxy("ALFrameManager")
self.bIsRunning = False
try:
self.photoCapture = ALProxy( "ALPhotoCapture" )
except Exception as e:
self.photoCapture = None
self.logger.error(e)
def onUnload(self):
pass
def onInput_onStart(self):
# アプリのルート/html/に画像を保存したいのでディレクトリを指定
import os
self.recordFolder = os.path.join(self.framemanager.getBehaviorPath(self.behaviorId), "../html")
if( self.bIsRunning ):
return
self.bIsRunning = True
resolution = self.resolutionMap[self.getParameter("Resolution")]
cameraID = self.cameraMap[self.getParameter("Camera")]
fileName = self.getParameter("File Name")
if self.photoCapture:
self.photoCapture.setResolution(resolution)
self.photoCapture.setCameraID(cameraID)
self.photoCapture.setPictureFormat("jpg")
self.photoCapture.takePicture( self.recordFolder, fileName )
self.bIsRunning = False
self.onStopped()
S3postボックス(pythonボックス)
新規pythonボックスを作成して、Access KeyとSecret Access Key用の新規変数を追加。(typeは文字列)
中途半端にここだけ変数にしたけど、bucketとかもここで変数にすると後々汎用性があっていいんじゃないかしら。
するとこんな感じになるのでIAMで作ったS3アクセス用ユーザーのクレデンシャルを入力する。
保存後にS3のURLを次のボックスにお伝えしたいので、onStoppedのTypeを文字列にしておく。
boxのonStoppedの出力端子(って呼び方でいいのかな)が青色になって文字列を出力できるようになる。パターン青、文字列です。
class MyClass(GeneratedClass):
def __init__(self):
GeneratedClass.__init__(self)
def onLoad(self):
self.framemanager = ALProxy("ALFrameManager")
self.folderName = None
def onUnload(self):
import sys
if self.folderName and self.folderName in sys.path:
sys.path.remove(self.folderName)
self.folderName = None
def onInput_onStart(self, p):
# アプリルート以下にlibディレクトリを作って入れる
import sys, os, time
self.folderName = os.path.join(
self.framemanager.getBehaviorPath(self.behaviorId), "../lib")
if self.folderName not in sys.path:
sys.path.append(self.folderName)
for moduleName in os.listdir(self.folderName):
if moduleName in sys.modules:
self.logger.info("Loaded: %s, %s" % (moduleName, sys.modules[moduleName].__file__))
reload(sys.modules[moduleName])
rootdir = os.path.join(self.framemanager.getBehaviorPath(self.behaviorId), "../html")
# 読み込むファイル名(take photoで撮影してアプリルートのhtml以下に保存したお写真)
load_filename = "image.jpg"
# 保存する際のファイル名は接頭語+時間にしておいた
save_filename = "image_"+str(time.time())+".jpg"
from boto.s3 import connection
from boto.s3.key import Key
# ボックスに追加した変数を取得する
ACCESS_KEY_ID = self.getParameter("AccessKey")
SECRET_ACCESS_KEY = self.getParameter("SecretAccessKey")
BUCKET_NAME = "<使用するBUCKET>"
conn = connection.S3Connection(ACCESS_KEY_ID, SECRET_ACCESS_KEY)
bucket = conn.get_bucket(BUCKET_NAME)
k = Key(bucket)
k.key = save_filename
k.set_contents_from_filename(rootdir + "/" + load_filename)
# UPしたファイルがslackから閲覧できるように公開にしておく
k.make_public()
s3Url = k.generate_url(expires_in=0, query_auth=False)
# 終了の通知を保存したS3ファイルのURLにする
self.onStopped(s3Url)
def onInput_onStop(self):
self.onUnload() #it is recommended to reuse the clean-up as the box is stopped
self.onStopped() #activate the output of the box
Slackボックス(pythonボックス)
slackボックスのonStartも文字列を受け取れるように変更しておく
class MyClass(GeneratedClass):
def __init__(self):
GeneratedClass.__init__(self)
def onLoad(self):
self.framemanager = ALProxy("ALFrameManager")
self.folderName = None
def onUnload(self):
import sys
if self.folderName and self.folderName in sys.path:
sys.path.remove(self.folderName)
self.folderName = None
def onInput_onStart(self, s3Url):
import sys, os
self.folderName = os.path.join(
self.framemanager.getBehaviorPath(self.behaviorId), "../lib")
if self.folderName not in sys.path:
sys.path.append(self.folderName)
for moduleName in os.listdir(self.folderName):
# モジュールのreload
if moduleName in sys.modules:
self.logger.info("Loaded: %s, %s" % (moduleName, sys.modules[moduleName].__file__))
reload(sys.modules[moduleName])
post_text = "pepperですよ〜"
self.logger.info("Slack送信中!... File: %s" % s3Url)
import json
from slackclient import SlackClient
token = "<slackのtoken>"
channel = "<slackのチャンネル>"
user = "<botのユーザー名>"
sc = SlackClient(token)
att = [{
"color": "#36a64f",
"title": "写真です",
"title_link": s3Url,
"text": "pepperから愛をこめて",
"image_url": s3Url
}]
self.logger.info(sc.api_call("chat.postMessage", attachments=json.dumps(att), channel=channel, username=user, as_user=user, text=post_text))
self.onStopped()
def onInput_onStop(self):
self.onUnload() #it is recommended to reuse the clean-up as the box is stopped
self.onStopped() #activate the output of the box
最終的なchoregraphe画面
という訳で無事にpepperで撮影した画像がS3にUPされ、UPした画像のURLを添付したslack postが実行された。
やってみて悩んだこと
実機テスト時のファイルのやりとり
ワークショップに参加した時にアルデバランアトリエのスタッフさんも「動画ファイルとかテストする時は大変です」と言っていたけど
全ファイルを例のlastUploadedChoregrapheBehaviorから削除→再度全ファイルUPという感じなので
ライブラリとか使っていると、たいへんに重たい。
デバッグ/ログ出力
self.logger.info()
でバーチャルロボットはログを確認できたけど、pepper実機では全然出力されなかった。
重大なエラーの場合boxが赤くなってマウスオーバーするとエラーメッセージが見られたけど、そうでもない時はうんともすんとも言わないpepperをドキドキしながら見つめてはコードをチェックしていた。
アトリエのワークショップで、タブレットのログの出し方は教わったけど、他はどう出したらいいのかな〜。
バーチャルロボットと同じようにログを確認したい。
これからやりたいこと
- 撮影時の盗撮感がすごいので改良したい。とりますよー!3,2,1!みたいな感じで撮影してもらいたい。シャッター音もあったほうがいいかな。
- パッケージ化?というのか、あのタブレットからタップして起動できる形のアプリを作りたい。