Remote Control of Domoticz by Telegram Bot

From Domoticz
Revision as of 22:35, 26 January 2018 by Simonrg (talk | contribs) (→‎Keeping DTGBOT Running)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Introduction

This page assumes you have set up your Telegram Bot as described on this page - Telegram Bot

In June 2015 Telegram introduced Telegram Bots, these are effectively just a different type of Telegram account, rather than having any bot functionality of their own. So the Telegram Bot page has explained how to use a Telegram Bot to send notifications. This page goes one stage further by adding automation functionality to your Telegram Bot.

Like any Telegram account a Telegram Bot is able to both send and receive messages. Messages are sent using https requests as previously described. Messages received by your Telegram Bot remain on the server for at least 24 hours (if not read) and need to be collected from the server by a program which provides the automation part of the Telegram Bot.

Introducing dtgbot

dtgbot is a lua program which continuously requests from the Telegram servers any new messages sent to your Telegram Bot. So it sends a long poll request to the server, which the server will not respond to for 60 seconds unless a new message arrive in which case it is returned to dtgbot instantly, this means that dtgbot puts a very low load on your Raspberry Pi as it only sends and recieves 1 http request every minute unless it is asked to do something.

When a message is recieved then dtgbot analyses whether it is a command, if so then it asks Domoticz either for information or instructs Domoticz to carry out an action. dtgbot is extensible either by writing new Lua functions (based on the same template) or by creating bash scripts which dtgbot can execute.

To see what dtgbot and your Telegram Bot can do together have a look at - Telegram Bot - dtgbot Functionality.

Installing dtgbot

These instructions are for use on a Raspberry Pi, for other systems you will need to find / compile the necessary Lua libraries yourself.

If you are already using telegram-cli, then you can either remove that from your system or leave it in place. dtgbot is based solely on the Telegram Bot api and so uses direct http calls to communicate with the Telegram servers.

Install Lua 5.2

dtgbot is a Lua program that runs outside of Domoticz, so you need to ensure the correct version of Lua is installed on your system, after making sure system is up to date:

sudo apt-get update
sudo apt-get upgrade
sudo apt-get install lua5.2

Installing Lua Libraries

dtgbot requires https / json ability to interact, (json, socket and ssl libraries) So you will need to install the necessary libraries for Lua. I have compiled these and made them availalbe on the forum - dtgbot - Domoticz TeleGram BOT. Download the two tar.gz files to a temporary directory (~/temp).

sudo mkdir -p /usr/local/share/lua/5.2/ #Only needed if this directory does not exist
cd /usr/local/share/lua/5.2/
sudo tar -xvf ~/temp/usrlocalsharelua52.tar.gz
sudo mkdir -p /usr/local/lib/lua/5.2/ #Only needed if this directory does not exist
cd /usr/local/lib/lua/5.2/
sudo tar -xvf ~/temp/usrlocalliblua52.tar.gz

Installing JQ dependency (needed for certain bash scripts)

cd
wget https://github.com/stedolan/jq/releases/download/jq-1.4/jq-1.4.tar.gz
tar -zxvf jq-1.4.tar.gz
rm jq-1.4.tar.gz
cd jq-1.4
./configure
make
sudo make install

Customisation File

In order to avoid accidentally passing system specific information, I have included all the system in a single shell script.

In order to create this file you need to edit the suggest file below with your email address, your Telegram bot token, the Telegram chat id for the default user you want to send messages to and have a temporary directory set up, I would recommend using a ram disk for the temporary directory, have a look here - http://www.domoticz.com/wiki/Setting_up_a_RAM_drive_on_Raspberry_Pi.

Create your customisation file:

sudo nano /etc/profile.d/DomoticzData.sh
#!/bin/bash

# Variables to avoid having to set local system information in individual automation scripts

#I can't get the next line to work during start-up so have gone to 127 instead
#export DomoticzIP=$(hostname -I|sed 's/[ ]*$//')
export DomoticzIP="127.0.0.1"
export DomoticzPort="8080"
export TempFileDir="/var/tmp/"
export BotHomePath="/home/pi/dtgbot/"
export BotBashScriptPath=$BotHomePath"bash/"
export BotLuaScriptPath=$BotHomePath"lua/"
export BotLuaLog=$TempFileDir"dtb.log"
export TelegramChatId='012343553'
export TelegramBotToken="000000000:keykeykeykeykeykeykeykey"
export TelegramBotOffset="TelegramBotOffset"
export EmailTo="[email protected]"

N.B. If your Domoticz system is password protected even for local network then you will need to replace "127.0.0.1" with "username:[email protected]"

