0.前日譚
我が家(共働き・夫婦二人暮らし)の家事の分担は原則「気づいたものがやる」で回っています。
この「気づいたものがやる」はある意味で平等に見えるのですが、その実、気が利く者の方が損をするシステムでもあります。
早い話、僕が、
「洗濯回さなきゃな、ゴミ捨てなきゃな、あと皿も洗わなきゃな」
と考えている一方、妻が、
「洗濯、コンロ磨き、カーテンにリセッシュ、バスマットの除菌、洗剤の詰め替え、ゴミ捨て、トイレ掃除、床掃除、シーツの取り換え、テレビの埃拭き、皿洗い etc etc...」
と、いろんな家事をタスクとして積んでいれば、担当する家事のタスク量も全く違うわけですね。
このことを先日、妻が言いにくそうに指摘してきたため、僕は「確かにそうだなぁ」と思いました。(今まですまぬ。。。妻よ。。。)
1.Trelloの導入
というわけで現状課題の分析と理想像を策定。
- AsIs
- 家事が二人の間で共有されておらず、妻側にとって納得感を持った家事の分担ができていない。
- ToBe
- 家事のリストを見える化し、お互いに家事の一覧がいつでも確認できる。その上で二人の協議を以て家事を分担する。(お互いを尊重する気持ちが大事)
タスクの見える化と言えば、まず真っ先にタスク管理ツールが思い浮かびます。
その中でも、操作が簡単で、全体的なレイアウトもシンプルな、Trelloを採用することとしました。
スマホアプリ版もあるので、「お互いに家事の一覧がいつでも確認できる」という要件も満たせます。
ここまで決めたところで、ふと、我が家のリビングに鎮座する専ら音楽再生機と化しているecho show君が目に入ります。
Alexa……、お前、やれるのか……?
#2. Alexaスキル開発
Alexaにてタスク名を登録できるようになれば、それすなわち、声出しによる互いの家事の共有になるのではないかという発想。
システム概略図は下記みたいな感じです。
上記を実現するため、早速、AlexaSkillsKitを用いて、開発していきます。
なお、Amazonアカウントの取得やAlexaスキルの基礎的な開発方法は下記のチュートリアルに譲ります。
チュートリアル
まず、想定されるタスク名は任意(「風呂掃除」、「買い出し」等)であるため、Alexaに任意の言葉を認識させるため、インテントの設定を行います。
{
"interactionModel": {
"languageModel": {
"invocationName": "家事の追加",
"intents": [
{
"name": "TaskIntent",
"slots": [
{
"name": "task",
"type": "any"
}
],
"samples": [
"{task}"
]
},
{
"name": "AMAZON.CancelIntent",
"samples": []
},
{
"name": "AMAZON.HelpIntent",
"samples": []
},
{
"name": "AMAZON.StopIntent",
"samples": []
},
{
"name": "AMAZON.NavigateHomeIntent",
"samples": []
}
],
"types": [
{
"name": "any",
"values": [
{
"name": {
"value": "ほげほげ"
}
}
]
}
]
}
}
}
TaskIntent
は任意の言葉が入るサンプル発話{task}
を持ちます。
また{task}
のスロットタイプにany
を作成し、スロット値は適当にほげほげ
としておきます。
本来、Intentはスキルに対するユーザの意図を表すため、このような何でも許容する設定は推奨されませんが、今回はタスク名を自由発話させたいため、上記設定で行きます。
Intentの設定が完了しましたので、次にコードです。
今回はTrelloにアクセスするライブラリとしてpy-trelloを用いることとしました。
lamdaサーバにてpy-trello
を扱うために、まずは設定ファイルを書き換えます。
boto3==1.9.216
ask-sdk-core==1.11.0
py-trello
続いて、lamda_function.py
を以下のコードを記述します。
from ask_sdk_core.skill_builder import SkillBuilder
from ask_sdk_core.dispatch_components import AbstractRequestHandler
from ask_sdk_core.dispatch_components import AbstractExceptionHandler
from ask_sdk_core.handler_input import HandlerInput
from ask_sdk_model import Response
from trello import TrelloClient
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
class LaunchRequestHandler(AbstractRequestHandler):
"""Handler for Skill Launch."""
def can_handle(self, handler_input):
# type: (HandlerInput) -> bool
return ask_utils.is_request_type("LaunchRequest")(handler_input)
def handle(self, handler_input):
# type: (HandlerInput) -> Response
speak_output = "登録するタスク名を教えて下さい。"
return (
handler_input.response_builder
.speak(speak_output)
.ask(speak_output)
.response
)
class TaskIntentHandler(AbstractRequestHandler):
def can_handle(self, handler_input):
# type: (HandlerInput) -> bool
return ask_utils.is_intent_name("TaskIntent")(handler_input)
def handle(self, handler_input):
# type: (HandlerInput) -> Response
# ユーザ発話内容をそのまま取得
task_name = handler_input.request_envelope.request.intent.slots["task"].value
# Trelloのkeyとsecretを設定
client = TrelloClient(
api_key='XXXXXXXXXXXXX',
api_secret='XXXXXXXXXXXXX',
)
# TrelloのボードIDとリストIDを設定
BOARD_ID="XXXXXXXXXXXXX"
board=client.get_board(BOARD_ID)
LIST_ID="XXXXXXXXXXXXX"
target_list=board.get_list(LIST_ID)
# Trelloにカードを追加
target_list.add_card(task_name)
# Alexaの発話内容を設定
speak_output = task_name + " を登録しました。"
return (
handler_input.response_builder
.speak(speak_output)
# .ask("add a reprompt if you want to keep the session open for the user to respond")
.response
)
#デフォルトコードは記載を割愛
class HelpIntentHandler(AbstractRequestHandler):
class CancelOrStopIntentHandler(AbstractRequestHandler):
class SessionEndedRequestHandler(AbstractRequestHandler):
class IntentReflectorHandler(AbstractRequestHandler):
class CatchAllExceptionHandler(AbstractExceptionHandler):
sb = SkillBuilder()
sb.add_request_handler(LaunchRequestHandler())
sb.add_request_handler(TaskIntentHandler())
sb.add_request_handler(HelpIntentHandler())
sb.add_request_handler(CancelOrStopIntentHandler())
sb.add_request_handler(SessionEndedRequestHandler())
sb.add_request_handler(IntentReflectorHandler()) # make sure IntentReflectorHandler is last so it doesn't override your custom intent handlers
sb.add_exception_handler(CatchAllExceptionHandler())
lambda_handler = sb.lambda_handler()
ポイントはhandler_input.request_envelope.request.intent.slots["task"].value
で設定したIntentに対するユーザ発話が取得できるところです。
なお、上記コードのAPIキーとボードID/リストIDは自分用に読み替えて下さい。
TrelloのAPIキーの取得は下記から可能です。
https://trello.com/app-key
また、ボードIDとリストIDの取得は下記コードを実行すれば取得できます。
from trello import TrelloClient
client = TrelloClient(
api_key='XXXXXXXXXXXXX',
api_secret='XXXXXXXXXXXXX',
)
boards=client.list_boards()
for board in boards:
print(board.name)
print(board.id)
print(board.url)
lists = board.all_lists()
for list_ in lists:
print(" " + list_.name)
print(" " + list_.id)
print(" ---------------")
print("---------------")
これで、ひとまずAlexa側のコーディングは完了。デプロイして実行してみます。
#3. 実行イメージ
#4. 実証
さて、実際にこのAlexaスキルを妻に説明して、運用してみたところ、
想像以上に好評でした。
※下記は実際の我が家のTrelloです。(画像はPC版です)
- Trelloによるタスク管理はシンプルで使いやすい
- 家事の全てが見える化できて、タスクの偏りによる不公平が生じない
- 発話することで家事を登録するため、家事に対する連帯感が沸く
- また、発話自体が互いのタスクの確認になるため、手間が省ける
妻からは等々のフィードバックを頂きました。(ありがてえ)
今後、運用を回し、新たな要件・問題点等が生じたら、逐次対応していくつもりです。
#5. 余談
開発中に気づいたんですけど、
AlexaはデフォルトでToDo機能を持ってるんですね。(悲)
Amazon Alexa で Todoist を使う
#6. 参考文献
(公式)チュートリアル
(公式)スキルのサンプル 日本語情報が少なくて、サンプルコードが正直一番参考になった。
ユーザーの自由発話に対応する Alexa Skill を作る
TrelloAPIをpythonで使う