Coupling IFTTT to Domoticz by eMail
Posted: Tuesday 23 September 2014 22:29
Past few weeks I have gained experience with IFTTT (If This Than That at https://ifttt.com/dashboard). I was interested to be able to connect IFTTT to Domoticz. My first experiment was to make use of the locations services of IFTTT (on my iPad). So when I coming home or leaving home, I want to toggle a switch to On or Off in Domoticz.
To be able to couple the location services of IFTTT to a Domoticz switch, I have make use of GMail messages.
So, when I am coming home, a recipe of IFTTT sent a message to my GMail address with in the subject the switch name ('Not at Home'). The body of the message does not matter but I start it with 'Off'. After that, a python script read all messages of the mailbox till the script find a subject with a corresponding switch name in Domoticz. In this case when the script find the message with the subject 'Not at Home' the script set my switch in Domoticz to 'Off'. Now the message will be deleted from the mail box. (When something was going wrong, the message will not be deleted!)
When I am leaving home, a recipe of IFTTT sent a message to my GMail address with in the subject the switch name ('Not at Home'). The body of the message must start it with 'On'. After that, a python script set my switch in Domoticz to 'On'. Now the message will be deleted from the mail box. (When something was going wrong, the message will not be deleted!)
When you want to implement the same, follow next steps:
1.
I have used the following two recipes:
- https://ifttt.com/recipes/133585-let-my ... cipeiseasy (For the area I used my home address; Recipe Title = Email my when I'am coming home!; To address = <mail address for homeautomation purposes>@gmail.com; Subject = Not at Home; Body = Off<br>{{OccurredAt}}<br>via iOS Location {{LocationMapUrl}})
- https://ifttt.com/recipes/132927-email- ... -from-work (For the area I used my home address; Recipe Title = Email my when I'am leaving home!; To address = <mail address for homeautomation purposes>@gmail.com; Subject = Not at Home; Body = On<br>{{OccurredAt}}<br>via iOS Location {{LocationMapUrl}})
2.
I made a bash file which runs every 5 minutes from a cron job (sudo crontab -e):
*/5 * * * * /home/pi/script/gmail.sh
The bash file looks like (gmail.sh):
3. In step 2 you see I make use of a python (v2.7) script. Hopefully, you can install python by your own.
In the script (gmail.py) you must change the constants IMAP_USERNAME and IMAP_PASSWORD with your own mail address and password! (For some people also the constant DOMOTICZ_HOST must be changed!)
PS. If you have suggestions to improve the source code above, don't hesitate!
4. Make a dummy switch in Domoticz with the name 'Not at Home'. (Must be the same name as the subjects in above recipes! So, when you choose another name, change also the subjects in your recipes.)
5. After this, you are ready to test above. I have used a new GMail address to separate my personal email from the home automation email. Hopefully it works! If you likes above, maybe you will sent me a personal message or response!
To be able to couple the location services of IFTTT to a Domoticz switch, I have make use of GMail messages.
So, when I am coming home, a recipe of IFTTT sent a message to my GMail address with in the subject the switch name ('Not at Home'). The body of the message does not matter but I start it with 'Off'. After that, a python script read all messages of the mailbox till the script find a subject with a corresponding switch name in Domoticz. In this case when the script find the message with the subject 'Not at Home' the script set my switch in Domoticz to 'Off'. Now the message will be deleted from the mail box. (When something was going wrong, the message will not be deleted!)
When I am leaving home, a recipe of IFTTT sent a message to my GMail address with in the subject the switch name ('Not at Home'). The body of the message must start it with 'On'. After that, a python script set my switch in Domoticz to 'On'. Now the message will be deleted from the mail box. (When something was going wrong, the message will not be deleted!)
When you want to implement the same, follow next steps:
1.
I have used the following two recipes:
- https://ifttt.com/recipes/133585-let-my ... cipeiseasy (For the area I used my home address; Recipe Title = Email my when I'am coming home!; To address = <mail address for homeautomation purposes>@gmail.com; Subject = Not at Home; Body = Off<br>{{OccurredAt}}<br>via iOS Location {{LocationMapUrl}})
- https://ifttt.com/recipes/132927-email- ... -from-work (For the area I used my home address; Recipe Title = Email my when I'am leaving home!; To address = <mail address for homeautomation purposes>@gmail.com; Subject = Not at Home; Body = On<br>{{OccurredAt}}<br>via iOS Location {{LocationMapUrl}})
2.
I made a bash file which runs every 5 minutes from a cron job (sudo crontab -e):
*/5 * * * * /home/pi/script/gmail.sh
The bash file looks like (gmail.sh):
Code: Select all
#!/bin/bash
#
#$HOME/script/gmail.py --verbose >> /tmp/gmail.log &
sudo python /home/pi/script/gmail.py >> /tmp/gmail.log &
In the script (gmail.py) you must change the constants IMAP_USERNAME and IMAP_PASSWORD with your own mail address and password! (For some people also the constant DOMOTICZ_HOST must be changed!)
Code: Select all
#!/usr/bin/python
import sys
import argparse
import json
import httplib
from datetime import datetime
import imaplib
import time
import email
__author__ = 'Jan N'
__version__ = '0.1'
DOMOTICZ_HOST = '127.0.0.1:8080'
IMAP_USERNAME = '<your mail address>@gmail.com'
IMAP_PASSWORD = '<your password>'
IMAP_SERVER = 'imap.gmail.com'
IMAP_PORT = '993'
IMAP_USE_SSL = True
def cli_options():
cli_args = {}
cli_args['verbose'] = True
# Parse the CLI
parser = argparse.ArgumentParser()
parser.add_argument('--verbose', help='Verbose mode', action='store_true', default=False)
# Parse arguments and die if error
try:
args = parser.parse_args()
except Exception:
sys.exit(2)
if args.verbose:
cli_args['verbose'] = args.verbose
return (cli_args)
def is_number(val):
try:
float(val)
return True
except ValueError:
return False
def date_time():
return datetime.now().strftime('%Y/%m/%d %H:%M:%S')
# This class is derived from Pymoticz, modified to use httplib
class Domoticz:
def __init__(self, domoticz_host=DOMOTICZ_HOST): # Default on localhost
self.host = domoticz_host
def _request(self, url):
(ip, port) = self.host.split(":")
http = httplib.HTTPConnection(ip, port, timeout=2)
http.request("GET", url)
result = http.getresponse()
if (result.status != 200):
raise Exception
http.close()
return json.loads(result.read())
def list(self):
url='/json.htm?type=devices&used=true&order=Name'
return self._request(url)
def turn_on(self, _id):
url='/json.htm?type=command¶m=switchlight&idx=%s&switchcmd=On' % (_id)
return self._request(url)
def turn_off(self, _id):
url='/json.htm?type=command¶m=switchlight&idx=%s&switchcmd=Off' % (_id)
return self._request(url)
def turn_on_if_off(self, _id):
status=False
if (self.get_switch_status(_id) == "Off"):
self.turn_on(_id)
status=True
return status
def turn_off_if_on(self, _id):
status=False
if (self.get_switch_status(_id) == "On"):
self.turn_off(_id)
status=True
return status
def turn_on_off(self, _id, _state):
url='/json.htm?type=command¶m=switchlight&idx=%s&switchcmd=%s' % (_id, _state)
return self._request(url)
def get_switch_status(self, _id):
try:
device = self.get_device_by_idx(_id)
except:
return None
return device['Status']
def get_device_by_idx(self, _id):
url='/json.htm?type=devices&rid=%s' % (_id)
try:
device = self._request(url)
except:
return None
return device['result'][0]
def get_device_by_name(self, _name):
devices = self.list()['result']
for item in devices:
if (item['Name'] == _name):
return item
return None
# This class is derived from cgoldberg
"""MailBox class for processing IMAP email.
(To use with Gmail: enable IMAP access in your Google account settings)
usage with GMail:
import mailbox
with mailbox.MailBox(gmail_username, gmail_password) as mbox:
print mbox.get_count()
print mbox.print_msgs()
for other IMAP servers, adjust settings as necessary.
"""
class MailBox(object):
def __init__(self, user, password):
self.user = user
self.password = password
if IMAP_USE_SSL:
self.imap = imaplib.IMAP4_SSL(IMAP_SERVER, IMAP_PORT)
else:
self.imap = imaplib.IMAP4(IMAP_SERVER, IMAP_PORT)
def __enter__(self):
self.imap.login(self.user, self.password)
return self
def __exit__(self, type, value, traceback): # @ReservedAssignment
self.imap.close()
self.imap.logout()
def get_count(self):
self.imap.select('Inbox')
status, data = self.imap.search(None, 'ALL') # @UnusedVariable
return sum(1 for num in data[0].split()) # @UnusedVariable
def fetch_message(self, num):
self.imap.select('Inbox')
status, data = self.imap.fetch(str(num), '(RFC822)') # @UnusedVariable
email_msg = email.message_from_string(data[0][1])
return email_msg
def delete_message(self, num):
self.imap.select('Inbox')
self.imap.store(num, '+FLAGS', r'\Deleted')
self.imap.expunge()
def delete_all(self):
self.imap.select('Inbox')
status, data = self.imap.search(None, 'ALL') # @UnusedVariable
for num in data[0].split():
self.imap.store(num, '+FLAGS', r'\Deleted')
self.imap.expunge()
def print_msgs(self):
self.imap.select('Inbox')
status, data = self.imap.search(None, 'ALL') # @UnusedVariable
for num in reversed(data[0].split()):
status, data = self.imap.fetch(num, '(RFC822)') # @UnusedVariable
print 'Message %s\n%s\n' % (num, data[0][1])
def get_latest_email_sent_to(self, email_address, timeout=300, poll=1):
start_time = time.time()
while ((time.time() - start_time) < timeout):
# It's no use continuing until we've successfully selected
# the inbox. And if we don't select it on each iteration
# before searching, we get intermittent failures.
status, data = self.imap.select('Inbox')
if status != 'OK':
time.sleep(poll)
continue
status, data = self.imap.search(None, 'TO', email_address)
data = [d for d in data if d is not None]
if status == 'OK' and data:
for num in reversed(data[0].split()):
status, data = self.imap.fetch(num, '(RFC822)')
email_msg = email.message_from_string(data[0][1])
return email_msg
time.sleep(poll)
raise AssertionError("No email sent to '%s' found in inbox "
"after polling for %s seconds." % (email_address, timeout))
def delete_msgs_sent_to(self, email_address):
self.imap.select('Inbox')
status, data = self.imap.search(None, 'TO', email_address)
if status == 'OK':
for num in reversed(data[0].split()):
status, data = self.imap.fetch(num, '(RFC822)')
self.imap.store(num, '+FLAGS', r'\Deleted')
self.imap.expunge()
def main():
# Parse the CLI options
(cli_parms) = cli_options()
# Print an empty line and date in verbose mode
if cli_parms['verbose']:
print ""
# Get instance of Domoticz class
d = Domoticz()
imap_username = IMAP_USERNAME
imap_password = IMAP_PASSWORD
with MailBox(imap_username, imap_password) as mbox:
messages = mbox.get_count()
# if (messages > 0):
# if cli_parms['verbose']:
# print "{0} DEBUG: #Messages = {1}".format(date_time(), messages)
# print "{0} DEBUG:".format(date_time())
# print mbox.print_msgs()
counter = 1
while (counter <= messages):
msg = mbox.fetch_message(counter)
varSubject = msg['subject']
# Set subject to Switch Name
switch_name = varSubject
# Get Switch Idx of switch from Domoticz
switch_idx = 0
if (switch_name != ""):
# Print Switch Name (= subject)
if cli_parms['verbose']:
print "{0} DEBUG: Check Switch Name: {1}".format(date_time(), switch_name),
# Get Switch Idx by Switch Name
try:
switch = d.get_device_by_name(switch_name)
# Check if switch exists
if (switch is not None):
# Get Switch Idx
switch_idx = switch['idx']
# Print Switch Idx
if cli_parms['verbose']:
print "-> Switch Idx = {0}".format(switch_idx)
# Check if message body starts with 'On'
turn_on = False
if isinstance(msg.get_payload(), list):
for eachPayload in msg.get_payload():
body = eachPayload.get_payload()
# Checks body starts with 'On'
if body.startswith('On'):
turn_on = True
break # Exists for loop
else: # There is only a text/plain part
body = msg.get_payload()
if body.startswith('On'):
turn_on = True
if (turn_on):
# Set switch to On
if (switch_idx != 0) and d.turn_on_if_off(switch_idx) and cli_parms['verbose']: # Turn switch On only if Off
print "{0} DEBUG: Switching {1}: {2}".format(date_time(), "On", switch_name)
else:
# Set switch to Off
if (switch_idx != 0) and d.turn_off_if_on(switch_idx) and cli_parms['verbose']: # Turn switch Off only if On
print "{0} DEBUG: Switching {1}: {2}".format(date_time(), "Off", switch_name)
# Delete message if switch exists
mbox.delete_message(counter)
# Switch does not exists
else:
if cli_parms['verbose']:
print "-> Switch does not exists"
except:
if cli_parms['verbose']:
print "-> List raises an exception"
counter += 1
if __name__ == "__main__":
main()
4. Make a dummy switch in Domoticz with the name 'Not at Home'. (Must be the same name as the subjects in above recipes! So, when you choose another name, change also the subjects in your recipes.)
5. After this, you are ready to test above. I have used a new GMail address to separate my personal email from the home automation email. Hopefully it works! If you likes above, maybe you will sent me a personal message or response!