Each blueprint now has its own folder containing: - blueprint.yaml: The automation code with a short header - README.md: Detailed documentation extracted from headers Updated CLAUDE.md with repository structure guidelines. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
405 lines
16 KiB
YAML
405 lines
16 KiB
YAML
# Telegram Keyboard Action Blueprint
|
|
# Creates interactive Telegram messages with inline keyboard buttons and
|
|
# handles button callbacks. See README.md for detailed documentation.
|
|
#
|
|
# Author: Alexei Dolgolyov (dolgolyov.alexei@gmail.com)
|
|
|
|
blueprint:
|
|
name: "Custom: Telegram Keyboard Action"
|
|
description: >
|
|
Sends a Telegram message with inline keyboard buttons to multiple chat IDs.
|
|
When a button is pressed, executes the corresponding callback action.
|
|
Supports up to 4 buttons with individual callbacks.
|
|
domain: automation
|
|
|
|
input:
|
|
# -------------------------------------------------------------------------
|
|
# Chat Configuration
|
|
# -------------------------------------------------------------------------
|
|
chat_group:
|
|
name: "Chats"
|
|
collapsed: false
|
|
input:
|
|
chat_ids:
|
|
name: Telegram Chat IDs
|
|
description: >
|
|
List of numeric chat IDs to send messages to.
|
|
You can find your chat ID by messaging @userinfobot on Telegram.
|
|
default: []
|
|
selector:
|
|
text:
|
|
multiple: true
|
|
|
|
chat_entities:
|
|
name: Telegram Notification Targets
|
|
description: >
|
|
Notify entities with chat ID in friendly name.
|
|
Format: "<Name> (<ChatID>)" - e.g., "Alex (2132562465)"
|
|
The number in parentheses will be extracted as chat ID.
|
|
default: []
|
|
selector:
|
|
entity:
|
|
domain: notify
|
|
multiple: true
|
|
|
|
config_entry_id:
|
|
name: Telegram Bot Config Entry ID
|
|
description: >
|
|
Required if you have multiple Telegram bots configured.
|
|
Find this in Home Assistant: Settings → Devices & Services → Telegram Bot
|
|
→ Click on your bot → look at the URL, the ID is after /config_entry/
|
|
Example: "01JKXXXXXXXXXXXXXXXXXXX"
|
|
Leave empty if you only have one Telegram bot.
|
|
default: ""
|
|
selector:
|
|
text:
|
|
|
|
# -------------------------------------------------------------------------
|
|
# Message Configuration
|
|
# -------------------------------------------------------------------------
|
|
message_group:
|
|
name: "Message"
|
|
collapsed: false
|
|
input:
|
|
keyboard_id:
|
|
name: Keyboard ID
|
|
description: >
|
|
Unique identifier for this keyboard. Used to distinguish
|
|
button presses from different blueprints/automations.
|
|
default: "keyboard"
|
|
selector:
|
|
text:
|
|
|
|
message_text:
|
|
name: Message Text
|
|
description: The message displayed above the keyboard buttons
|
|
default: "Hey, here's a new message"
|
|
selector:
|
|
text:
|
|
multiline: true
|
|
|
|
buttons:
|
|
name: Button Labels
|
|
description: >
|
|
Text displayed on each button (up to 4 buttons).
|
|
Buttons appear in a single row.
|
|
default:
|
|
- "✔"
|
|
- "✖"
|
|
selector:
|
|
text:
|
|
multiple: true
|
|
|
|
answers:
|
|
name: Answer Messages (optional)
|
|
description: >
|
|
Reply message for each button (same order as buttons).
|
|
Leave empty to not send any reply when button is pressed.
|
|
default: []
|
|
selector:
|
|
text:
|
|
multiple: true
|
|
|
|
hide_keyboard_on_press:
|
|
name: Hide Keyboard After Press
|
|
description: Remove the keyboard buttons after any button is pressed
|
|
default: true
|
|
selector:
|
|
boolean:
|
|
|
|
hide_message_on_press:
|
|
name: Delete Message After Press
|
|
description: Delete the entire message after any button is pressed
|
|
default: false
|
|
selector:
|
|
boolean:
|
|
|
|
# -------------------------------------------------------------------------
|
|
# Button Callbacks
|
|
# -------------------------------------------------------------------------
|
|
callbacks_group:
|
|
name: "Callbacks"
|
|
collapsed: false
|
|
input:
|
|
button_1_callback:
|
|
name: Button 1 Callback
|
|
description: Actions to run when first button is pressed
|
|
default: []
|
|
selector:
|
|
action: {}
|
|
|
|
button_2_callback:
|
|
name: Button 2 Callback
|
|
description: Actions to run when second button is pressed
|
|
default: []
|
|
selector:
|
|
action: {}
|
|
|
|
button_3_callback:
|
|
name: Button 3 Callback
|
|
description: Actions to run when third button is pressed
|
|
default: []
|
|
selector:
|
|
action: {}
|
|
|
|
button_4_callback:
|
|
name: Button 4 Callback
|
|
description: Actions to run when fourth button is pressed
|
|
default: []
|
|
selector:
|
|
action: {}
|
|
|
|
# -------------------------------------------------------------------------
|
|
# Debug
|
|
# -------------------------------------------------------------------------
|
|
debug_group:
|
|
name: "Debug"
|
|
collapsed: true
|
|
input:
|
|
enable_debug:
|
|
name: Enable Debug Notifications
|
|
description: >
|
|
Send persistent notifications for debugging automation behavior.
|
|
Shows resolved chat IDs, callback data, and configuration.
|
|
default: false
|
|
selector:
|
|
boolean:
|
|
|
|
# Parallel mode allows multiple button presses to be processed simultaneously
|
|
mode: parallel
|
|
|
|
# =============================================================================
|
|
# Trigger
|
|
# =============================================================================
|
|
trigger:
|
|
# Listen for Telegram callback events (button presses)
|
|
- platform: event
|
|
event_type: telegram_callback
|
|
id: "telegram_callback"
|
|
|
|
# =============================================================================
|
|
# Variables
|
|
# =============================================================================
|
|
variables:
|
|
# Input references
|
|
buttons: !input buttons
|
|
keyboard_id: !input keyboard_id
|
|
hide_keyboard_on_press: !input hide_keyboard_on_press
|
|
hide_message_on_press: !input hide_message_on_press
|
|
answers: !input answers
|
|
config_entry_id: !input config_entry_id
|
|
|
|
# Callback references (needed for condition checks)
|
|
button_1_callback: !input button_1_callback
|
|
button_2_callback: !input button_2_callback
|
|
button_3_callback: !input button_3_callback
|
|
button_4_callback: !input button_4_callback
|
|
|
|
# Determine trigger type
|
|
# When automation is called via service (action), trigger.platform is undefined
|
|
is_manual_trigger: "{{ trigger.platform is not defined or trigger.platform is none }}"
|
|
|
|
# Check if this callback event belongs to our keyboard
|
|
# Callback data format: /<keyboard_id>_<index>
|
|
is_our_callback: >
|
|
{% if is_manual_trigger %}
|
|
false
|
|
{% else %}
|
|
{% set callback_data = trigger.event.data.data | default('') %}
|
|
{{ callback_data.startswith('/' ~ keyboard_id ~ '_') }}
|
|
{% endif %}
|
|
|
|
# Debug flag
|
|
is_debug: !input enable_debug
|
|
|
|
# =============================================================================
|
|
# Actions
|
|
# =============================================================================
|
|
action:
|
|
# ---------------------------------------------------------------------------
|
|
# Debug Logging - Callback Events
|
|
# ---------------------------------------------------------------------------
|
|
- choose:
|
|
- conditions:
|
|
- condition: template
|
|
value_template: "{{ is_debug and not is_manual_trigger }}"
|
|
sequence:
|
|
- service: persistent_notification.create
|
|
data:
|
|
title: "Telegram Keyboard Debug - Callback"
|
|
message: >
|
|
**Trigger Info:**
|
|
- Platform: {{ trigger.platform | default('unknown') }}
|
|
- Is our callback: {{ is_our_callback }}
|
|
|
|
{% if is_our_callback %}
|
|
**Callback Data:**
|
|
- Raw data: {{ trigger.event.data.data | default('N/A') }}
|
|
- Chat ID: {{ trigger.event.data.chat_id | default('N/A') }}
|
|
- Message ID: {{ trigger.event.data.message.message_id | default('N/A') }}
|
|
- Keyboard ID: {{ keyboard_id }}
|
|
{% else %}
|
|
**Ignored** (callback not for this keyboard)
|
|
- Expected prefix: /{{ keyboard_id }}_
|
|
- Received: {{ trigger.event.data.data | default('N/A') }}
|
|
{% endif %}
|
|
|
|
**Config:**
|
|
- Config Entry ID: {{ config_entry_id if config_entry_id | length > 0 else '(not set)' }}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Handle Button Press (Callback Event)
|
|
# ---------------------------------------------------------------------------
|
|
- choose:
|
|
- conditions:
|
|
- condition: template
|
|
value_template: "{{ is_our_callback }}"
|
|
sequence:
|
|
- variables:
|
|
# Extract data from the callback event
|
|
callback_data: "{{ trigger.event.data.data }}"
|
|
chat_id: "{{ trigger.event.data.chat_id }}"
|
|
message_id: "{{ trigger.event.data.message.message_id }}"
|
|
# Extract button index from callback data (format: /<keyboard_id>_<index>)
|
|
# This handles multi-digit indices correctly
|
|
button_index: >
|
|
{% set prefix = '/' ~ keyboard_id ~ '_' %}
|
|
{% set idx_str = callback_data[prefix | length:] %}
|
|
{{ idx_str | int(-1) }}
|
|
|
|
# Execute the appropriate button callback
|
|
- choose:
|
|
- conditions: "{{ button_index == 0 and button_1_callback | length > 0 }}"
|
|
sequence: !input button_1_callback
|
|
- conditions: "{{ button_index == 1 and button_2_callback | length > 0 }}"
|
|
sequence: !input button_2_callback
|
|
- conditions: "{{ button_index == 2 and button_3_callback | length > 0 }}"
|
|
sequence: !input button_3_callback
|
|
- conditions: "{{ button_index == 3 and button_4_callback | length > 0 }}"
|
|
sequence: !input button_4_callback
|
|
|
|
# Send answer message if configured for this button
|
|
- choose:
|
|
- conditions: >
|
|
{{ button_index >= 0 and button_index < (answers | length)
|
|
and (answers[button_index] | length) > 0 }}
|
|
sequence:
|
|
- service: telegram_bot.send_message
|
|
data:
|
|
target: "{{ chat_id }}"
|
|
message: "{{ answers[button_index] }}"
|
|
# Reply to original message unless we're deleting it
|
|
reply_to_message_id: >
|
|
{{ omit if hide_message_on_press else message_id }}
|
|
config_entry_id: >
|
|
{{ config_entry_id if config_entry_id | length > 0 else omit }}
|
|
|
|
# Handle message/keyboard cleanup
|
|
- choose:
|
|
# Delete entire message
|
|
- conditions: "{{ hide_message_on_press }}"
|
|
sequence:
|
|
- service: telegram_bot.delete_message
|
|
data:
|
|
chat_id: "{{ chat_id }}"
|
|
message_id: "{{ message_id }}"
|
|
config_entry_id: >
|
|
{{ config_entry_id if config_entry_id | length > 0 else omit }}
|
|
|
|
# Just remove keyboard (keep message)
|
|
- conditions: "{{ hide_keyboard_on_press }}"
|
|
sequence:
|
|
- service: telegram_bot.edit_replymarkup
|
|
data:
|
|
chat_id: "{{ chat_id }}"
|
|
message_id: "{{ message_id }}"
|
|
inline_keyboard: []
|
|
config_entry_id: >
|
|
{{ config_entry_id if config_entry_id | length > 0 else omit }}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Send Initial Message (Manual Trigger / Service Call)
|
|
# ---------------------------------------------------------------------------
|
|
- choose:
|
|
- conditions:
|
|
- condition: template
|
|
value_template: "{{ is_manual_trigger }}"
|
|
sequence:
|
|
- variables:
|
|
# Get chat IDs from direct input
|
|
chat_ids: !input chat_ids
|
|
|
|
# Extract chat IDs from notify entity friendly names
|
|
# Format: "Name (123456789)" or "Name (-123456789)" -> extracts the number
|
|
# Supports both positive (user) and negative (group/channel) chat IDs
|
|
chat_entities: !input chat_entities
|
|
chat_ids_from_entities: >
|
|
{% set ns = namespace(ids=[]) %}
|
|
{% for entity in chat_entities %}
|
|
{% set friendly_name = state_attr(entity, 'friendly_name') | default('') %}
|
|
{% set match = friendly_name | regex_findall('\\((-?\\d+)\\)') %}
|
|
{% if match | length > 0 %}
|
|
{% set ns.ids = ns.ids + [match[0]] %}
|
|
{% endif %}
|
|
{% endfor %}
|
|
{{ ns.ids }}
|
|
|
|
# Combine all chat IDs
|
|
result_chat_ids: "{{ (chat_ids + chat_ids_from_entities) | unique | list }}"
|
|
|
|
# Build inline keyboard with callback data
|
|
# Format: ["Button Text:/<keyboard_id>_<index>", ...]
|
|
message_text: !input message_text
|
|
inline_keyboard: >
|
|
{% set ns = namespace(buttons=[]) %}
|
|
{% for i in range(buttons | length) %}
|
|
{% set callback_data = '/' ~ keyboard_id ~ '_' ~ i %}
|
|
{% set ns.buttons = ns.buttons + [(buttons[i] ~ ':' ~ callback_data)] %}
|
|
{% endfor %}
|
|
{{ ns.buttons }}
|
|
|
|
# Debug logging for manual trigger
|
|
- choose:
|
|
- conditions:
|
|
- condition: template
|
|
value_template: "{{ is_debug }}"
|
|
sequence:
|
|
- service: persistent_notification.create
|
|
data:
|
|
title: "Telegram Keyboard Debug - Send Message"
|
|
message: >
|
|
**Chat ID Resolution:**
|
|
- Direct chat IDs: {{ chat_ids | join(', ') if chat_ids | length > 0 else '(none)' }}
|
|
- From entities: {{ chat_ids_from_entities | join(', ') if chat_ids_from_entities | length > 0 else '(none)' }}
|
|
- **Resolved IDs: {{ result_chat_ids | join(', ') if result_chat_ids | length > 0 else '(none)' }}**
|
|
|
|
**Entity Parsing:**
|
|
{% for entity in chat_entities %}
|
|
- {{ entity }}: "{{ state_attr(entity, 'friendly_name') | default('N/A') }}"
|
|
{% else %}
|
|
- (no entities configured)
|
|
{% endfor %}
|
|
|
|
**Message:**
|
|
- Keyboard ID: {{ keyboard_id }}
|
|
- Buttons: {{ buttons | join(', ') }}
|
|
- Message: {{ message_text[:100] }}{{ '...' if message_text | length > 100 else '' }}
|
|
|
|
**Config:**
|
|
- Config Entry ID: {{ config_entry_id if config_entry_id | length > 0 else '(not set)' }}
|
|
|
|
# Validate we have at least one chat ID
|
|
- choose:
|
|
- conditions: "{{ result_chat_ids | length == 0 }}"
|
|
sequence:
|
|
- stop: "No chat IDs resolved. Check chat_ids or chat_entities configuration."
|
|
|
|
# Send the message with keyboard
|
|
- service: telegram_bot.send_message
|
|
data:
|
|
target: "{{ result_chat_ids }}"
|
|
message: "{{ message_text }}"
|
|
inline_keyboard: "{{ inline_keyboard }}"
|
|
config_entry_id: >
|
|
{{ config_entry_id if config_entry_id | length > 0 else omit }}
|