Telegram bot notifications and communication

Tags: #<Tag:0x00007f3f1c1102d0>

Hi all,
I just wanted to post my setup using Telegram to send notifications about calendar events and general home information.

The samples below show notifications about:

  • Home Assistant has booted up,
  • Nobody detected at home, but some devices are left on,
  • Notify of an upcoming calendar event (before it happens),
  • Calendar reminder with actionable “remind me in X minutes”.

Example from below with remind me later buttons:

To start with, we define a telegram bot to be able to message with.
There are many guides to setting this up, it’s as simple as sending a message to an existing Telegram Bot called BotFather. One thing they all seemed to miss though, is that you need to send a message to your bot before it can interact with you. So once you’ve got your bot and sent it a message (this can be in private or a group chat with housemates/family) you then need the chat id.

So we define the bot in configuration.yaml, giving our api_key from BotFather and the chat_id we want to be able to communicate with (this can be a list).

telegram_bot:
  - platform: polling
    api_key: 2764xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    allowed_chat_ids:
     - xyxyxyxy

Then we set up a HA notification service using the telegram platform, I named mine onplus because it’s communicating mainly with my phone and the chat_id is that of me<->bot. This chat_id must be in the list of allowed chats above.

notify:
  - name: oneplus
    platform: telegram
    chat_id: xyxyxyxy

Starting with a simple automation then (this indentation and synax is from a separate automations.yaml), this triggers when HA starts and sends a message using the notify service we set up.

- id: 'telegramboot'
  alias: 'Telegram bot to notify on boot'
  trigger:
  - event: start
    platform: homeassistant
  condition: []
  action:
  - service: notify.oneplus
    data:
      message: Home Assistant on Pi booted

Slightly more complex now, if all the devices that I track (device_tracker) are not_home, check to see if any of the major devices in the home are left on. If a device is on, send a message. This results in a few false positives due to devices dropping out of wifi or whatever, but it’s not bad. You could extend this with some interactivity like in the later examples (you could add a “turn off” button, for instance). My main devices are a Harmony Hub for the TV and a Wake-On-LAN switch for my computer, generally these devices should be off when nobody is home.

- id: 'telegramawayelectronics'
  alias: 'Telegram bot to notify nobody home and electronics on'
  trigger:
  - entity_id: group.all_devices
    from: home
    platform: state
    to: not_home
  condition:
  - condition: or
    conditions:
    - condition: state
      entity_id: remote.harmony_hub
      state: 'on'
    - condition: state
      entity_id: switch.computer
      state: 'on'
  action:
  - data:
      message: Nobody home, but device(s) have been left on.
    service: notify.oneplus

Now I want to get a notification the night before the bins are due for collection. I could never get the calendar offset to work properly in HA and I wanted to keep the bin event in the right day/time. You could create an extra hidden calendar just for this, but I wanted to be able to see the same events on my phone and edit them easily. So I had to make an intermediate sensor to be the middle-man between the calendar and the automation. I wrote about this in another thread.

Basically, it checks the start and end time of the next event in a calendar and turns itself on and off depending on whether it’s starting in the next 6 hours. You may also need the date__time sensor included just below. This code sample is extracted from my sensors.yaml, so the syntax and indentation may vary for you.

- platform: template
  sensors:
    bins_out_offset:
      friendly_name: "Bins out offset"
      value_template: >
        {% if as_timestamp(states.calendar.house.attributes.start_time) - as_timestamp(strptime(states.sensor.date__time.state, "%Y-%m-%d, %H:%M" ) ) < 21600 and as_timestamp(states.calendar.house.attributes.end_time) > as_timestamp(strptime(states.sensor.date__time.state, "%Y-%m-%d, %H:%M" ) ) %}on{% else %}off{% endif %}
- platform: time_date
  display_options:
    - 'date_time'

Now the sensor is set up to switch on/off with 6 hours notice (change the 21600 for a different amount of seconds), we can send a notification when the state turns on.
Here I also include an inline_keyboard with actionable buttons. I provide some clean text to display and then a command I want to execute when they’re pressed. Back in automations.yaml now.

