303 lines
12 KiB
Markdown
303 lines
12 KiB
Markdown
[//]: # (title: Build a bot to communicate with your smart home over Telegram)
|
||
[//]: # (description: Integrate Telegram and Platypush to design chat-based automation flow.)
|
||
[//]: # (image: /img/telegram-1.jpg)
|
||
[//]: # (author: Fabio Manganiello <fabio@platypush.tech>)
|
||
[//]: # (published: 2020-01-03)
|
||
|
||
You’ve got your smart home fully set up. You regularly like to show off with your friends how cool it is to turn on
|
||
light bulbs, play videos and movies with a hint to your voice assistant, make coffee, and adjust the thermostat with a
|
||
tap on an app. Congratulations!
|
||
|
||
But if you’re an automation enthusiast who rarely settles, you’ve probably grown frustrated with the number of apps
|
||
you’ll have to download and the number of interfaces you’ll have to master to control your gadgets.
|
||
|
||
You’ll probably have an app for the lights, one for your media center, one for your smart shades, one for your
|
||
thermostat, and a Google Home app that zealously (and hopelessly) tries to put all of them in the same place.
|
||
|
||
Probably, most of these apps won’t communicate with the others, and probably many of them won’t work if you aren’t on
|
||
the same network as your gadget.
|
||
|
||
Wouldn’t it be cool if we could control everything from the same interface, without cluttering our phones or computers
|
||
with tons of apps, through an interface that is accessible both from mobile and desktop devices as well as through
|
||
external scripts/integrations, whether you are on your home network or outdoor? An interface that was both lightweight
|
||
and straightforward to use?
|
||
|
||
But wait, hasn’t such an interface been around for a while, under the name of messaging or chat? After all, wouldn’t it
|
||
be cool to control our house, gadgets, and cloud services through the same interface that we use to send cat pictures to
|
||
our friends, and through a bot completely tailored to our needs, without all the boilerplate/frustration that usually
|
||
comes with third-party bots?
|
||
|
||
## Chat-powered smart homes
|
||
|
||
Enter the world of chatbot-powered smart homes.
|
||
|
||
In this story, I’ll show you how to easily set up commands and routines on top of existing smart home installations.
|
||
|
||
Many messaging apps and platforms exist out there, but so far the efforts of many of them (Facebook Messenger,
|
||
Whatsapp, Hangouts, etc.) in providing a usable developer’s API have been disappointing, to say the least.
|
||
|
||
Gone are the golden days when everyone used XMPP or IRC as their messaging backbone and it was easy to integrate with
|
||
services that all spoke the same language: today’s messaging apps’ world is extremely fragmented.
|
||
|
||
Moreover, as it’s in the interest of many of the big players to create walled gardens that don’t communicate with the
|
||
rest of the world, the most-used solutions out there don’t come with officially supported APIs/developer interfaces.
|
||
|
||
Not only: some of the big players out there actively discourage users from using anything either than the official app
|
||
to interact with the
|
||
platform ([Whatsapp, I’m looking at you](https://news.softpedia.com/news/WhatsApp-Permanently-Bans-Users-of-Unofficial-Clients-475013.shtml)).
|
||
|
||
In this extremely fragmented world made of several unconnected islands, Telegram represents a welcome exception. Their
|
||
[official bot API](https://core.telegram.org/bots/api) is well-documented and supported, and it’s very easy for anyone
|
||
who knows a bit of programming to build integrations.
|
||
|
||
## Create the bot
|
||
|
||
It’s quite easy to create a new bot on Telegram:
|
||
|
||
- Open a conversation with the [BotFather](https://t.me/botfather).
|
||
|
||
- Type `/start` followed by `/newbot` to create a new bot. Give your bot a display name and a username.
|
||
|
||
- You’ll be provided with a link to start a conversation with your bot and a unique API key. Store it somewhere as we’ll
|
||
use it soon to configure the Platypush plugin.
|
||
|
||
![BotFather interaction](../img/telegram-2.png)
|
||
|
||
## Configuring your bot in Platypush
|
||
|
||
- Install Platypush with the HTTP and Telegram extensions:
|
||
|
||
```shell
|
||
pip install 'platypush[http,telegram]'
|
||
[sudo] apt-get install redis-server
|
||
[sudo] systemctl start redis
|
||
[sudo] systemctl enable redis
|
||
```
|
||
|
||
- Play a bit with it if you haven’t done so yet. Find a few things that you’d like to manage/automate — lights, music,
|
||
media, sensors, database, robots, smart plugs… — and install/configure the associated extensions.
|
||
|
||
In this article, we’ll see how to configure our newly-created bot to control Philips Hue lights, music playback, and
|
||
PiCamera streaming.
|
||
|
||
- Add the Telegram configuration to your `~/.config/platypush/config.yaml` file:
|
||
|
||
```yaml
|
||
chat.telegram:
|
||
api_token: <your bot token>
|
||
|
||
backend.chat.telegram:
|
||
enabled: true
|
||
```
|
||
|
||
The [backend](https://docs.platypush.tech/en/latest/platypush/backend/chat.telegram.html) enables you to receive
|
||
events (like new messages, attachments, requests, etc.) and create custom hooks on them.
|
||
|
||
The [plugin](https://docs.platypush.tech/en/latest/platypush/plugins/chat.telegram.html) enables you to write to
|
||
chats, programmatically send messages and attachments, administer channels, etc.
|
||
|
||
Let’s say that we want the bot to implement the following commands:
|
||
|
||
- `/start`: Welcome the new user.
|
||
- `/help`: Show the available commands.
|
||
- `/lights_on`: Turn on the lights.
|
||
- `/lights_off`: Turn off the lights.
|
||
- `/music_play`: Play a music resource/URL.
|
||
- `/music_pause`: Toggle the playback pause state.
|
||
- `/music_next`: Play the next song.
|
||
- `/music_prev`: Play the previous song.
|
||
- `/start_streaming`: Start remote streaming on the PiCamera.
|
||
- `/stop_streaming`: Stop remote streaming on the PiCamera.
|
||
|
||
All we have to do is to create event hooks in the Platypush `config.yaml`. In this context, you’ll need to:
|
||
|
||
```shell
|
||
pip install 'platypush[hue,mpd,picamera]'
|
||
```
|
||
|
||
```yaml
|
||
# Hue lights configuration
|
||
light.hue:
|
||
# Hue bridge IP address
|
||
bridge: 192.168.1.10
|
||
|
||
# Default groups to control
|
||
groups:
|
||
- Living Room
|
||
|
||
# MPD/Mopidy configuration
|
||
music.mpd:
|
||
host: localhost
|
||
port: 6600
|
||
|
||
# PiCamera configuration
|
||
camera.pi:
|
||
vflip: False
|
||
hflip: False
|
||
```
|
||
|
||
- Create a script (e.g. `~/.config/platypush/scripts/bot.py`) to handle the logic of your hooks:
|
||
|
||
```python
|
||
from platypush.event.hook import hook
|
||
from platypush.utils import run
|
||
|
||
from platypush.message.event.chat.telegram import CommandMessageEvent
|
||
|
||
available_cmds = [
|
||
'/lights_on',
|
||
'/lights_off',
|
||
'/music_play [resource]',
|
||
'/music_pause',
|
||
'/music_prev',
|
||
'/music_next',
|
||
'/start_streaming',
|
||
'/stop_streaming',
|
||
]
|
||
|
||
# /start command handler
|
||
@hook(CommandMessageEvent, command='start')
|
||
def on_start_cmd(event, **context):
|
||
run('chat.telegram.send_message', chat_id=event.chat_id,
|
||
text='Welcome! Type /help to see the available commands')
|
||
|
||
# /help command handler
|
||
@hook(CommandMessageEvent, command='help')
|
||
def on_help_cmd(event, **context):
|
||
run('chat.telegram.send_message', chat_id=event.chat_id,
|
||
text='Available commands:\n\n' + '\n'.join(available_cmds))
|
||
|
||
# /lights_on command handler
|
||
@hook(CommandMessageEvent, command='lights_on')
|
||
def on_lights_on_cmd(event, **context):
|
||
run('light.hue.on')
|
||
run('chat.telegram.send_message', chat_id=event.chat_id,
|
||
text='Lights turned on')
|
||
|
||
# /lights_off command handler
|
||
@hook(CommandMessageEvent, command='lights_off')
|
||
def on_lights_off_cmd(event, **context):
|
||
run('light.hue.off')
|
||
run('chat.telegram.send_message', chat_id=event.chat_id,
|
||
text='Lights turned off')
|
||
|
||
# /music_play command handler
|
||
@hook(CommandMessageEvent, command='music_play')
|
||
def on_music_play_cmd(event, **context):
|
||
if event.cmdargs:
|
||
run('music.mpd.play', resource=event.cmdargs[0])
|
||
else:
|
||
run('music.mpd.play')
|
||
|
||
run('chat.telegram.send_message', chat_id=event.chat_id,
|
||
text='Playing music')
|
||
|
||
# /music_pause command handler
|
||
@hook(CommandMessageEvent, command='music_pause')
|
||
def on_music_pause_cmd(event, **context):
|
||
run('music.mpd.pause')
|
||
run('chat.telegram.send_message', chat_id=event.chat_id,
|
||
text='Music paused')
|
||
|
||
# /music_prev command handler
|
||
@hook(CommandMessageEvent, command='music_prev')
|
||
def on_music_prev_cmd(event, **context):
|
||
run('music.mpd.previous')
|
||
run('chat.telegram.send_message', chat_id=event.chat_id,
|
||
text='Playing previous track')
|
||
|
||
# /music_next command handler
|
||
@hook(CommandMessageEvent, command='music_next')
|
||
def on_music_next_cmd(event, **context):
|
||
run('music.mpd.next')
|
||
run('chat.telegram.send_message', chat_id=event.chat_id,
|
||
text='Playing next track')
|
||
|
||
# /start_streaming command handler
|
||
@hook(CommandMessageEvent, command='start_streaming')
|
||
def on_start_streaming_cmd(event, **context):
|
||
run('camera.pi.start_streaming', listen_port=2222)
|
||
run('chat.telegram.send_message', chat_id=event.chat_id,
|
||
text='Camera stream started - check it with vlc tcp/h264://hostname:2222')
|
||
|
||
# /stop_streaming command handler
|
||
@hook(CommandMessageEvent, command='stop_streaming')
|
||
def on_stop_streaming_cmd(event, **context):
|
||
run('camera.pi.stop_streaming')
|
||
run('chat.telegram.send_message', chat_id=event.chat_id,
|
||
text='Camera stream stopped')
|
||
```
|
||
|
||
- Start Platypush:
|
||
|
||
```shell
|
||
# Manual start
|
||
platypush
|
||
|
||
# Service start
|
||
systemctl --user start platypush.service
|
||
```
|
||
|
||
- Open a conversation with your bot on Telegram through the link provided by the BotFather and start playing with it:
|
||
|
||
![Bot interaction](../img/telegram-3.png)
|
||
|
||
You can also invite the bot to a chat group if you want to share it with multiple users - now your friends can finally
|
||
easily mess the lights in your house!
|
||
|
||
## Securing access
|
||
|
||
Right now the bot is accessible by anyone — you probably don’t want that. You can configure the Telegram back end so it
|
||
only accepts messages from a specific list of chat IDs. (In Telegram, the chat_id is used both for private users and
|
||
groups).
|
||
|
||
Send a message to the bot and open the Platypush logs or check its standard output: you should see some messages like
|
||
this:
|
||
|
||
```
|
||
2020-01-03 19:09:32,701| INFO|platypush|Received event: {"type": "event", "target": "turing", "origin": "turing", "id": "***", "args": {"type": "platypush.message.event.chat.telegram.CommandMessageEvent", "chat_id": your_chat_id, "message": {"text": "/help", ...}, "user": {"user_id": your_user_id, "username": "****", "is_bot": false, "link": "https://t.me/you", "language_code": "en", "first_name": "***", "last_name": "***"}, "command": "help", "cmdargs": []}}
|
||
```
|
||
|
||
Copy the `chat_id` associated with your user in the backend configuration:
|
||
|
||
```yaml
|
||
backend.chat.telegram:
|
||
authorized_chat_ids:
|
||
- your_user_id
|
||
```
|
||
|
||
The bot will now reply with an error if you try to send a message from an unauthorized user.
|
||
|
||
![Secured bot interaction](../img/telegram-4.png)
|
||
|
||
## Conclusions
|
||
|
||
We have only explored one specific feature of the Telegram integration in this article: the ability of the bot to react
|
||
to [`CommandMessageEvent`](https://docs.platypush.tech/en/latest/platypush/events/chat.telegram.html#platypush.message.event.chat.telegram.CommandMessageEvent)
|
||
events, run actions, and reply with text messages.
|
||
|
||
As you can see from
|
||
the [list of supported Telegram events](https://docs.platypush.tech/en/latest/platypush/events/chat.telegram.html)
|
||
you can do more, such as:
|
||
|
||
- Create hooks when someone shares contact info. Ever thought of letting a bot automatically store the new contacts sent
|
||
to you by your friends via chat?
|
||
|
||
- Create hooks when you share a photo, video, or image file. For example, automatically download all media files sent to
|
||
a chat to your hard drive or a remote Dropbox folder.
|
||
|
||
- Run actions on text messages instead of commands. You can use
|
||
the [`TextMessageEvent`](https://docs.platypush.tech/en/latest/platypush/events/chat.telegram.html#platypush.message.event.chat.telegram.TextMessageEvent),
|
||
for example, if you prefer to type “turn on the lights” instead of `/lights_on`.
|
||
|
||
- Take a picture from the camera and send it to yourself through
|
||
the [`send_photo`](https://docs.platypush.tech/en/latest/platypush/events/chat.telegram.html#platypush.message.event.chat.telegram.TextMessageEvent)
|
||
action.
|
||
|
||
- Automatically moderate chat groups through
|
||
the [`chat.telegram`](https://docs.platypush.tech/en/latest/platypush/plugins/chat.telegram.html) actions.
|
||
|
||
- You can also deploy multiple bots, e.g. per device, so you can run actions on a specific device from the associated
|
||
chat, or instead, use a single bot as entry point and deliver messages to other devices over MQTT, Kafka, or HTTP API.
|
||
|
||
As long as there’s a plugin for it, you can now do it through your chat bot.
|