0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

iOSショートカットアプリ×AWS Lambdaでエアコンのタイマーを作る

0
Last updated at Posted at 2026-07-20

はじめ

Nature Remoアプリを使っている方の中で、こんな悩みを抱えている人はいませんか?

「「「今から1時間後にエアコンを弱めたい……!!」」」

Nature Remoアプリのオートメーション機能でこれを実現しようとすると、毎回時刻を計算してスケジュールを登録する必要があり、非常に面倒です。オートメーション自体もどんどん増えていってしまいます。

積み重なるオートメーションの山

最初はiOSショートカットアプリの「待機」アクションで解決しようとしましたが、画面をロックするとショートカットの実行が停止してしまい、動かないという問題にぶつかりました。

そこで今回は、AWS Lambda + EventBridge Schedulerを使い、時間管理をiPhone側からクラウド側に完全に移すことで、画面ロック中でも確実に「n分後にエアコン操作」を実行できる仕組みを作ったので、その内容をまとめます。

構成方針

[iOSショートカット]
   → Lambda Function URL(HTTPS)にPOSTリクエスト { "minutes": N }
      → Lambda①「schedule-aircon」
         → EventBridge Schedulerで「現在時刻+N分後」の1回限りスケジュールを作成
            → 実行後は自動削除(ActionAfterCompletion: DELETE)
      → N分後、EventBridgeがLambda②「run-aircon」を起動
         → Nature Remo Cloud APIを叩いてエアコンを操作

実装

Nature Remo Cloud APIの準備

Nature Remo公式サイトからアクセストークンを発行し、appliances一覧を取得してエアコンのappliance_idを控えます。

curl -X GET "https://api.nature.global/1/appliances" \
  -H "Authorization: Bearer <アクセストークン>"

エアコン操作は以下のように呼び出せます。

curl -X POST "https://api.nature.global/1/appliances/<appliance_id>/aircon_settings" \
  -H "Authorization: Bearer <アクセストークン>" \
  -d "operation_mode=cool" \
  -d "temperature=26" \
  -d "air_direction=1" \
  -d "air_volume=1" \
  -d "button="

button=(空文字)を指定すると電源ONの意味になります。

Lambda②: エアコンを実際に操作する関数(run-aircon)

import os
import urllib.request
import urllib.parse

ACCESS_TOKEN = os.environ["NATURE_REMO_TOKEN"]
APPLIANCE_ID = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"

def lambda_handler(event, context):
    url = f"https://api.nature.global/1/appliances/{APPLIANCE_ID}/aircon_settings"
    params = {
        "operation_mode": "cool",
        "temperature": "26",
        "air_direction": "1",
        "air_volume": "1",
        "button": "",
    }
    data = urllib.parse.urlencode(params).encode()
    req = urllib.request.Request(url, data=data, method="POST")
    req.add_header("Authorization", f"Bearer {ACCESS_TOKEN}")
    urllib.request.urlopen(req)
    return {"statusCode": 200, "body": "OK"}

温度パラメータは、appliances一覧のレスポンスに含まれるrange.modes.cool.tempの候補値(文字列)と完全に一致する形式で送る必要があります。"26.0"のような表記は候補にないためHTTPError: 400 Bad Requestになる点は注意(1敗)。

Lambda①: n分後の実行をスケジュールする関数(schedule-aircon)

import boto3
import json
import time
import os
from datetime import datetime, timedelta, timezone

scheduler = boto3.client("scheduler")

TARGET_LAMBDA_ARN = "arn:aws:lambda:ap-northeast-1:xxxxxxxxxxxx:function:run-aircon"
ROLE_ARN = "arn:aws:iam::xxxxxxxxxxxx:role/scheduler-invoke-role"
SECRET = os.environ["SHORTCUT_SECRET"]

def lambda_handler(event, context):
    body = json.loads(event.get("body") or "{}")

    if body.get("secret") != SECRET:
        return {"statusCode": 403, "body": "Forbidden"}

    minutes = int(body.get("minutes", 0))
    run_at = datetime.now(timezone.utc) + timedelta(minutes=minutes)
    schedule_name = f"aircon-{int(time.time())}"

    scheduler.create_schedule(
        Name=schedule_name,
        ScheduleExpression=f"at({run_at.strftime('%Y-%m-%dT%H:%M:%S')})",
        ScheduleExpressionTimezone="UTC",
        FlexibleTimeWindow={"Mode": "OFF"},
        Target={
            "Arn": TARGET_LAMBDA_ARN,
            "RoleArn": ROLE_ARN,
            "Input": "{}",
        },
        ActionAfterCompletion="DELETE",
    )
    return {
        "statusCode": 200,
        "body": json.dumps({"scheduled": schedule_name, "run_at_utc": run_at.isoformat()})
    }

呼び出し元を限定するため、簡易的な合言葉(secret)をbodyに含めてチェックする形にしています。厳密にやるならAWS_IAM認証や署名付きリクエストにするのがより安全です。

IAMロールの構成

今回は3つのロールに分けています。

  • run-aircon-role: run-aircon用。AWSLambdaBasicExecutionRoleのみ
  • schedule-aircon-role: schedule-aircon用。scheduler:CreateScheduleとiam:PassRoleを許可
  • scheduler-invoke-role: EventBridge Scheduler自体が引き受けるロール。信頼ポリシーでscheduler.amazonaws.comを許可し、lambda:InvokeFunctionを許可

iam:PassRoleをワイルドカードで許可すると、IAMのポリシーチェッカーから警告が出ます。今回は動作確認が取れた後に、iam:PassedToService条件キーを付けて対象をscheduler-invoke-roleのみに絞り込みました。

{
  "Effect": "Allow",
  "Action": "iam:PassRole",
  "Resource": "arn:aws:iam::<アカウントID>:role/scheduler-invoke-role",
  "Condition": {
    "StringEquals": {
      "iam:PassedToService": "scheduler.amazonaws.com"
    }
  }
}

iOSショートカットアプリの構成

schedule-airconにLambda Function URLを発行し、iOSショートカットアプリ側からPOSTでJSONを送るだけの構成にしました。実際に組んだショートカット「エアコンおやすみタイマー」の中身は以下の通りです。

1. メニューから選択
   ├─ 30       → テキスト「30」
   ├─ 60       → テキスト「60」
   ├─ 120      → テキスト「120」
   └─ 手入力する → 入力を要求(数字)
   メニューの終了
2. URLの内容を取得(POST, Lambda Function URL)

POST先はLambda Function URLで、bodyにはminutesとsecretをJSON形式で渡します。

{
  "minutes": 30,
  "secret": "xxxxxxxxxxxx"
}
もう少しで夕飯が届く

まとめ

iOSショートカットアプリの「待機」アクションは画面ロックで停止してしまうため、時間管理が重要な処理には不向きでした。時間管理をAWS Lambda + EventBridge Schedulerというクラウド側に完全に移すことで、iPhone側の状態に左右されない、タイマーの仕組みを作ることができました。

個人利用の範囲であれば、Lambda・EventBridge Schedulerともに無料枠に収まる規模のため、追加コストはほぼかからずに運用できています。

同じようにNature Remoアプリのオートメーション機能の使いにくさに悩んでいる方の参考になれば幸いです。

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?