- id: 'telegrambinsout'
  alias: 'Telegram bot to remind to put bins out'
  trigger:
  - entity_id: sensor.bins_out_offset
    platform: state
    to: 'on'
  condition: []
  action:
  - service: notify.oneplus
    data_template:
      message: '{{ states.calendar.house.attributes.message }}'
      data:
          inline_keyboard:
          - '30 Minutes:/30m, 1 Hour:/1h, 3 Hours:/3h'
          - 'No reminder:/removekeyboard'

This gives us the below result:

Now we need to handle the button presses, so firstly if we don’t want a reminder, we can just get rid of the keyboard from taking up so much space. The trigger is setup to handle the /removekeyboard command from above. The action first notifies telegram that we have handled the command, and then it edits the previous message to provide an empty inline_keyboard.

- id: 'telegramremoveinline'
  alias: 'Telegram callback to remove keyboard'
  hide_entity: true
  trigger:
    platform: event
    event_type: telegram_callback
    event_data:
      data: '/removekeyboard'
  action:
  - service: telegram_bot.answer_callback_query
    data_template:
      callback_query_id: '{{ trigger.event.data.id }}'
      message: 'OK'
  - service: telegram_bot.edit_replymarkup
    data_template:
      message_id: '{{ trigger.event.data.message.message_id }}'
      chat_id: '{{ trigger.event.data.user_id }}'
      inline_keyboard: []

The OK to Telegram shows up like the following (with different text):

Handling the reminder commands are very similar. It is triggered when the specific command button is pressed. The action this time does the same as above, but then has a delay according to which button was pressed. After the delay, another message is sent with using the same message text as before and using the same keyboard again.

- id: 'telegramrepeat30m'
  alias: 'Telegram callback to repeat message in 30 minutes'
  hide_entity: true
  trigger:
    platform: event
    event_type: telegram_callback
    event_data:
      data: '/30m'
  action:
  - service: telegram_bot.answer_callback_query
    data_template:
      callback_query_id: '{{ trigger.event.data.id }}'
      message: 'OK, reminding you in 30 minutes'
  - service: telegram_bot.edit_replymarkup
    data_template:
      message_id: '{{ trigger.event.data.message.message_id }}'
      chat_id: '{{ trigger.event.data.user_id }}'
      inline_keyboard: []
  - delay: '00:30:00'
  - service: notify.oneplus
    data_template:
      message: '{{ trigger.event.data.message.text }}'
      data:
        inline_keyboard:
          - '30 Minutes:/30m, 1 Hour:/1h, 3 Hours:/3h'
          - 'No reminder:/removekeyboard'

Another of the same commands, but with a 1 hour delay instead.

- id: 'telegramrepeat1h'
  alias: 'Telegram callback to repeat message in 1 hour'
  hide_entity: true
  trigger:
    platform: event
    event_type: telegram_callback
    event_data:
      data: '/1h'
  action:
  - service: telegram_bot.answer_callback_query
    data_template:
      callback_query_id: '{{ trigger.event.data.id }}'
      message: 'OK, reminding you in 1 hour'
  - service: telegram_bot.edit_replymarkup
    data_template:
      message_id: '{{ trigger.event.data.message.message_id }}'
      chat_id: '{{ trigger.event.data.user_id }}'
      inline_keyboard: []
  - delay: '01:00:00'
  - service: notify.oneplus
    data_template:
      message: '{{ trigger.event.data.message.text }}'
      data:
        inline_keyboard:
          - '30 Minutes:/30m, 1 Hour:/1h, 3 Hours:/3h'
          - 'No reminder:/removekeyboard'

Using this same pattern, you can add a keyboard to the previous examples and be able to turn off a device without leaving Telegram. These “remind me” automations are reusable since they resend the same message text as was sent before.

You can interact with the chat bot via text too. The above commands (/30m, /1h) can all be typed, the buttons are just a fancy way of doing it. So you can have a command /homestatus and have HA reply with the temperature and a list of devices that are on, for example. It could also include actionable things like turn all lights off.

