夫婦の家事事情をAlexaとTrelloで解決した話

# 0.前日譚

我が家(共働き・夫婦二人暮らし)の家事の分担は原則「気づいたものがやる」で回っています。
この「気づいたものがやる」はある意味で平等に見えるのですが、その実、**気が利く者の方が損をするシステム**でもあります。

早い話、僕が、
「洗濯回さなきゃな、ゴミ捨てなきゃな、あと皿も洗わなきゃな」
と考えている一方、妻が、
「洗濯、コンロ磨き、カーテンにリセッシュ、バスマットの除菌、洗剤の詰め替え、ゴミ捨て、トイレ掃除、床掃除、シーツの取り換え、テレビの埃拭き、皿洗い　etc etc...」
と、いろんな家事をタスクとして積んでいれば、担当する家事のタスク量も全く違うわけですね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043878/012c84fc-2be7-4291-f4cf-efc2366e5ae6.png)
**※イメージ図です**

このことを先日、妻が言いにくそうに指摘してきたため、僕は「確かにそうだなぁ」と思いました。（今まですまぬ。。。妻よ。。。）

# 1.Trelloの導入

というわけで現状課題の分析と理想像を策定。

* AsIs
    * 家事が二人の間で共有されておらず、妻側にとって納得感を持った家事の分担ができていない。
* ToBe
    * 家事のリストを見える化し、お互いに家事の一覧がいつでも確認できる。その上で二人の協議を以て家事を分担する。（お互いを尊重する気持ちが大事）

タスクの見える化と言えば、まず真っ先にタスク管理ツールが思い浮かびます。
その中でも、操作が簡単で、全体的なレイアウトもシンプルな、Trelloを採用することとしました。
スマホアプリ版もあるので、「お互いに家事の一覧がいつでも確認できる」という要件も満たせます。

ここまで決めたところで、ふと、我が家のリビングに鎮座する専ら音楽再生機と化しているecho show君が目に入ります。
Alexa……、お前、やれるのか……？

#2. Alexaスキル開発

Alexaにてタスク名を登録できるようになれば、それすなわち、声出しによる互いの家事の共有になるのではないかという発想。
システム概略図は下記みたいな感じです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043878/ddef5dc2-47eb-482b-3739-e7204651854e.png)

上記を実現するため、早速、AlexaSkillsKitを用いて、開発していきます。

なお、Amazonアカウントの取得やAlexaスキルの基礎的な開発方法は下記のチュートリアルに譲ります。
[チュートリアル](https://developer.amazon.com/ja-JP/alexa/alexa-skills-kit/get-deeper/tutorials-code-samples/build-a-skill)

まず、想定されるタスク名は任意（「風呂掃除」、「買い出し」等）であるため、Alexaに任意の言葉を認識させるため、インテントの設定を行います。

```json
{
    "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": "ほげほげ"
                            }
                        }
                    ]
                }
            ]
        }
    }
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043878/74441e87-a345-36db-a659-4f9b1dc4acad.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043878/50e6edd7-b13f-a7a9-f351-02073684014b.png)


`TaskIntent`は任意の言葉が入るサンプル発話`{task}`を持ちます。
また`{task}`のスロットタイプに`any`を作成し、スロット値は適当に`ほげほげ`としておきます。
本来、Intentはスキルに対するユーザの意図を表すため、このような何でも許容する設定は推奨されませんが、今回はタスク名を自由発話させたいため、上記設定で行きます。

Intentの設定が完了しましたので、次にコードです。
今回はTrelloにアクセスするライブラリとして[py-trello](https://github.com/sarumont/py-trello)を用いることとしました。
lamdaサーバにて`py-trello`を扱うために、まずは設定ファイルを書き換えます。

```requirements.txt
boto3==1.9.216
ask-sdk-core==1.11.0
py-trello
```

続いて、`lamda_function.py`を以下のコードを記述します。

```python: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の取得は下記コードを実行すれば取得できます。

```python:GetTrelloID.py
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. 実行イメージ

1. **発話にて**Alexaに家事の追加を指示。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043878/fc6536a3-8cb6-550b-be3a-de5bb03ff65c.png)

1. その後のTrelloの画面
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043878/32a0a41f-d092-286a-eaaf-2896d87a5ecb.png)

#4. 実証

さて、実際にこのAlexaスキルを妻に説明して、運用してみたところ、

**想像以上に好評でした。**

※下記は実際の我が家のTrelloです。(画像はPC版です)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1043878/3abc5909-fcb4-4d84-b8ec-9584a650f449.png)

* Trelloによるタスク管理はシンプルで使いやすい
* 家事の全てが見える化できて、タスクの偏りによる不公平が生じない
* 発話することで家事を登録するため、**家事に対する連帯感**が沸く
    * また、発話自体が互いの**タスクの確認**になるため、手間が省ける

妻からは等々のフィードバックを頂きました。（ありがてえ）

今後、運用を回し、新たな要件・問題点等が生じたら、逐次対応していくつもりです。

#5. 余談

開発中に気づいたんですけど、
AlexaはデフォルトでToDo機能を持ってるんですね。(悲)
[Amazon Alexa で Todoist を使う](https://todoist.com/ja/help/articles/use-amazon-alexa-with-todoist)

#6. 参考文献

[(公式)チュートリアル](https://developer.amazon.com/ja-JP/alexa/alexa-skills-kit/get-deeper/tutorials-code-samples/build-a-skill)
[(公式)スキルのサンプル](https://developer.amazon.com/ja-JP/docs/alexa/alexa-skills-kit-sdk-for-python/sample-skills.html)  ~~日本語情報が少なくて、サンプルコードが正直一番参考になった。~~
[ユーザーの自由発話に対応する Alexa Skill を作る](https://qiita.com/halhorn/items/48998d6b7a166cc905c6)
[TrelloAPIをpythonで使う](https://qiita.com/baraobara/items/1059e57ddc37f79ae78a)