blog/markdown/Build-a-bot-to-communicate-...

304 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

[//]: # (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)
Youve 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 youre an automation enthusiast who rarely settles, youve probably grown frustrated with the number of apps
youll have to download and the number of interfaces youll have to master to control your gadgets.
Youll 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 wont communicate with the others, and probably many of them wont work if you arent on
the same network as your gadget.
Wouldnt 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, hasnt such an interface been around for a while, under the name of messaging or chat? After all, wouldnt 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, Ill 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 developers 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: todays messaging apps world is extremely fragmented.
Moreover, as its in the interest of many of the big players to create walled gardens that dont communicate with the
rest of the world, the most-used solutions out there dont 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, Im 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 its very easy for anyone
who knows a bit of programming to build integrations.
## Create the bot
Its 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.
- Youll be provided with a link to start a conversation with your bot and a unique API key. Store it somewhere as well
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 havent done so yet. Find a few things that youd like to manage/automate — lights, music,
media, sensors, database, robots, smart plugs… — and install/configure the associated extensions.
In this article, well 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.
Lets 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, youll 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 dont 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 theres a plugin for it, you can now do it through your chat bot.