Also, once setup, the Telegram bot can be used by more than just HA. For example you can use a command line to send a message:
curl -s "https://api.telegram.org/bot$KEY/sendMessage?chat_id=$CHATID&disable_web_page_preview=1&text=$TEXT"

37 Likes

Good work! The possibilities of the telegram bot are huge. Just another example:

It’s a combination of the darksky sensor and the telegram bot.

2 Likes

Love the telegram service for interaction with my homeassistant, I use a similar setup myself. Thanks for sharing your ideas, it’s given me a few thoughts on extra things I can add to mine :+1:

The alert component (https://www.home-assistant.io/components/alert/) can also be integrated easily. I prefer the inline keyboard setup of @Kallb123 anyhow.

@syssi I didn’t even know the alert component existed! Thanks for pointing that out, it seems quite useful.

Good idea about the weather too, I didn’t think about the possibilty of using images :slight_smile:

@mf_social care to share any of your Telegram interactions? I’m always looking for new ideas!

The two files in this folder, telegram_actions and telegram_info…

link removed

3 Likes

Hi everybody !

Do you know how process to do a line break in a telegram notifications sent ?
I tried « /n » but it doesn’t work !

Thank you !

Put two lines in the message. 1 return gives you a space, 2 gives you a new line, 3 gives a new paragraph :+1:

@mf_social @Kallb123

I’ve been shamelessly plagiarising some of the things I’ve seen since I discovered HA a few weeks ago and this is definitely one of the best things I’ve seen.

I am wondering though if it is possible to pass the inline keyboard data dynamically to a notification engine?

I’ve tried this (and other things):

automation:
### TEST TRIGGER ###
    trigger:
      platform: state
      entity_id: input_boolean.ha_test_msg
      to: 'on'
### TEST TRIGGER ###

    action:
      - service: script.text
        data_template:
          tell: notify.telegram_me
          message: your journey home is going to be longer than usual, currently it is estimated to be "{{ states('sensor.me_to_home_waze') }}" minutes.
          keyboard1: "- '30 Minutes:/30m, 1 Hour:/1h, 3 Hours:/3h'"
          keyboard2: "- 'No reminder:/removekeyboard'"

This is the script that sends the Telegram msg

  # Text message
  text:
    sequence:
      - condition: template
        value_template: "{{ tell | length != 0 }}"
      - service_template: "{{ tell }}"
        data_template:
          message: "{{ message|replace('   ',' ') | replace('  ',' ') }}"
          data:
            inline_keyboard:
              - keyboard1: '{{ keyboard1 }}'
              - keyboard2: '{{ keyboard2 }}'
              - keyboard3: '{{ keyboard3 }}'
              - keyboard4: '{{ keyboard4 }}'
              - keyboard5: '{{ keyboard5 }}'

And here is the error - the last line probably gives it away but I can’t work out what the syntax should be (if it is possible).

Log Details (ERROR)

Mon Apr 23 2018 16:33:56 GMT+0100 (GMT Summer Time)

Error executing service <ServiceCall telegram_bot.send_message: target=[491420434], message=<homeassistant.helpers.template.Template object at 0x705a2e10>, inline_keyboard=[{'keyboard1': "- '30 Minutes:/30m, 1 Hour:/1h, 3 Hours:/3h'"}, {'keyboard2': "- 'No reminder:/removekeyboard'"}, {'keyboard3': ''}, {'keyboard4': ''}, {'keyboard5': ''}]>
Traceback (most recent call last):
  File "/usr/lib/python3.6/site-packages/homeassistant/core.py", line 1002, in _event_to_service_call
    await service_handler.func(service_call)
  File "/usr/lib/python3.6/site-packages/homeassistant/components/telegram_bot/__init__.py", line 278, in async_send_telegram_message
    partial(notify_service.send_message, **kwargs))
  File "/usr/lib/python3.6/concurrent/futures/thread.py", line 56, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/usr/lib/python3.6/site-packages/homeassistant/components/telegram_bot/__init__.py", line 465, in send_message
    params = self._get_msg_kwargs(kwargs)
  File "/usr/lib/python3.6/site-packages/homeassistant/components/telegram_bot/__init__.py", line 440, in _get_msg_kwargs
    [_make_row_inline_keyboard(row) for row in keys])
  File "/usr/lib/python3.6/site-packages/homeassistant/components/telegram_bot/__init__.py", line 440, in <listcomp>
    [_make_row_inline_keyboard(row) for row in keys])
  File "/usr/lib/python3.6/site-packages/homeassistant/components/telegram_bot/__init__.py", line 404, in _make_row_inline_keyboard
    raise ValueError(str(row_keyboard))
