import sys import time import logging import threading import RPi.GPIO as GPIO import requests import smtplib import json from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from time import localtime, strftime from six.moves.configparser import ConfigParser try: import tweepy from tweepy import OAuthHandler as TweetHandler except ModuleNotFoundError: TweetHandler = None try: import paho.mqtt.publish as mqttpublish except ModuleNotFoundError: mqttpublish = None try: from slackclient import SlackClient except ModuleNotFoundError: SlackClient = None PUSHOVER_SOUNDS = None def email(msg): try: message = MIMEMultipart('related') message['Subject'] = msg message['From'] = email_sender message['To'] = email_recipient message.preamble = 'This is a multi=part message in MIME format.' message_alternative = MIMEMultipart('alternative') message.attach(message_alternative) message_text = MIMEText(msg + '\n') message_text = MIMEText('

' + msg + '

') message_text.replace_header('Content-Type', 'text/html') message_alternative.attach(message_text) if email_ssl is True: s = smtplib.SMTP_SSL(email_server, email_port) else: s = smtplib.SMTP(email_server, email_port) s.starttls() s.login(email_sender, email_password) s.sendmail(email_sender, email_recipient, message.as_string()) s.quit() except (KeyboardInterrupt, SystemExit): raise except: pass def mqtt(msg): try: mqtt_auth = None if len(mqtt_username) > 0: mqtt_auth = { 'username': mqtt_username, 'password': mqtt_password } mqttpublish.single(mqtt_topic, msg, qos=0, retain=False, hostname=mqtt_hostname, port=int(mqtt_port), client_id=mqtt_clientid, keepalive=60, will=None, auth=mqtt_auth, tls=None) except (KeyboardInterrupt, SystemExit): raise except: pass def pushbullet(cfg, msg): try: data_send = {"type": "note", "body": msg} requests.post( 'https://api.pushbullet.com/v2/pushes', data=json.dumps(data_send), headers={'Authorization': 'Bearer ' + cfg, 'Content-Type': 'application/json'}) except (KeyboardInterrupt, SystemExit): raise except: pass def get_pushoversounds(app_key): global PUSHOVER_SOUNDS if not PUSHOVER_SOUNDS: url_data = "https://api.pushover.net/1/sounds.json?token={}" .format(app_key) try: r = requests.get(url_data) json_data = r.json() PUSHOVER_SOUNDS = json_data['sounds'] except (KeyboardInterrupt, SystemExit): raise except: pass def pushover(user_key, app_key, msg, device='', sound=''): global PUSHOVER_SOUNDS if not PUSHOVER_SOUNDS: get_pushoversounds(app_key) data_send = {"user": user_key, "token": app_key, "message": msg} if device: data_send['device'] = device if sound in PUSHOVER_SOUNDS: data_send['sound'] = sound try: requests.post( 'https://api.pushover.net/1/messages.json', data=json.dumps(data_send), headers={'Content-Type': 'application/json'}) except (KeyboardInterrupt, SystemExit): raise except: pass def iftt(msg): try: iftt_url = "https://maker.ifttt.com/trigger/{}/with/key/{}".format(iftt_maker_channel_event, iftt_maker_channel_key) report = {"value1" : msg} resp = requests.post(iftt_url, data=report) except (KeyboardInterrupt, SystemExit): raise except: pass def slack_webhook(msg): try: requests.post(slack_webhook_url, json={'text': msg}, headers={"Content-type": "application/json"}) except (KeyboardInterrupt, SystemExit): raise except: pass def tweet(msg): try: # Twitter is the only API that NEEDS something like a timestamp, # since it will reject identical tweets. tweet = msg + ' ' + strftime("%Y-%m-%d %H:%M:%S", localtime()) auth = TweetHandler(twitter_api_key, twitter_api_secret) auth.set_access_token(twitter_access_token, twitter_access_token_secret) tweepy.API(auth).update_status(status=tweet) except (KeyboardInterrupt, SystemExit): raise except: pass def slack(msg): try: slack = msg + ' ' + strftime("%Y-%m-%d %H:%M:%S", localtime()) sc = SlackClient(slack_api_token) sc.api_call( 'chat.postMessage', channel='#random', text=slack) except (KeyboardInterrupt, SystemExit): raise except: pass def telegram(msg): try: telegram_url = "https://api.telegram.org/bot{}/sendMessage?chat_id={}&text={}".format(telegram_api_token, telegram_user_id, msg) resp = requests.post(telegram_url) except (KeyboardInterrupt, SystemExit): raise except: pass def discord(msg): try: resp = requests.post(discord_webhook_url, json={"content": msg}, params={"wait": True}) resp.raise_for_status() except (KeyboardInterrupt, SystemExit): raise except Exception as e: logging.warning("Failed to send to Discord: %s", e) def send_alert(message): if len(message) > 1: logging.info(message) if len(pushover_user_key) > 0 and len(pushover_app_key) > 0: pushover(pushover_user_key, pushover_app_key, message, pushover_device, pushover_sound) if len(pushbullet_api_key) > 0: pushbullet(pushbullet_api_key, message) if len(pushbullet_api_key2) > 0: pushbullet(pushbullet_api_key2, message) if len(twitter_api_key) > 0: tweet(message) if len(slack_api_token) > 0: slack(message) if len (slack_webhook_url) > 0: slack_webhook(message) if len(iftt_maker_channel_key) > 0: iftt(message) if len(mqtt_topic) > 0: mqtt(message) if len(email_recipient) > 0: email(message) if len(telegram_api_token) > 0 and len(telegram_user_id) > 0: telegram(message) if len(discord_webhook_url) > 0: discord(message) def send_appliance_active_message(): send_alert(start_message) global appliance_active appliance_active = True def send_appliance_inactive_message(): send_alert(end_message) global appliance_active appliance_active = False def vibrated(x): global vibrating global last_vibration_time global start_vibration_time logging.debug('Vibrated') last_vibration_time = time.time() if not vibrating: start_vibration_time = last_vibration_time vibrating = True def heartbeat(): current_time = time.time() logging.debug("HB at {}".format(current_time)) global vibrating delta_vibration = last_vibration_time - start_vibration_time if (vibrating and delta_vibration > begin_seconds and not appliance_active): send_appliance_active_message() if (not vibrating and appliance_active and current_time - last_vibration_time > end_seconds): send_appliance_inactive_message() vibrating = current_time - last_vibration_time < 2 threading.Timer(1, heartbeat).start() logging.basicConfig(format='%(message)s', level=logging.INFO) if len(sys.argv) == 1: logging.critical("No config file specified") sys.exit(1) vibrating = False appliance_active = False last_vibration_time = time.time() start_vibration_time = last_vibration_time config = ConfigParser() config.read(sys.argv[1]) verbose = config.getboolean('main', 'VERBOSE') sensor_pin = config.getint('main', 'SENSOR_PIN') begin_seconds = config.getint('main', 'SECONDS_TO_START') end_seconds = config.getint('main', 'SECONDS_TO_END') pushbullet_api_key = config.get('pushbullet', 'API_KEY') pushover_user_key = config.get('pushover', 'user_api_key') pushover_app_key = config.get('pushover', 'app_api_key') pushover_device = config.get('pushover', 'device') pushover_sound = config.get('pushover', 'sound') mqtt_hostname = config.get('mqtt', 'mqtt_hostname') mqtt_port = config.get('mqtt', 'mqtt_port') mqtt_topic = config.get('mqtt', 'mqtt_topic') mqtt_username = config.get('mqtt', 'mqtt_username') mqtt_password = config.get('mqtt', 'mqtt_password') mqtt_clientid = config.get('mqtt', 'mqtt_clientid') if len(mqtt_topic) > 0 and mqttpublish is None: logging.critical("MQTT requested but not installed") sys.exit(1) pushbullet_api_key2 = config.get('pushbullet', 'API_KEY2') start_message = config.get('main', 'START_MESSAGE') end_message = config.get('main', 'END_MESSAGE') twitter_api_key = config.get('twitter', 'api_key') twitter_api_secret = config.get('twitter', 'api_secret') twitter_access_token = config.get('twitter', 'access_token') twitter_access_token_secret = config.get('twitter', 'access_token_secret') if len(twitter_api_key) > 0 and TweetHandler is None: logging.critical("Twitter requested but not installed") sys.exit(1) slack_api_token = config.get('slack', 'api_token') slack_webhook_url = config.get('slack','webhook_url') if (len(slack_api_token) > 0 or len(slack_webhook_url) > 0) and SlackClient is None: logging.critical("Slack requested but not installed") sys.exit(1) iftt_maker_channel_event = config.get('iftt','maker_channel_event') iftt_maker_channel_key = config.get('iftt','maker_channel_key') email_recipient = config.get('email', 'recipient') email_sender = config.get('email', 'sender') email_password = config.get('email', 'password') email_server = config.get('email', 'server') email_port = config.get('email', 'port') email_ssl = config.getboolean('email', 'ssl') telegram_api_token = config.get('telegram', 'telegram_api_token') telegram_user_id = config.get('telegram', 'telegram_user_id') discord_webhook_url = config.get('discord', 'discord_webhook_url') if verbose: logging.getLogger().setLevel(logging.DEBUG) send_alert(config.get('main', 'BOOT_MESSAGE')) GPIO.setwarnings(False) GPIO.setmode(GPIO.BCM) GPIO.setup(sensor_pin, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) GPIO.add_event_detect(sensor_pin, GPIO.RISING) GPIO.add_event_callback(sensor_pin, vibrated) logging.info('Running config file {} monitoring GPIO pin {}'\ .format(sys.argv[1], str(sensor_pin))) threading.Timer(1, heartbeat).start()