Skip to content

Conversation

@hanwg
Copy link
Contributor

@hanwg hanwg commented Dec 25, 2025

Proposed change

Currently, chat_id is used as targets for telegram bot actions.
chat_id is not intuitive since it is a numeric identifier.

With the introduction of notify entities in #149853, we can instead use these entities as targets.
This change introduces a new notify_target parameter which uses the entity selector to allow the user to select telegram bot notify entities for actions.

image

Example actions for use in automations/scripts:

# old
action: telegram_bot.send_message
data:
  message: test
  target: 1111111111  # this is being deprecated

# new (using notify entities, preferred method for most users)
action: telegram_bot.send_message
data:
  message: test
  entity_id: notify.bot_user_1111111111  # entity selector for specifying notify entities

# new (alternate way using chat_ids)
action: telegram_bot.send_message
data:
  message: test
  chat_id: 1111111111  # new field

Detailed changes:

  1. Scope: Change is for all Telegram bot actions.
  2. target action parameter is being deprecated:
  • Current: target parameter is used for specifying chat IDs
  • Change: added new chat_id parameter. During this migration period, chat IDs found in target are automatically copied to the new chat_id field. A repair issue will also created to inform the user to update their automations/scripts.
  1. Design consideration: This PR doesn't introduce new entity service but rather, it modifies the existing action to handle notify entities. The integration has lots of actions and introducing new entity services will make the number of actions swell. Furthermore, users are already familiar with the names of the existing actions.
  2. Updated action responses (return values): Added new field entity_id to ServiceResponse. This new output value can be used in complex automations e.g. output entity_id of send_message can be used as input for edit_message.
  3. New section: Advanced. The purpose of this new section is to hide the config_entry_id and chat_id fields. This helps to avoid confusion for new users by drawing the user's attention to the new entity_id field for specifying notify entities as targets. Custom targets are meant for advance usage where the user has to specify the numeric chat ids.
  4. New behavior: We first build the list of targets based on the entity_id, config_entry_id and target fields. The service is then invoked once for each of the targets. (See _build_targets() in __init__.py)

Type of change

  • Dependency upgrade
  • Bugfix (non-breaking change which fixes an issue)
  • New integration (thank you!)
  • New feature (which adds functionality to an existing integration)
  • Deprecation (breaking change to happen in the future)
  • Breaking change (fix/feature causing existing functionality to break)
  • Code quality improvements to existing code or addition of tests

Additional information

Checklist

  • I understand the code I am submitting and can explain how it works.
  • The code change is tested and works locally.
  • Local tests pass. Your PR cannot be merged unless tests pass
  • There is no commented out code in this PR.
  • I have followed the development checklist
  • I have followed the perfect PR recommendations
  • The code has been formatted using Ruff (ruff format homeassistant tests)
  • Tests have been added to verify that the new code works.
  • Any generated code has been carefully reviewed for correctness and compliance with project standards.

If user exposed functionality or configuration variables are added/changed:

If the code communicates with devices, web services, or third-party tools:

  • The manifest file has all fields filled out correctly.
    Updated and included derived files by running: python3 -m script.hassfest.
  • New or updated dependencies have been added to requirements_all.txt.
    Updated by running python3 -m script.gen_requirements_all.
  • For the updated dependencies - a link to the changelog, or at minimum a diff between library versions is added to the PR description.

To help with the load of incoming pull requests:

@hanwg hanwg marked this pull request as ready for review December 26, 2025 11:40
Copy link
Contributor

@luca-angemi luca-angemi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested as a custom component with latest nightly. All actions are working properly.

@MartinHjelmare
Copy link
Member

Some comments are not resolved above.

@MartinHjelmare MartinHjelmare marked this pull request as draft December 29, 2025 17:45
@hanwg
Copy link
Contributor Author

hanwg commented Dec 30, 2025

I think I've resolved all comments but if I missed any, do point them out to me.

@hanwg hanwg marked this pull request as ready for review December 30, 2025 06:38
@SiriosDev

This comment was marked as spam.

async def _call_service(
service: ServiceCall, notify_service: TelegramNotificationService, chat_id: int
) -> dict[str, JsonValueType] | None:
msgtype = service.service
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
msgtype = service.service
service_name = service.service

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done in d847757

def _build_targets(
hass: HomeAssistant, service: ServiceCall
) -> list[tuple[TelegramBotConfigEntry, int, str]]:
"""Build list of targets where each target is represented by its corresponding config entry, chat ID and notify entity id."""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please limit docstring lines to 72 characters. The first line should be a header and a single sentence. After that an optional body can follow separated from the header by a blank line.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done in 693e70f

async def _call_service(
service: ServiceCall, notify_service: TelegramNotificationService, chat_id: int
) -> dict[str, JsonValueType] | None:
msgtype = service.service
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing docstring.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done in 693e70f

chat_ids = migrate_chat_ids
if ATTR_CHAT_ID in service.data:
chat_ids = chat_ids | (
{int(service.data[ATTR_CHAT_ID])}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need to copy to integer if the chat id is already an integer. Isn't the chat id parameter in the service call data either an integer or a list of integers?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, chat_id is either int or list[int].
service data has the dict[str, Any] type and I was trying to erase the Any type to avoid int | Any.

done in d4f2d7c

service.hass.config_entries.async_entries(DOMAIN)
)
if len(config_entries) == 1:
config_entry = config_entries[0]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calculating a default config entry here or specifying one in the service call data only makes sense when not specifying entity_id. An entity is connected to a single config entry, so it's not possible to change what config entry (bot) to use for an entity target.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to make the config entry id parameter and the entity_id parameter in the service calls exclusive. It's confusing if we allow both at the same time.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calculating the default config entry is necessary to avoid breaking changes with existing automations.
For example, the following automation does not have config_entry_id or chat_id specified and the existing behavior is to automatically select a default bot and chat_id.

action: telegram_bot.send_message
data:
  message: test

Copy link
Contributor Author

@hanwg hanwg Jan 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only have a small handful of users that I interact with regularly and the feedback from them was that they preferred to have both entity_id and config_entry_id + chat_id.
The way that they understand it is that config_entry_id + chat_id are additional targets on top of what is specified in entity_id.

They aren't very inclined to the idea of mutually exclusiveness of entity_id and config_entry_id + chat_id since they might need to duplicate their automations.

Example automations below for illustration.

# not mutually exclusive - single action to specify both notify entities and chat ids

actions:
  - action: telegram_bot.send_message
    data:
      message: test
      entity_id:
        - notify.bot_user
      config_entry_id: xxx
      chat_id:
        - 1111111111
# mutually exclusive - need to duplicate actions

actions:
  - action: telegram_bot.send_message
    data:
      message: test
      entity_id:
        - notify.bot_user
   - action: telegram_bot.send_message
     data:
       message: test
       config_entry_id: xxx
       chat_id:
         - 1111111111

To balance simplicity with flexibility, that's why I’m supporting both entity_id and config_entry_id + chat_id. By moving the latter to an 'Advanced' section, the basic configuration remains accessible (easy) for most users.

@MartinHjelmare MartinHjelmare marked this pull request as draft January 12, 2026 15:21
@hanwg hanwg marked this pull request as ready for review January 13, 2026 02:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants