โปรเจกต์งานอดิเรก → รับคำสั่งไว้ทำทีหลัง
คู่มือ · Home Assistant · Assist · ใช้งานทุกวันตั้งแต่สิงหาคม 2026

“ปิดแอร์ในอีกยี่สิบนาที” — รับคำสั่งไว้ทำทีหลัง ด้วยเสียง

บ้านทำงานด้วย “เดี๋ยวก่อน” ไม่ใช่ “เดี๋ยวนี้”: หลับไปพร้อมพัดลม เปิดม่านตอนพระอาทิตย์ขึ้น ปิดไฟเมื่อหนังจบ ตัวจับเวลาเสียงในตัวของ Home Assistant ทำได้แค่ส่งเสียงเตือน คู่มือนี้เพิ่มการตั้งเวลาสั่งงานจริงให้ผู้ช่วยเสียง — ไฟ แอร์ หรือผ้าม่าน ห้องไหนก็ได้ ระบุเวลาหรือนับถอยหลัง เป็นภาษาอังกฤษ ไทย หรือจีนกลาง — ด้วยคอมโพเนนต์ Scheduler จากชุมชน และสคริปต์เล็ก ๆ สามตัวที่ไม่เคยอ้างเกินกว่าที่ตรวจสอบแล้ว YAML ครบชุด พร้อมปรับใช้

การออกแบบ ก่อนดูโค้ด

หนึ่งพื้นฐาน สามกริยา คอมโพเนนต์ Scheduler สร้างตารางเวลาแบบครั้งเดียวที่เรียกเซอร์วิสหนึ่งครั้งแล้วลบตัวเองได้ ส่วนที่เหลือทั้งหมดคือสคริปต์สามตัวที่โมเดลภาษาเรียกใช้ได้: ตั้งเวลา แสดงรายการ และยกเลิก แต่ละตัวคืนข้อความสำหรับพูดในภาษาที่ผู้ใช้พูดมา และผู้ช่วยถูกสั่งให้พูดตามคำต่อคำ

ตรวจก่อนพูด “ได้เลย ตอนหกโมง” ไม่มีค่าอะไรถ้าไม่มีอะไรถูกสร้างขึ้นจริง สคริปต์ตั้งเวลาจะรอสองวินาที หาเอนทิตีตารางเวลาที่เพิ่งขอสร้าง ตรวจว่าเปิดอยู่และมีเวลาทำงานครั้งถัดไปจริง แล้วจึงบอกเวลา — เวลาที่ตรวจสอบแล้ว อ่านกลับมาจากเอนทิตี ไม่ใช่เวลาที่หวังไว้ การยกเลิกก็ทำแบบเดียวกันแต่กลับด้าน: นับสิ่งที่เหลืออยู่หลังยกเลิก และยอมรับถ้ามีอะไรยังรอดอยู่

กฎอยู่ในคำอธิบายของเครื่องมือ โมเดลภาษาอ่านคำอธิบายของเครื่องมือในจังหวะที่มันกำลังตัดสินใจว่าจะใช้หรือไม่พอดี คำอธิบายจึงเป็นที่อยู่ของการจัดเส้นทาง: อย่าใช้เครื่องมือตัวจับเวลาสำหรับเรื่องนี้ แปลง “หกโมงเช้า” เป็น 06:00 ถือว่า “ไม่ต้องเปิดม่านตอนหกโมง” คือการยกเลิก ห้ามบอกว่ายกเลิกแล้วโดยไม่ได้เรียกเครื่องมือนี้ ในการทดสอบของเรา วิธีนี้น่าเชื่อถือกว่าการใส่กฎเดียวกันไว้ใน system prompt มาก

ล้มเหลวแบบปิด การผสมที่เป็นไปไม่ได้ — ผ้าม่านในห้องที่ไม่มีผ้าม่าน เวลาที่แปลความหมายไม่ได้ — จะคืนข้อความว่าไม่มีอะไรถูกตั้งเวลา โมเดลจึงแต่งความสำเร็จที่ไม่เคยได้รับมาไม่ได้

