はじめに
以前に外出先からエアコンを動かす仕組みを作りました。
しかしながら、実際に使っていると、うっかり忘れてしまうという運用上の欠点が発覚しました(知ってた)。
そこで今回は、予め決まった時間になると「エアコンをONにする?」と聞くようにすることで、うっかり忘れてしまうことを防ぎたいと思います!
全体像
Lambdaを定期実行し、「エアコンをONにする?」とSlackにボタン付きでPOSTします。
SlackでYesボタンを選択すると、別途用意されているAPI Gatewayを叩くことでエアコンが付きます!
なお、本記事の内容は、赤枠部分になります。赤枠以外は前回の内容そのままです。
環境構築
Slackの環境構築
難しいことは行いません。メッセージを「受信」してアクション結果を「送信」するSlackアプリを作成します。
なお、投稿対象となるチャンネルは準備済みとします。
アプリの作成
Slackの下記サイトを開き、Appを作成します。
適当に作成します。
Incoming Webhooks
「Incoming Webhooks」を選択し、Onにします。
一番下の「Add New Webhooks to Workspace」を選択し、許可を行います。
ここで作成されたWebhooks URLは後に必要になるためメモしておきます。
Interactive Components
続いて、「Interactive Components」を選択し、Onにします。
「Request URL」には、応答先のURLを入力し、「Save Changes」を選択します。
ここの応答先とは、Slack上でボタンを押した際にPOSTする先となります。私の場合は、以前に作成したAPI Gatewayを使い回すため、そのURLを入力しています。
AWSの環境構築
Slackに通知するLambda(定期実行)を作成します。
AWS SAMを使っているため、ポイントだけご紹介します。
Lambda
import os
import json
import requests
SLACK_WEBHOOK_URL = os.environ["SLACK_WEBHOOK_URL"]
def lambda_handler(event, context):
post_slack("エアコンをOnにしますか?")
def post_slack(message):
payload = {
"attachments": [
{
"text": message,
"callback_id": "ask_aircon",
"color": "#3AA3E3",
"attachment_type": "default",
"actions": [
{
"name": "aircon",
"text": "Yes",
"type": "button",
"value": "yes"
},
{
"name": "aircon",
"text": "No",
"type": "button",
"value": "no"
}
]
}
]
}
# http://requests-docs-ja.readthedocs.io/en/latest/user/quickstart/
try:
response = requests.post(SLACK_WEBHOOK_URL, data=json.dumps(payload))
except requests.exceptions.RequestException as e:
print(e)
else:
print(response.status_code)
環境変数として下記を設定しています。
Key | Value |
---|---|
SLACK_WEBHOOK_URL | Incoming WebhooksのWebhooks URL |
全貌はこちらをどうぞ。
CloudWatch Events
下記のcron式を使用し、「毎週月~金のAM10時(UTC)」に実行させています。日本時間の19時ですね。帰宅時間!(帰れるとは言ってない)
cron(0 10 ? * MON-FRI *)
Raspberry Pi
以前の作成したRaspberry Piのコードを修正します。
修正結果はこちら。受信データの解析処理を分離させました。
#長いので省略
def subscribe_callback(client, userdata, message):
logger.info("Received a new message: ")
logger.info(message.payload)
logger.info("from topic: ")
logger.info(message.topic)
params = parse_payload.parse(message.payload)
logger.info(json.dumps(params, indent=4))
remote_control(params)
def remote_control(params):
if is_aircon_on(params):
logger.info("Execute GPIO_AIRCON_PIN")
execute(GPIO_AIRCON_PIN)
def is_aircon_on(params):
if params["command"] == "/control" and params["param"] == "aircon":
return True
if params["command"] == "ask_aircon" and params["param"] == "yes":
return True
return False
import json
import urllib.parse as url_parse
def parse(payload):
format_type = _check_format(payload)
if format_type == "slash_commands":
data = _parse_slash_commands(payload)
return {
"command": data["command"],
"param": data["text"],
"data": data
}
elif format_type == "interactive_message":
data = _parse_interactive_message(payload)
return {
"command": data["callback_id"],
"param": data["actions"][0]["value"],
"data": data
}
else:
return {}
def _check_format(payload):
data = url_parse.unquote(payload.decode(encoding="utf-8"))
if data.startswith("payload="):
return "interactive_message"
else:
return "slash_commands"
def _parse_slash_commands(payload):
params = {}
key_value_list = url_parse.unquote(payload.decode(encoding="utf-8")).split("&")
for item in key_value_list:
(key, value) = item.split("=")
params[key] = value
return params
def _parse_interactive_message(payload):
data = url_parse.unquote(payload.decode(encoding="utf-8")).lstrip("payload=")
return json.loads(data)
全貌はこちらをどうぞ。
動作結果
平日の19時になると、Slackにメッセージが来ます。それをiPhoneが通知してくれます。
通知をタップ、もしくは、Slackを開くと下記のようになっており、「Yes」を選択すればエアコンがONになります!!
1週間ほど運用してみましたが、エアコンを付け忘れて帰宅しても部屋が暑い、ということは無くなりました!!はっぴー!!
参考
- https://api.slack.com/interactive-messages
- https://api.slack.com/docs/message-buttons
- https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md
- https://qiita.com/masakabe/items/c47fc98e939475594842
- https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/events/ScheduledEvents.html