openHAB and Telegram Bot

Posted by ads' corner on Monday, 2020-06-22
Posted in [Ansible][Linux][Openhab]

openHAB 2 comes with a Telegram binding which allows to run a Telegram Bot. This bot can both send messages to users and groups, and can receive commands and respond to them. That’s useful: your home automation system can send all kind of details to your mobile phone.

For this to make it work it needs a couple things:

First of all a mobile phone with the Telegram app on it. You can either have the bot message you directly, but this only works for one person. Or you create a group, and have the bot send the messages to the group instead. Find out about the group ID here.

Then you need to create a Telegram Bot. Instructions are available here.

And everything needs to be hooked up in openHAB.

Let’s start with installing the Telegram binding. As usual I use Ansible to install and deploy everything, instructions are available in some of these blog postings.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
- name: Get list of available and installed extensions
  uri:
    url: "http://{{ ansible_host }}:8080/rest/extensions"
  register: oh2_extensions
  changed_when: false

- name: Install extensions
  uri:
    url: "http://{{ ansible_host }}:8080/rest/extensions/{{ item }}/install"
    method: POST
  when: "not (oh2_extensions.json|byattr('id', item))[0].installed"
  with_items:
    - binding-telegram
  register: oh2_install_extensions

Store the Bot Token in a text file (it’s credentials/telegram-bot.txt in my case). Make sure only your user can read the file - anyone with access to the Token can control the bot. (chmod 0600 or chmod 0660).

Things

I use a couple different Telegram groups:

  • One for the family, the bot is sending important messages there
  • One for more detailed messages I’m interested in (kind of a debugging channel)
  • A testing channel which I use to try out new features

The (negative) channel ids are stored in text files. The ids are loaded by Ansible when I deploy the telegram.things file:

1
Thing telegram:telegramBot:HA_Bot [ chatIds="{{ lookup('file', playbook_dir + '/credentials/telegram-ha-channel.txt') }}","{{ lookup('file', playbook_dir + '/credentials/telegram-ha-test-channel.txt') }}","{{ lookup('file', playbook_dir + '/credentials/telegram-ha-details-channel.txt') }}", botToken="{{ lookup('file', playbook_dir + '/credentials/telegram-bot.txt') }}", parseMode="Markdown" ]

Looks a bit long, because of the many Ansible Lookups. The deployed file in /etc/openhab2/things/telegram.things is much shorter:

1
Thing telegram:telegramBot:HA_Bot [ chatIds="-<channel main>","-<channel details>","-<channel test>", botToken="<token>", parseMode="Markdown" ]

Note: “HA_Bot” is the name of this bot. This name is required for all other activities with this bot.

Items

The Binding provides a couple channels, I’m mostly interested in:

  • lastMessageText: the last command sent by someone
  • lastMessageDate: the time someone sent the last command
  • chatId: the group or private chat someone sent a command to - the bot needs to reply to that

All channels defined as Items:

1
2
3
4
5
6
7
String		telegramLastMessage		"Telegram Bot Last Message"		{ channel = "telegram:telegramBot:HA_Bot:lastMessageText" }
String		telegramLastMessageURL		"Telegram Bot Last Message URL"		{ channel = "telegram:telegramBot:HA_Bot:lastMessageURL" }
DateTime	telegramlastMessageDate		"Telegram Bot Last Message Date"	{ channel = "telegram:telegramBot:HA_Bot:lastMessageDate" }
String		telegramLastMessageName		"Telegram Bot Last Message Sender"	{ channel = "telegram:telegramBot:HA_Bot:lastMessageName" }
String		telegramLastMessageUsername	"Telegram Bot Last Message Username"	{ channel = "telegram:telegramBot:HA_Bot:lastMessageUsername" }
String		telegramLastMessageChatId	"Telegram Bot Last Message chatId"	{ channel = "telegram:telegramBot:HA_Bot:chatId" }
String		telegramLastMessageReplyId	"Telegram Bot Last Message replyId"	{ channel = "telegram:telegramBot:HA_Bot:replyId" }

Rules

Once the bot is running, it can send messages on it’s own:

1
2
    val telegramAction = getActions("telegram","telegram:telegramBot:HA_Bot")
    telegramAction.sendTelegram(Long::parseLong(TELEGRAM_CHANNEL_HA_DETAILS), "%s %s", "Telegram Bot", "online")

This will send a test message to the Telegram group defined in TELEGRAM_CHANNEL_HA_DETAILS.

For responding to messages, the bot needs to be triggered by an event. It’s not useful to work on lastMessageText, because a user can send the same message multiple times - in this case no change event is triggered. An update is triggered, but that’s not helpful here. However with every message the lastMessageDate will change, and that can be used for a trigger:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
rule "Telegram Bot receive test"
when
    Item telegramlastMessageDate received update
then
    if (telegramLastMessage.state.toString == "/test") {
        logInfo("Telegram Query Test", "Test")
        val telegramAction = getActions("telegram","telegram:telegramBot:HA_Bot")
        telegramAction.sendTelegram(Long::parseLong(telegramLastMessageChatId.state.toString), "Test received")
    }
end

This example picks up a /test command, and sends a simple message back.


Categories: [Ansible] [Linux] [Openhab]