]> www.vanbest.org Git - motioneye-debian/commitdiff
implemented email notifications
authorCalin Crisan <ccrisan@gmail.com>
Wed, 13 Aug 2014 19:06:11 +0000 (22:06 +0300)
committerCalin Crisan <ccrisan@gmail.com>
Wed, 13 Aug 2014 19:06:11 +0000 (22:06 +0300)
sendmail.py [changed mode: 0644->0755]
settings_default.py
src/config.py
src/tzctl.py
static/js/main.js
templates/main.html

old mode 100644 (file)
new mode 100755 (executable)
index b09acf0..3216867
@@ -16,6 +16,8 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>. 
 
+import datetime
+import logging
 import os
 import smtplib
 import socket
@@ -25,16 +27,33 @@ from email.mime.text import MIMEText
 
 import settings
 
+from motioneye import _configure_settings, _configure_logging
 
-def send_mail(host, port, username, password, tls, to, subject, message):
-    conn = smtplib.SMTP(host, port, timeout=getattr(settings, 'SMTP_TIMEOUT', 60))
+_configure_settings()
+_configure_logging()
+
+import config
+import tzctl
+
+
+messages = {
+    'motion_start': 'Motion has been detected by camera "%(camera)s/%(hostname)s" at %(moment)s (%(timezone)s).'
+}
+
+subjects = {
+    'motion_start': 'motionEye: motion detected by "%(camera)s"'
+}
+
+
+def send_mail(server, port, account, password, tls, to, subject, message):
+    conn = smtplib.SMTP(server, port, timeout=getattr(settings, 'SMTP_TIMEOUT', 60))
     if tls:
         conn.starttls()
     
-    if username and password:
-        conn.login(username, password)
+    if account and password:
+        conn.login(account, password)
     
-    _from = username or 'motioneye@' + socket.gethostname()
+    _from = account or 'motioneye@' + socket.gethostname()
     
     email = MIMEText(message)
     email['Subject'] = subject
@@ -45,25 +64,57 @@ def send_mail(host, port, username, password, tls, to, subject, message):
     conn.quit()
 
 
+def format_message(subject, message, camera_id, moment):
+    format_dict = {
+        'camera': config.get_camera(camera_id)['@name'],
+        'hostname': socket.gethostname(),
+        'moment': moment.strftime('%Y-%m-%d %H:%M:%S'),
+    }
+    
+    if settings.LOCAL_TIME_FILE:
+        format_dict['timezone'] = tzctl.get_time_zone()
+    
+    else:
+        format_dict['timezone'] = 'local time'
+
+    message = message % format_dict
+    subject = subject % format_dict
+    subject = subject.replace('\n', ' ')
+
+    message += '\n\n'
+    message += 'motionEye.'
+
+    return (subject, message)
+
+
 def print_usage():
-    print 'Usage: sendmail.py <to> <message>'
-    print 'Environment: HOST, PORT, USERNAME, PASSWORD, TLD'
+    print 'Usage: sendmail.py <server> <port> <account> <password> <tls> <to> <msg_id> <camera_id> <moment>'
 
 
 if __name__ == '__main__':
-    if len(sys.argv) < 4:
+    if len(sys.argv) < 10:
         print_usage()
         sys.exit(-1)
     
-    host = os.environ.get('SMTP_HOST', 'localhost')
-    port = int(os.environ.get('SMTP_PORT', '465')) 
-    username = os.environ.get('SMTP_USERNAME')
-    password = os.environ.get('SMTP_PASSWORD')
-    tls = os.environ.get('SMTP_TLS') == 'true' or True
-    to = sys.argv[1]
-    subject = sys.argv[2]
-    message = sys.argv[3]
+    server = sys.argv[1]
+    port = int(sys.argv[2]) 
+    account = sys.argv[3]
+    password = sys.argv[4]
+    tls = sys.argv[5].lower() == 'true'
+    to = sys.argv[6]
+    msg_id = sys.argv[7]
+    camera_id = sys.argv[8]
+    moment = sys.argv[9]
+    
+    message = messages.get(msg_id)
+    subject = subjects.get(msg_id)
+    if not message or not subject:
+        logging.error('unknown message id')
+        sys.exit(-1)
+    
+    moment = datetime.datetime.strptime(moment, '%Y-%m-%dT%H:%M:%S')
+    subject, message = format_message(subject, message, camera_id, moment)
     