ValueError: {'keyboard1': "- '30 Minutes:/30m, 1 Hour:/1h, 3 Hours:/3h'"}

At a very quick and unresearched guess I would think no, because you can only send strings not lists and I don’t think it will accept it in the format you’ve done it to try and incorporate the list as strings.

I’ll try and keep it in mind and see if I can think of how it might work, but I think you’re hitting a pretty solid boundary there.

Thanks for replying, I’ll stop trying for now!

But what if your next commit would’ve worked and proved me wrong? :astonished:

:stuck_out_tongue_winking_eye:

Yeah, what if? :stuck_out_tongue:
I’ve been at this for over two hours! :worried:

I’m more than happy to have an excuse to move on!!!

1 Like

Ha ha, yup, it’s addictive all this…

Indeed it is…

So, to make those callback scripts truly reusable the notify service after the delay needs to be set dynamically. Is that possible?

- id: 'telegramrepeat30m'
  alias: 'Telegram callback to repeat message in 30 minutes'
  hide_entity: true
  trigger:
    platform: event
    event_type: telegram_callback
    event_data:
      data: '/30m'
  action:
  - service: telegram_bot.answer_callback_query
    data_template:
      callback_query_id: '{{ trigger.event.data.id }}'
      message: 'OK, reminding you in 30 minutes'
  - service: telegram_bot.edit_replymarkup
    data_template:
      message_id: '{{ trigger.event.data.message.message_id }}'
      chat_id: '{{ trigger.event.data.user_id }}'
      inline_keyboard: []
  - delay: '00:30:00'
 ###### HERE
  - service: notify.DYNAMICALY_SET NAME
 ###### ^^^^^
    data_template:
      message: '{{ trigger.event.data.message.text }}'
      data:
        inline_keyboard:
          - '30 Minutes:/30m, 1 Hour:/1h, 3 Hours:/3h'
          - 'No reminder:/removekeyboard'
1 Like

Yup

service_template: >
  {% if SITUATION_ONE_LOGIC %} notify.SERVICE_ONE
  {% elif SITUATION_TWO_LOGIC %} notify.SERVICE_TWO
  {% else %} notify.SERVICE_THREE {% endif %}

Of course!
I’ve been looking at this for too long!
Time for a drink I think :slight_smile:

Thanks (again)

1 Like

I see you are strong with this !

Could you help me with my alarmclock ?

I have an input_datetime to choose the time of my morning alarmclock.
I would like to write in telegram to choose the hour :slight_smile:

For example: “Wake me at 6h32” and of course the input_datetime take the hour…!

Do you know how to do ?

Thank you !!

You could try sending /alarm 06:32:00 to this…

trigger:
  platform: event
  event_type: telegram_command
  event_data:
    command: '/alarm'
action:
  - service: input_datetime.set_datetime
    entity_id: input_datetime.YOUR _ALARM_CLOCK
    data_template:
      time: "{{ trigger.event.data.args }}" 
  - service: telegram_bot.send_message
    data_template:
      target: "{{ trigger.event.data.user_id }}"
      message: "Alarm time is  {{ states('input_datetime.YOUR_ALARM_CLOCK') }}" 

And see how close that is to working. Then you could probably make it a bit less clunky by actually parsing the text for the time. Baby steps…

Thank you !!

The 2nd action works but not the input_datetime template !
I will looking for a solution !

EDIT: I know why it doesn’t work !
When the arg is write for the input_datetime, the state is between quote: ‘06:32:00’
I think it’s for this reason…

Do you know how remove the quote ?

Thank you !

1 Like