ตารางเวลาเก็บกวาดตัวเอง repeat_type: single พร้อมวันเริ่มและวันสิ้นสุดตั้งเป็นวันที่ทำงาน หมายความว่าเอนทิตีทำงานครั้งเดียวแล้วหายไป ไม่มีอะไรสะสม “มีอะไรตั้งเวลาไว้บ้าง” จึงได้คำตอบที่เป็นจริงเสมอ

กฎหนึ่งข้อ ที่จ่ายราคามาเต็ม ๆ อย่ายกเลิกตารางเวลาที่ผู้ใช้ตั้งไว้เพียงเพราะการอนุมาน ถ้าคำขอใหม่ดูขัดกับตารางที่มีอยู่ ให้บอกว่ามีอะไรค้างอยู่แล้วถาม สคริปต์ยกเลิกจึงตั้งใจให้เข้าถึงได้ด้วยคำขอที่ชัดเจนเท่านั้น

สิ่งที่ต้องมี

สคริปต์ที่ 1 — ตั้งเวลาสั่งงาน

ระบุอย่างใดอย่างหนึ่งเท่านั้นระหว่าง at_time กับ in_minutes เวลาที่ผ่านไปแล้วของวันนี้จะเลื่อนไปพรุ่งนี้ การสั่งเปิดแอร์บังคับโหมดเย็น เพราะ climate.turn_on จะคืนค่าโหมดที่เครื่องจำไว้ — และโหมด “ทำความร้อน” ตอนหกโมงเช้าในกรุงเทพฯ ไม่ใช่สิ่งที่ใครขอ แก้ entity_map และตัวเลือกห้องให้ตรงกับบ้านของคุณ
Settings → Automations & scenes → Scripts → new → Edit in YAML
alias: Schedule a device action (at a time or after a delay)
icon: mdi:calendar-clock
mode: queued
description: >-
  Schedules a device action to happen later — either AT a clock time or IN a number of minutes. Use this
  for ANY request of the form 'open/close/turn on/turn off <device> at <time>' or '... in <N> minutes/hours'
  (e.g. 'open the curtains at 6 a.m. tomorrow', 'turn off the bedroom lights in 20 minutes', 'ปิดแอร์อีกครึ่งชั่วโมง').
  Do NOT use the timer tool for these — a timer only chimes, it cannot perform a device action. Convert
  spoken times to 24-hour HH:MM ('six a.m.' → '06:00', 'half past nine tonight' → '21:30'); if the time
  has already passed today it is scheduled for tomorrow automatically. Convert durations to minutes ('an
  hour' → 60). Pass exactly one of at_time or in_minutes. Returns a 'message' field in the language you
  pass as 'language' — speak that message back to the user verbatim; it states the VERIFIED time the action
  will run. If the message says nothing was scheduled, tell the user that.
fields:
  action:
    name: Action
    description: 'What to do: ''open'' or ''close'' (curtains), ''turn_on'' or ''turn_off'' (lights, aircon).'
    required: true
    example: open
    selector:
      select:
        options:
        - open
        - close
        - turn_on
        - turn_off
  device:
    name: Device
    description: 'Which device class: ''curtains'', ''lights'' or ''aircon''.'
    required: true
    example: curtains
    selector:
      select:
        options:
        - curtains
        - lights
        - aircon
  room:
    name: Room
    description: >-
      The room. If the user names a room, pass it exactly as one of the options. If they don't, pass the
      room the user is speaking from.
    required: true
    example: Living Room
    selector:
      select:
        options:
        - Living Room
        - Bedroom
        - Kid's Room
        - Cooking Room
        - Shower Room
        - Bath Room
  at_time:
    name: At time
    description: >-
      Clock time to run the action, 24-hour HH:MM (e.g. '06:00', '21:30'). Use this OR in_minutes, not
      both.
    required: false
    example: 06:00
    selector:
      text: null
  in_minutes:
    name: In minutes
    description: Delay in whole minutes from now (e.g. 20, 90). Use this OR at_time, not both.
    required: false
    example: 20
    selector:
      number:
        min: 1
        max: 1440
        mode: box
  language:
    name: Language
    description: >-
      The language the user spoke in: 'th' Thai, 'zh' Chinese, 'en' English. The message is returned in
      this language. Defaults to 'en'.
    required: false
    example: th
    selector:
      select:
        options:
        - en
        - th
        - zh
sequence:
- alias: Resolve inputs
  variables:
    lang: '{{ language | default(''en'') }}'
    act: '{{ action | default('''') }}'
    dev: '{{ device | default('''') }}'
    rm: '{{ room | default('''') }}'
    entity_map:
      curtains:
        Living Room: cover.living_room_curtains
      lights:
        Living Room: light.living_room
        Bedroom: light.bedroom
        Kid's Room: light.kids_room
        Cooking Room: light.cooking_room
        Shower Room: light.shower_room
        Bath Room: light.bath_room
      aircon:
        Living Room: climate.living_room
        Bedroom: climate.bedroom
        Kid's Room: climate.kids_room
    target_entity: '{{ entity_map.get(dev, {}).get(rm, '''') }}'
    svc: >-
      {% if dev == 'curtains' and act == 'open' %}cover.open_cover{% elif dev == 'curtains' and act ==
      'close' %}cover.close_cover{% elif dev == 'lights' and act in ['turn_on','open'] %}light.turn_on{%
      elif dev == 'lights' and act in ['turn_off','close'] %}light.turn_off{% elif dev == 'aircon' and
      act in ['turn_on','open'] %}climate.set_hvac_mode{% elif dev == 'aircon' and act in ['turn_off','close']
      %}climate.set_hvac_mode{% else %}{% endif %}
    svc_data: >-
      {% if dev == 'aircon' and act in ['turn_on','open'] %}{{ {'hvac_mode': 'cool'} }}{% elif dev ==
      'aircon' and act in ['turn_off','close'] %}{{ {'hvac_mode': 'off'} }}{% else %}{{ {} }}{% endif
      %}
    has_delay: '{{ in_minutes is defined and in_minutes is not none and (in_minutes | int(0)) > 0 }}'
    has_time: >-
      {{ at_time is defined and at_time is not none and (at_time | string) is match('^([01]?\d|2[0-3]):[0-5]\d$')
      }}
    fire_at: >-
      {% set n = now() %}{% if in_minutes is defined and in_minutes is not none and (in_minutes | int(0))
      > 0 %}{{ (n + timedelta(minutes=(in_minutes | int))).isoformat() }}{% elif at_time is defined and
      at_time is not none and (at_time | string) is match('^([01]?\d|2[0-3]):[0-5]\d$') %}{% set hh =
      (at_time | string).split(':')[0] | int %}{% set mm = (at_time | string).split(':')[1] | int %}{%
      set t = n.replace(hour=hh, minute=mm, second=0, microsecond=0) %}{% if t <= n %}{% set t = t + timedelta(days=1)
      %}{% endif %}{{ t.isoformat() }}{% else %}{{ '' }}{% endif %}
- alias: Validate
  choose:
  - conditions:
    - condition: template
      value_template: '{{ target_entity == '''' or svc == '''' or fire_at == '''' }}'
    sequence:
    - variables:
        message: >-
          {% if lang == 'th' %}ขอโทษค่ะ ตั้งเวลาไม่ได้ ไม่ได้ตั้งอะไรไว้เลย{% elif lang == 'zh' %}抱歉,无法设置定时,什么也没有安排。{%
          else %}Sorry — I couldn't set that schedule. Nothing was scheduled.{% endif %}
        reason: target={{ target_entity }} svc={{ svc }} fire_at={{ fire_at }}
    - variables:
        result_vars:
          message: '{{ message }}'
          scheduled: false
          reason: '{{ reason }}'
    - stop: invalid request
      response_variable: result_vars
- alias: Compute names and times
  variables:
    fire_dt: '{{ as_datetime(fire_at) }}'
    fire_date: '{{ as_datetime(fire_at).strftime(''%Y-%m-%d'') }}'
    fire_hhmm: '{{ as_datetime(fire_at).strftime(''%H:%M'') }}'
    is_tomorrow: '{{ as_datetime(fire_at).date() != now().date() }}'
    sched_name: >-
      Voice: {{ act }} {{ rm }} {{ dev }} @ {{ as_datetime(fire_at).strftime('%d/%m %H:%M') }} [{{ now().strftime('%H%M%S')
      }}]
- alias: Create the schedule
  action: scheduler.add
  continue_on_error: true
  data:
    name: '{{ sched_name }}'
    weekdays:
    - daily
    start_date: '{{ fire_date }}'
    end_date: '{{ fire_date }}'
    repeat_type: single
    timeslots:
    - start: '{{ fire_hhmm }}'
      actions:
      - service: '{{ svc }}'
        entity_id: '{{ target_entity }}'
        service_data: '{{ svc_data }}'
- delay:
    seconds: 2
- alias: Verify the schedule entity exists
  variables:
    sched_entity: >-
      {{ states.switch | selectattr('attributes.friendly_name', 'eq', 'Scheduler ' ~ sched_name) | map(attribute='entity_id')
      | list | first | default('') }}
    sched_ok: >-
      {{ sched_entity != '' and is_state(sched_entity, 'on') and state_attr(sched_entity, 'next_trigger')
      is not none }}
- alias: Build the spoken message
  variables:
    room_th: >-
      {{ {'Living Room': 'ห้องนั่งเล่น', 'Bedroom': 'ห้องนอน', "Kid's Room": 'ห้องเด็ก', 'Cooking Room':
      'ห้องครัว', 'Shower Room': 'ห้องอาบน้ำ', 'Bath Room': 'ห้องน้ำ'}.get(rm, rm) }}
    room_zh: >-
      {{ {'Living Room': '客厅', 'Bedroom': '卧室', "Kid's Room": '孩子的房间', 'Cooking Room': '厨房', 'Shower Room':
      '淋浴间', 'Bath Room': '浴室'}.get(rm, rm) }}
    dev_en: '{{ {''curtains'': ''curtains'', ''lights'': ''lights'', ''aircon'': ''aircon''}.get(dev,
      dev) }}'
    dev_th: '{{ {''curtains'': ''ม่าน'', ''lights'': ''ไฟ'', ''aircon'': ''แอร์''}.get(dev, dev) }}'
    dev_zh: '{{ {''curtains'': ''窗帘'', ''lights'': ''灯'', ''aircon'': ''空调''}.get(dev, dev) }}'
    verb_en: >-
      {{ {'open': 'open', 'close': 'close', 'turn_on': 'turn on', 'turn_off': 'turn off'}.get(act, act)
      }}
    verb_th: '{{ {''open'': ''เปิด'', ''close'': ''ปิด'', ''turn_on'': ''เปิด'', ''turn_off'': ''ปิด''}.get(act,
      act) }}'
    verb_zh: '{{ {''open'': ''打开'', ''close'': ''关闭'', ''turn_on'': ''打开'', ''turn_off'': ''关闭''}.get(act,
      act) }}'
    when_en: >-
      {% if has_delay %}in {{ in_minutes | int }} minutes, at {{ fire_hhmm }}{% else %}at {{ fire_hhmm
      }}{% if is_tomorrow %} tomorrow{% endif %}{% endif %}
    when_th: >-
      {% if has_delay %}อีก {{ in_minutes | int }} นาที เวลา {{ fire_hhmm }} น.{% else %}เวลา {{ fire_hhmm
      }} น.{% if is_tomorrow %} พรุ่งนี้{% endif %}{% endif %}
    when_zh: >-
      {% if has_delay %}{{ in_minutes | int }}分钟后,{{ fire_hhmm }}{% else %}{% if is_tomorrow %}明天{% endif
      %}{{ fire_hhmm }}{% endif %}
    message: >-
      {% if sched_ok %}{% if lang == 'th' %}จะ{{ verb_th }}{{ dev_th }}{{ room_th }}{{ when_th }}ค่ะ{%
      elif lang == 'zh' %}将于{{ when_zh }}{{ verb_zh }}{{ room_zh }}{{ dev_zh }}。{% else %}I'll {{ verb_en
      }} the {{ rm | lower }} {{ dev_en }} {{ when_en }}.{% endif %}{% else %}{% if lang == 'th' %}ขอโทษค่ะ
      ตั้งเวลาไม่ได้ ไม่ได้ตั้งอะไรไว้เลย{% elif lang == 'zh' %}抱歉,无法设置定时,什么也没有安排。{% else %}Sorry — I
      couldn't set that schedule. Nothing was scheduled.{% endif %}{% endif %}
    result_vars:
      message: '{{ message }}'
      scheduled: '{{ sched_ok }}'
      schedule_entity: '{{ sched_entity }}'
      fires_at: '{{ fire_at }}'
      target: '{{ target_entity }}'
      service: '{{ svc }}'
- alias: Return
  stop: done
  response_variable: result_vars

สคริปต์ที่ 2 — มีอะไรค้างอยู่ และอีกนานเท่าไร

อ่านเอนทิตีสวิตช์ของ Scheduler กลับมา — ตัวที่สคริปต์ที่ 1 ตั้งชื่อว่า Voice: … — แล้วรายงานเรียงตามเวลาทำงาน พร้อม “อีก 7 ชั่วโมง 28 นาที” ที่ตรวจสอบแล้วในแต่ละรายการ โมเดลจึงไม่ต้องคิดเลขเอง
Script → Edit in YAML
alias: List scheduled actions
icon: mdi:calendar-search
mode: queued
description: >-
  Reports pending scheduled device actions (set with schedule_action: 'at <time>' or 'in <N> minutes').
  Use this whenever the user asks what is scheduled, what timers/schedules are set, WHEN something will
  happen, or HOW LONG UNTIL something happens ('when will the curtains open', 'how long until the aircon
  turns off', 'อีกนานแค่ไหนม่านจะเปิด', '还有多久窗帘会打开'). Do NOT use the built-in timer status — it only knows
  chime timers. If the user names a device or room, pass it as a filter so the answer is about that device
  only. ALWAYS call this tool before answering, even if you answered a similar question earlier in this
  conversation — schedules change and only this tool knows the current state. Returns 'message' in the
  language you pass as 'language'; speak it verbatim. Each item carries 'relative' (verified time remaining,
  e.g. 'in 7 hours 28 minutes') — read it out for 'how long until' questions, never calculate it yourself.
  If the message says nothing is scheduled for the device, say exactly that.
fields:
  language:
    name: Language
    description: 'The language the user spoke in: ''th'' Thai, ''zh'' Chinese, ''en'' English. Defaults
      to ''en''.'
    required: false
    example: th
    selector:
      select:
        options:
        - en
        - th
        - zh
  device:
    name: Device
    description: >-
      Optional filter: 'curtains', 'lights' or 'aircon'. Pass it when the user asks about a specific device.
    required: false
    example: curtains
    selector:
      select:
        options:
        - curtains
        - lights
        - aircon
  room:
    name: Room
    description: 'Optional filter: the room, exactly as one of the options.'
    required: false
    example: Living Room
    selector:
      select:
        options:
        - Living Room
        - Bedroom
        - Kid's Room
        - Cooking Room
        - Shower Room
        - Bath Room
sequence:
- variables:
    lang: '{{ language | default(''en'') }}'
    dev: '{{ device | default('''') }}'
    rm: '{{ room | default('''') }}'
    items: >-
      {% set ns = namespace(rows=[]) %}{% for s in states.switch if s.entity_id.startswith('switch.schedule_voice')
      and s.state == 'on' and s.attributes.next_trigger is not none %}{% set fn = s.attributes.friendly_name
      | replace('Scheduler Voice: ', '') %}{% set parts = fn.split(' @ ')[0].split(' ') %}{% if (not dev
      or dev == parts[-1]) and (not rm or rm == (parts[1:-1] | join(' '))) %}{% set ns.rows = ns.rows
      + [{'entity': s.entity_id, 'action': parts[0], 'room': parts[1:-1] | join(' '), 'device': parts[-1],
      'at': s.attributes.next_trigger | string}] %}{% endif %}{% endfor %}{{ ns.rows | sort(attribute='at')
      }}
    count: '{{ items | count }}'
- variables:
    room_th:
      Living Room: ห้องนั่งเล่น
      Bedroom: ห้องนอน
      Kid's Room: ห้องเด็ก
      Cooking Room: ห้องครัว
      Shower Room: ห้องอาบน้ำ
      Bath Room: ห้องน้ำ
    room_zh:
      Living Room: 客厅
      Bedroom: 卧室
      Kid's Room: 孩子的房间
      Cooking Room: 厨房
      Shower Room: 淋浴间
      Bath Room: 浴室
    dev_th:
      curtains: ม่าน
      lights: ไฟ
      aircon: แอร์
    dev_zh:
      curtains: 窗帘
      lights: 灯
      aircon: 空调
    verb_en:
      open: open
      close: close
      turn_on: turn on
      turn_off: turn off
    verb_th:
      open: เปิด
      close: ปิด
      turn_on: เปิด
      turn_off: ปิด
    verb_zh:
      open: 打开
      close: 关闭
      turn_on: 打开
      turn_off: 关闭
- variables:
    dev_lbl_en: >-
      {% if dev and rm %}the {{ rm | lower }} {{ dev }}{% elif dev %}the {{ dev }}{% elif rm %}the {{
      rm | lower }}{% else %}{% endif %}
    dev_lbl_th: >-
      {% set d = {'curtains': 'ม่าน', 'lights': 'ไฟ', 'aircon': 'แอร์'}.get(dev, '') %}{% set r = {'Living
      Room': 'ห้องนั่งเล่น', 'Bedroom': 'ห้องนอน', "Kid's Room": 'ห้องเด็ก', 'Cooking Room': 'ห้องครัว',
      'Shower Room': 'ห้องอาบน้ำ', 'Bath Room': 'ห้องน้ำ'}.get(rm, '') %}{{ d ~ r }}
    dev_lbl_zh: >-
      {% set d = {'curtains': '窗帘', 'lights': '灯', 'aircon': '空调'}.get(dev, '') %}{% set r = {'Living
      Room': '客厅', 'Bedroom': '卧室', "Kid's Room": '孩子的房间', 'Cooking Room': '厨房', 'Shower Room': '淋浴间',
      'Bath Room': '浴室'}.get(rm, '') %}{{ r ~ d }}
    message: >-
      {% if count | int == 0 %}{% if lang == 'th' %}{% if dev_lbl_th %}ไม่มีรายการตั้งเวลาสำหรับ{{ dev_lbl_th
      }}ค่ะ{% else %}ไม่มีรายการตั้งเวลาค่ะ{% endif %}{% elif lang == 'zh' %}{% if dev_lbl_zh %}{{ dev_lbl_zh
      }}没有任何定时安排。{% else %}没有任何定时安排。{% endif %}{% else %}{% if dev_lbl_en %}Nothing is scheduled for {{
      dev_lbl_en }}.{% else %}Nothing is scheduled.{% endif %}{% endif %}{% else %}{% set ns = namespace(txt=[])
      %}{% for r in items %}{% set secs = ((as_datetime(r.at) - now()).total_seconds()) | int %}{% set
      h = secs // 3600 %}{% set m = (secs % 3600) // 60 %}{% set rel = ('in ' ~ (h ~ ' hour' ~ ('s' if
      h != 1 else '') ~ ' ' if h > 0 else '') ~ (m ~ ' minute' ~ ('s' if m != 1 else '') if (m > 0 or
      h == 0) else '')) | trim %}{% set rel_th = ('อีก ' ~ (h ~ ' ชั่วโมง ' if h > 0 else '') ~ (m ~ '
      นาที' if (m > 0 or h == 0) else '')) | trim %}{% set rel_zh = ((h ~ '小时' if h > 0 else '') ~ (m
      ~ '分钟' if (m > 0 or h == 0) else '') ~ '后') %}{% set day = '' if as_datetime(r.at).date() == now().date()
      else (' พรุ่งนี้' if lang == 'th' else ('明天' if lang == 'zh' else ' tomorrow')) %}{% if lang ==
      'th' %}{% set ns.txt = ns.txt + [verb_th.get(r.action, r.action) ~ dev_th.get(r.device, r.device)
      ~ room_th.get(r.room, r.room) ~ ' เวลา ' ~ as_datetime(r.at).strftime('%H:%M') ~ ' น.' ~ day ~ '
      (' ~ rel_th ~ ')'] %}{% elif lang == 'zh' %}{% set ns.txt = ns.txt + [day ~ as_datetime(r.at).strftime('%H:%M')
      ~ verb_zh.get(r.action, r.action) ~ room_zh.get(r.room, r.room) ~ dev_zh.get(r.device, r.device)
      ~ '(' ~ rel_zh ~ ')'] %}{% else %}{% set ns.txt = ns.txt + [verb_en.get(r.action, r.action) ~ '
      the ' ~ (r.room | lower) ~ ' ' ~ r.device ~ ' at ' ~ as_datetime(r.at).strftime('%H:%M') ~ day ~
      ' (' ~ rel ~ ')'] %}{% endif %}{% endfor %}{% if lang == 'th' %}มี {{ count }} รายการค่ะ: {{ ns.txt
      | join(', ') }}{% elif lang == 'zh' %}共有{{ count }}项安排:{{ ns.txt | join(';') }}。{% else %}{{ count
      }} scheduled: {{ ns.txt | join('; ') }}.{% endif %}{% endif %}
    result_vars:
      message: '{{ message }}'
      count: '{{ count }}'
      items: >-
        {% set ns = namespace(l=[]) %}{% for r in items %}{% set secs = ((as_datetime(r.at) - now()).total_seconds())
        | int %}{% set h = secs // 3600 %}{% set m = (secs % 3600) // 60 %}{% set rel = ('in ' ~ (h ~
        ' hour' ~ ('s' if h != 1 else '') ~ ' ' if h > 0 else '') ~ (m ~ ' minute' ~ ('s' if m != 1 else
        '') if (m > 0 or h == 0) else '')) | trim %}{% set ns.l = ns.l + [{'entity': r.entity, 'action':
        r.action, 'room': r.room, 'device': r.device, 'at': r.at, 'relative': rel, 'minutes_until': secs
        // 60}] %}{% endfor %}{{ ns.l }}
- stop: done
  response_variable: result_vars

สคริปต์ที่ 3 — ยกเลิก

กรองตามอุปกรณ์และห้องถ้าระบุมา ลบรายการที่ตรงกัน รอหนึ่งวินาที แล้วนับสิ่งที่ยังอยู่ก่อนเลือกประโยคที่จะพูด
Script → Edit in YAML
alias: Cancel scheduled actions
icon: mdi:calendar-remove
mode: queued
description: >-
  Cancels pending scheduled device actions that were set with schedule_action. Use this for ANY request
  to undo, stop or prevent a scheduled action, however it is phrased: 'cancel the curtain schedule', 'don't
  open the curtains at six', 'never mind the aircon later', 'stop the lights turning off', 'the curtains
  have been cancelled' (treat a statement that something is cancelled as a request to cancel it), 'ยกเลิกที่ตั้งเวลาไว้',
  'อย่าเปิดม่านตอนหกโมง', '取消窗帘的定时'. Never reply that something is cancelled or will not happen without
  calling this tool — only its message is the truth. Filter by device and/or room if the user names them;
  pass nothing to cancel ALL scheduled actions (confirm first if more than one would be cancelled and
  they didn't say 'all'). Returns a 'message' field in the language you pass as 'language' — speak it
  back verbatim.
fields:
  device:
    name: Device
    description: 'Optional filter: ''curtains'', ''lights'' or ''aircon''.'
    required: false
    example: curtains
    selector:
      select:
        options:
        - curtains
        - lights
        - aircon
  room:
    name: Room
    description: 'Optional filter: the room, exactly as one of the options.'
    required: false
    example: Living Room
    selector:
      select:
        options:
        - Living Room
        - Bedroom
        - Kid's Room
        - Cooking Room
        - Shower Room
        - Bath Room
  language:
    name: Language
    description: 'The language the user spoke in: ''th'', ''zh'' or ''en''. Defaults to ''en''.'
    required: false
    example: th
    selector:
      select:
        options:
        - en
        - th
        - zh
sequence:
- variables:
    lang: '{{ language | default(''en'') }}'
    dev: '{{ device | default('''') }}'
    rm: '{{ room | default('''') }}'
    targets: >-
      {% set ns = namespace(l=[]) %}{% for s in states.switch if s.entity_id.startswith('switch.schedule_voice')
      %}{% set fn = s.attributes.friendly_name | replace('Scheduler Voice: ', '') %}{% set parts = fn.split('
      @ ')[0].split(' ') %}{% set r_room = parts[1:-1] | join(' ') %}{% set r_dev = parts[-1] %}{% if
      (dev == '' or dev == r_dev) and (rm == '' or rm == r_room) %}{% set ns.l = ns.l + [s.entity_id]
      %}{% endif %}{% endfor %}{{ ns.l }}
    n: '{{ targets | count }}'
- repeat:
    for_each: '{{ targets }}'
    sequence:
    - action: scheduler.remove
      continue_on_error: true
      data:
        entity_id: '{{ repeat.item }}'
- delay:
    seconds: 1
- variables:
    left: '{{ states.switch | selectattr(''entity_id'', ''in'', targets) | list | count }}'
    message: >-
      {% if n | int == 0 %}{% if lang == 'th' %}ไม่มีรายการตั้งเวลาที่ตรงกันค่ะ{% elif lang == 'zh' %}没有匹配的定时安排。{%
      else %}There was nothing scheduled that matches.{% endif %}{% elif left | int == 0 %}{% if lang
      == 'th' %}ยกเลิกแล้ว {{ n }} รายการค่ะ{% elif lang == 'zh' %}已取消{{ n }}项安排。{% else %}Cancelled {{
      n }} scheduled action{{ 's' if n | int > 1 else '' }}.{% endif %}{% else %}{% if lang == 'th' %}ขอโทษค่ะ
      ยกเลิกไม่สำเร็จ ยัง{{ left }}รายการค้างอยู่{% elif lang == 'zh' %}抱歉,取消失败,仍有{{ left }}项未取消。{% else
      %}Sorry — {{ left }} of {{ n }} could not be cancelled and are still scheduled.{% endif %}{% endif
      %}
    result_vars:
      message: '{{ message }}'
      cancelled: '{{ (n | int) - (left | int) }}'
      remaining: '{{ left }}'
- stop: done
  response_variable: result_vars

เปิดให้ใช้ แล้วลองดู

บันทึกจากการใช้งานทุกวัน

ส่วนหนึ่งของภาพที่ใหญ่กว่า สคริปต์เหล่านี้เป็นเครื่องมือหนึ่งในหลายตัวของผู้ช่วยเสียงที่พูดได้ทุกภาษาของครอบครัว และกฎตรวจก่อนอ้างว่าสำเร็จแบบเดียวกันก็อยู่ในสายการเล่นหนังและเส้นทางเพลงด้วย