And set it to be executable:

sudo chmod +x /etc/profile.d/DomoticzData.sh

This single file avoids you having to customise any of the bash or Lua scripts mentioned for automating Telegram and Domoticz.

As Lua functions run by Domoticz are run within Domoticz, the environment variables defined in DomoticzData.sh need to be available to Domoticz, add these two lines into /etc/init.d/domoticz.sh, just before #Exit.

# Get the preset variables so that they are available for Lua functions within Domoticz
. /etc/profile.d/DomoticzData.sh

Now all the variables can be available to your Domoticz Lua / Bash scripts by adding in the appropriate os.getenv calls as detailed under the In Lua Functions section.

Domoticz User Variables

Create the following user variables in Domoticz before starting dtgbot

TelegramBotOffset - integer: In order to stop dtgbot from processing the same message repeatedly it needs to keep track of the id of the next message. To do this create a user variable in Domoticz called TelegramBotOffset and set it to a value of 1. The first use of dtgbot will result in all unread messages being received and processed, at which point the offset will be correctly set and then only new messages will be processed from now on.

TelegramBotMenu - string: This will determine whether custom keyboards are displayed - set the value to On to use custom keyboard and Off to not use custom keyboards.

TelegramBotWhiteListedIDs string: Use this string to secure your dtgbot from being used by everybody. Put a list of userids that you want to allow to access your system separated by | here as userid1|userid2|userid3 , these are numbers so it will look like 12345678|2343249724|4534957 or juse 1234342234 if you only want 1 person to access the system. If this variable does not exist then anybody can access your bot.

Installing / Updating dtgbot Scripts

All the scripts are now on github, if you have already installed a previous version of dtgbot, then execute the following commands before installing:

sudo service dtgbot stop
cd ~
mv dtgbot olddtgbot

To install the scripts execute the following commands:

cd ~
git clone https://github.com/steps39/dtgbot.git
cd ~/dtgbot
chmod 755 dtgbot
chmod 755 *.sh
cd ~/dtgbot/bash
chmod 755 *.sh

You now have dtgbot, if you have previously installed then to start the new version just do:

sudo service dtgbot start

Otherwise follow the instructions below.

Creating / Replacing the dtgbot Service

The service script was downloaded as part of the scripts package, so simply needs moving to the right location - normally this file will only rarely be updated when it is simply stop the dtgbot service (sudo service dtgbot stop) and then follow the instructions below.

cd ~/dtgbot/service
sudo mv dtgbot /etc/init.d
cd ..
rmdir service
cd /etc/init.d
sudo chmod +x dtgbot
sudo update-rc.d dtgbot remove      #Remove previous version
sudo update-rc.d dtgbot defaults    #Install new version

Running dtgbot

Setting Up The Service

dtgbot is designed to run as a service, meaning it will start automatically when your computer is rebooted, however as it issues 60 second http requests, so it can take up to 60 seconds to stop, so even after a service stop, wait a further 60 seconds before restarting it, otherwise the new version will crash and the old version will continue running.

To start dtgbot:

sudo service dtgbot start

To check on dtgbot status:

sudo service dtgbot status

To stop dtgbot:

sudo service dtgbot stop

Then wait 60 seconds before restarting (or check ps -ef | grep -i dtgbot.lua - should only return the grep line if the script has finished)

Keeping dtgbot Running

To ensure that dtgbot will be running when you need it, you can use Monit, Monit will monitor any running service and take action if it is not behaving properly. dtgbot is required to run continually so Monit can be set up to restart dtgbot if it is no longer running. dtgbot should not just stop running, but just in case it does Monit will now restart it.

Also some users have found that dtgbot can hang. This appears to happen when dtgbot has made the curl call to the Telegram server to request a message and the internet connection is somehow interrupted. Normally, dtgbot should either recieve a message from Telegram server or timeout after 60 second. but in this case the Curl call to Telegram does not timeout and stays open forever resulting in the dtgbot script hanging. So dtgbot does some extra logging by writing date/time to a logfile in the temp directory defined for dtgbot with a Monit rule testing for the age of this file and restarting dtgbot if it has not been updated in the last 2 minutes.

Instructions for using Monit with Domoticz can be found on the wiki - Monitoring domoticz, once Monit is installed and set up, add this section to the end of your /etc/monit/monitrc and then restart the monit service. Please also check that paths below are correct for your set-up.

# Monitor the DTGBOT Service
check process dtgbot with pidfile /var/run/dtgbot.pid
  start program = "/home/pi/dtgbot/restartbot.sh" timeout 20 seconds
  stop  program = "/home/pi/dtgbot/stopbot.sh" timeout 20 seconds
  if 5 restarts within 5 cycles then timeout