-    send_mail(host, port, username, password, tls, to, subject, message)
+    send_mail(server, port, account, password, tls, to, subject, message)
     
-    print('message sent.')
+    logging.info('message sent.')
index 2faf74cd21d10f9eedd82605cfff70d63d6571bc..961738827f27e091399acf4602eea93ba34aff21 100644 (file)
@@ -67,3 +67,6 @@ LOCAL_TIME_FILE = None
 
 # enables rebooting after changing system settings (such as wifi settings or system updates)
 ENABLE_REBOOT = False
+
+# the timeout in seconds to use when talking to a SMTP server
+SMTP_TIMEOUT = 60
index 056b4331e711034d2dc93c95ef063a973bde7e58..d27371a964c6cfa19cee8c80ea616fdf706c1d9e 100644 (file)
@@ -593,14 +593,15 @@ def camera_ui_to_dict(ui):
         'pre_capture': int(ui['pre_capture']),
         'post_capture': int(ui['post_capture']),
         
-        # motion notifications
-        '@motion_notifications': ui['motion_notifications'],
-        '@motion_notifications_emails': ui['motion_notifications_emails'],
-        
         # working schedule
-        '@working_schedule': ''
+        '@working_schedule': '',
+    
+        # events
+        'on_event_start': ''
     }
     
+    on_event_start = []
+    
     if 'brightness' in ui:
         if int(ui['brightness']) == 50:
             data['brightness'] = 0
@@ -693,6 +694,21 @@ def camera_ui_to_dict(ui):
         max_val = min(max_val, 9999999)
         
         data['ffmpeg_bps'] = int(ui['movie_quality']) * max_val / 100
+    
+    if ui['motion_notifications']:
+        send_mail_path = os.path.join(settings.PROJECT_PATH, 'sendmail.py')
+        send_mail_path = os.path.abspath(send_mail_path)
+        
+        emails = re.sub('\\s', '', ui['motion_notifications_emails'])
+        
+        on_event_start.append('%(script)s %(server)s %(port)s %(account)s %(password)s %(tls)s %(to)s motion_start %%t %%Y-%%m-%%dT%%H:%%M:%%S' % {
+                'script': send_mail_path,
+                'server': ui['smtp_server'],
+                'port': ui['smtp_port'],
+                'account': ui['smtp_account'],
+                'password': ui['smtp_password'],
+                'tls': ui['smtp_tls'],
+                'to': emails})
 
     if ui['working_schedule']:
         data['@working_schedule'] = (
@@ -703,6 +719,9 @@ def camera_ui_to_dict(ui):
                 ui['friday_from'] + '-' + ui['friday_to'] + '|' + 
                 ui['saturday_from'] + '-' + ui['saturday_to'] + '|' + 
                 ui['sunday_from'] + '-' + ui['sunday_to'])
+    
+    if on_event_start:
+        data['on_event_start'] = '; '.join(on_event_start)
 
     return data
 
@@ -785,10 +804,6 @@ def camera_dict_to_ui(data):
         'pre_capture': int(data['pre_capture']),
         'post_capture': int(data['post_capture']),
         
-        # motion notifications
-        'motion_notifications': data['@motion_notifications'],
-        'motion_notifications_emails': data['@motion_notifications_emails'],
-        
         # working schedule
         'working_schedule': False,
         'monday_from': '09:00', 'monday_to': '17:00',
@@ -800,6 +815,10 @@ def camera_dict_to_ui(data):
         'sunday_from': '09:00', 'sunday_to': '17:00'
     }
 
+    on_event_start = data.get('on_event_start') or []
+    if on_event_start:
+        on_event_start = [e.strip() for e in on_event_start.split(';')]
+
     # the brightness & co. keys in the ui dictionary
     # indicate the presence of these controls
     # we must call v4l2ctl functions to determine the available controls    
@@ -910,8 +929,24 @@ def camera_dict_to_ui(data):
         max_val = data['width'] * data['height'] * data['framerate'] / 3
         max_val = min(max_val, 9999999)
         
-        ui['movie_quality'] = min(100, int(round(ffmpeg_bps * 100.0 / max_val))) 
+        ui['movie_quality'] = min(100, int(round(ffmpeg_bps * 100.0 / max_val)))
     