# Monitor the DTGBOT loop file which should be updated each minute.
# Restart DTGBOT when not updated in 2 minutes
check file dtgloop with path /var/tmp/dtgloop.txt
  start program = "/home/pi/dtgbot/restartbot.sh" timeout 20 seconds
  if timestamp > 2 minutes then restart
if 5 restarts within 5 cycles then timeout

Troubleshooting

In most cases dtgbot will guide you when errors occur, so please include the contents of the two log files when requesting help, don't forget to remove any reference to your token or chat_id before uploading.

dtb.log

dtgbot writes its messages to a log file which is defined in /etc/profile.d/DomoticzData.sh and exported in the variable BotLuaLog. By default BotLuaLog is pointing to /var/tmp/dtb.log.

dtb.log.errors

If dtgbot crashes, then errors will be written to this file which is defined by the enivronment variable BotLuaLog with .errors added. Please include contents in forum post if dtgbot has crashed.

dtgbot.pid

dtgbot will exit if its process id file (pid file) does not exisit, this is created by the service when dtgbot is started in /var/run/dtgbot.pid. The dtgbot.lua script will not stop until up to 60 seconds after the service is stopped due to the delayed nature of http getUpdates request.

Clearing Messages that Cause Errors

dtgbot will not re-read a message which it has already acted on and it will now skip a message which causes a crash after a certain stage, however if it crashes in peculiar circumstances then it could keep re-reading the message that caused it to crash, to overcome this you need to set the TelegramBotOffset variable in Domoticz to 1 higher than the offending update id. So for example with this extract from a crashing log: TBOidx 1

JSON request <http://10.10.10.127:8080/json.htm?type=command&param=getuservariable&idx=1>
Decoded 277849972
TBO 277849972
https://api.telegram.org/botkeykeykeykey/
.Message: 1
update_id    277849972
Battery
Handle command function started with Battery and IDIDIDIDI
JSON request <http://10.10.10.127:8080/json.htm?type=command&param=getuservariables>

You should reset TelegramBotOffset in Domoticz to 277849973.

How to Read dtb.log.errors and Overcome Configuration Errors

There is one reason why dtb.log.errors is created, the dtgbot has encountered an error, but this can be due to two causes: 1/ a fundamnetal error in the script. 2/ your Domoticz set-up being outside the range that dtgbot expects.

1 you probably need help over, however if you have good Lua skills then please deal with 1 and let the maintainers know, but most of the time please raise this on the forum or as an issue on github - https://github.com/steps39/dtgbot/issues.

2 you may be able to quickly overcome by reading dtb.log.errors and checking the scripts referred to for clues, for example:

/usr/bin/lua5.2: /home/pi/dtgbot//dtg_domoticz.lua:125: attempt to get length of global 'result' (a nil value)

stack traceback:

  /home/pi/dtgbot//dtg_domoticz.lua:125: in function 'device_list_names_idxs'
  /home/pi/dtgbot/dtgbot.lua:175: in function 'dtgbot_initialise'
  /home/pi/dtgbot/dtgbot.lua:229: in main chunk
  [C]: in ?

To read dtb.log.errors, start at the top and work down, it tells you that the dtgbot stopped because of an error at line 125 in /home/pi/dtgbot/lua/dtg_domoticz.lua, so looking at line 125:

   for i = 1, #result do

The error said result had a nil value, nil means nothing in Lua, so it looks like result should contain something and doesn't.

Not obvious, so have a look at the comment for the function this line is in, device_list_names_idxs, a bit further up the code where the function is first defined:

-- returns a list of Domoticz items based on type i.e. devices or scenes
function device_list_names_idxs(DeviceType)

Still not obvious but does refer to dtgbot interacting with Domoticz, i.e. getting the list from Domoticz, so what list hasn't Domoticz returned?

Now have a look at the next location - /home/pi/dtgbot/dtgbot.lua:175:

  Roomlist = device_list_names_idxs("plans")

Ok now possibly makes a bit more sense, refers to plans plus room, so it is trying to retrieve roomplans from Domoticz, in this case there were no roomplans defined.

So try to define a roomplan and restart dtgbot, it works so you are back in business.

However, dtgbot should really have coped with this situation. So please now show how smart you are by raising this on the forum and/or as an issue on github - https://github.com/steps39/dtgbot/issues, as with your troubleshooting it will far easier to fix as the maintainers all had rooms defined anyway.

You will find this example as the first issue on github, so it will be solved in a future release.