+    for e in on_event_start:
+        if e.count('sendmail.py') and e.count('motion_start'):
+            e = e.split(' ')
+            if len(e) != 10:
+                continue
+
+            ui['motion_notifications'] = True 
+            ui['smtp_server'] = e[1]
+            ui['smtp_port'] = e[2]
+            ui['smtp_account'] = e[3]
+            ui['smtp_password'] = e[4]
+            ui['smtp_tls'] = e[5].lower() == 'true'
+            ui['motion_notifications_emails'] = e[6]
+
+            break
+
     working_schedule = data['@working_schedule']
     if working_schedule:
         days = working_schedule.split('|')
@@ -1210,11 +1245,10 @@ def _set_default_motion_camera(camera_id, data, old_motion):
     data.setdefault('ffmpeg_video_codec', 'msmpeg4')
     data.setdefault('@preserve_movies', 0)
     
-    data.setdefault('@motion_notifications', False)
-    data.setdefault('@motion_notifications_emails', '')
-    
     data.setdefault('@working_schedule', '')
 
+    data.setdefault('on_event_start', '')
+
 
 def _get_wifi_settings(data):
     wifi_settings = wifictl.get_wifi_settings()
index 6e19891050cbcaaf8acadda00b2e658563b9290d..fbd04e070b07be965ee29e43b10587bf94274ef1 100644 (file)
@@ -24,6 +24,8 @@ import subprocess
 
 def _get_time_zone_symlink():
     file = settings.LOCAL_TIME_FILE
+    if not file:
+        return None
     
     for i in xrange(8): # recursively follow the symlinks @UnusedVariable
         try:
@@ -46,6 +48,9 @@ def _get_time_zone_symlink():
 
 
 def _get_time_zone_md5():
+    if settings.LOCAL_TIME_FILE:
+        return None
+
     try:
         output = subprocess.check_output('cd /usr/share/zoneinfo; find * -type f | xargs md5sum', shell=True)
 
index 4beaaf475f9029c097a287c2d40b84cc66532077..19c46c016fcd291aad83f7fc71a4fa03f0a81e3f 100644 (file)
@@ -214,6 +214,9 @@ function initUI() {
     makeTextValidator($('#imageFileNameEntry'), true);
     makeTextValidator($('#movieFileNameEntry'), true);
     makeTextValidator($('#emailAddressesEntry'), true);
+    makeTextValidator($('#smtpServerEntry'), true);
+    makeTextValidator($('#smtpAccountEntry'), true);
+    makeTextValidator($('#smtpPasswordEntry'), true);
     
     /* number validators */
     makeNumberValidator($('#streamingPortEntry'), 1024, 65535, false, false, true);
@@ -223,6 +226,7 @@ function initUI() {
     makeNumberValidator($('#eventGapEntry'), 1, 86400, false, false, true);
     makeNumberValidator($('#preCaptureEntry'), 0, 100, false, false, true);
     makeNumberValidator($('#postCaptureEntry'), 0, 100, false, false, true);
+    makeNumberValidator($('#smtpPortEntry'), 1, 65535, false, false, true);
     
     /* time validators */
     makeTimeValidator($('#mondayFrom'));
@@ -243,6 +247,7 @@ function initUI() {
     /* ui elements that enable/disable other ui elements */
     $('#motionEyeSwitch').change(updateConfigUi);
     $('#showAdvancedSwitch').change(updateConfigUi);
+    $('#wifiSwitch').change(updateConfigUi);
     $('#storageDeviceSelect').change(updateConfigUi);
     $('#autoBrightnessSwitch').change(updateConfigUi);
     $('#resolutionSelect').change(updateConfigUi);
@@ -260,7 +265,6 @@ function initUI() {
     $('#preserveMoviesSelect').change(updateConfigUi);
     $('#motionNotificationsSwitch').change(updateConfigUi);
     $('#workingScheduleSwitch').change(updateConfigUi);
-    $('#wifiSwitch').change(updateConfigUi);
     
     $('#storageDeviceSelect').change(function () {
         $('#rootDirectoryEntry').val('/');
@@ -649,6 +653,11 @@ function cameraUi2Dict() {
         /* motion notifications */
         'motion_notifications': $('#motionNotificationsSwitch')[0].checked,
         'motion_notifications_emails': $('#emailAddressesEntry').val(),
+        'smtp_server': $('#smtpServerEntry').val(),
+        'smtp_port': $('#smtpPortEntry').val(),
+        'smtp_account': $('#smtpAccountEntry').val(),
+        'smtp_password': $('#smtpPasswordEntry').val(),
+        'smtp_tls': $('#smtpTlsSwitch')[0].checked,
         
         /* working schedule */
         'working_schedule': $('#workingScheduleSwitch')[0].checked,
@@ -820,7 +829,12 @@ function dict2CameraUi(dict) {
     /* motion notifications */
     $('#motionNotificationsSwitch')[0].checked = dict['motion_notifications'];
     $('#emailAddressesEntry').val(dict['motion_notifications_emails']);
-    
+    $('#smtpServerEntry').val(dict['smtp_server']);
+    $('#smtpPortEntry').val(dict['smtp_port']);
+    $('#smtpAccountEntry').val(dict['smtp_account']);
+    $('#smtpPasswordEntry').val(dict['smtp_password']);
+    $('#smtpTlsSwitch')[0].checked = dict['smtp_tls'];
+
     /* working schedule */
     $('#workingScheduleSwitch')[0].checked = dict['working_schedule'];
     $('#mondayFrom').val(dict['monday_from']);
index 17985582da06826f0eaa9545fdc82152383dfd78..61f38f8f779740833d5044f9918809b0cfc3d20b 100644 (file)
                     </tr>
                 </table>
                 
-                <div class="settings-section-title hidden"><input type="checkbox" class="styled section notifications" id="motionNotificationsSwitch">Motion Notifications</div>
+                <div class="settings-section-title advanced-setting"><input type="checkbox" class="styled section notifications" id="motionNotificationsSwitch">Motion Notifications</div>
                 <table class="settings">
-                    <tr class="settings-item hidden">
+                    <tr class="settings-item advanced-setting">
                         <td class="settings-item-label"><span class="settings-item-label">Email Addresses</span></td>
                         <td class="settings-item-value"><input type="text" class="styled notifications" id="emailAddressesEntry" placeholder="email addresses..."></td>
-                        <td><span class="help-mark" title="email addresses (separated by comma) that are added here will receive notifications whenever a motion event is detected (leave empty to disable email notifications)">?</span></td>
+                        <td><span class="help-mark" title="email addresses (separated by comma) that are added here will receive notifications whenever a motion event is detected">?</span></td>
+                    </tr>
+                    <tr class="settings-item advanced-setting">
+                        <td colspan="100"><div class="settings-item-separator"></div></td>
+                    </tr>
+                    <tr class="settings-item advanced-setting">
+                        <td class="settings-item-label"><span class="settings-item-label">SMTP Server</span></td>
+                        <td class="settings-item-value"><input type="text" class="styled notifications" id="smtpServerEntry" placeholder="e.g. smtp.gmail.com"></td>
+                        <td><span class="help-mark" title="enter the hostname or IP address of your SMTP server (for Gmail use smtp.gmail.com)">?</span></td>
+                    </tr>
+                    <tr class="settings-item advanced-setting">
+                        <td class="settings-item-label"><span class="settings-item-label">SMTP Port</span></td>
+                        <td class="settings-item-value"><input type="text" class="styled number notifications" id="smtpPortEntry" placeholder="e.g. 587"></td>
+                        <td><span class="help-mark" title="enter the port used by your SMTP server (usually 465 for non-TLS connections and 587 for TLS connections)">?</span></td>
+                    </tr>
+                    <tr class="settings-item advanced-setting">
+                        <td class="settings-item-label"><span class="settings-item-label">SMTP Account</span></td>
+                        <td class="settings-item-value"><input type="text" class="styled notifications" id="smtpAccountEntry" placeholder="account@gmail.com..."></td>
+                        <td><span class="help-mark" title="enter your SMTP account (normally your email address)">?</span></td>
+                    </tr>
+                    <tr class="settings-item advanced-setting">
+                        <td class="settings-item-label"><span class="settings-item-label">SMTP Password</span></td>
+                        <td class="settings-item-value"><input type="password" class="styled notifications" id="smtpPasswordEntry"></td>
+                        <td><span class="help-mark" title="enter your SMTP account password (for Gmail use your Google password or an app-specific generated password)">?</span></td>
+                    </tr>
+                    <tr class="settings-item advanced-setting">
+                        <td class="settings-item-label"><span class="settings-item-label">Use TLS</span></td>
+                        <td class="settings-item-value"><input type="checkbox" class="styled notifications" id="smtpTlsSwitch"></td>
+                        <td><span class="help-mark" title="enable this if your SMTP server requires TLS (Gmail works either way)">?</span></td>
                     </tr>
                 </table>