Example #1
0
def camera_img_acquire(image_type, camera_unique_id, max_age):
    """Capture an image and return the filename."""
    if not current_user.is_authenticated:
        return "You are not logged in and cannot access this endpoint"

    if image_type == 'new':
        tmp_filename = None
    elif image_type == 'tmp':
        tmp_filename = f'{camera_unique_id}_tmp.jpg'
    else:
        return
    path, filename = camera_record('photo', camera_unique_id, tmp_filename=tmp_filename)
    if not path and not filename:
        msg = "Could not acquire image."
        logger.error(msg)
        return msg
    else:
        image_path = os.path.join(path, filename)
        time_max_age = datetime.datetime.now() - datetime.timedelta(seconds=int(max_age))
        timestamp = os.path.getctime(image_path)
        if datetime.datetime.fromtimestamp(timestamp) > time_max_age:
            date_time = datetime.datetime.fromtimestamp(timestamp).strftime('%Y-%m-%d %H:%M:%S')
            return_values = f'["{filename}","{date_time}"]'
        else:
            return_values = '["max_age_exceeded"]'
        return Response(return_values, mimetype='text/json')
Example #2
0
    def run_action(self, message, dict_vars):
        try:
            controller_id = dict_vars["value"]["camera_id"]
        except:
            controller_id = self.controller_id

        camera = db_retrieve_table_daemon(
            Camera, unique_id=controller_id, entry='first')

        if not camera:
            msg = f" Error: Camera with ID '{controller_id}' not found."
            message += msg
            self.logger.error(msg)
            return message

        message += f" Capturing photo with camera {controller_id} ({camera.name})."

        path, filename = camera_record('photo', controller_id)
        if not path and not filename:
            msg = " Could not acquire image."
            self.logger.error(msg)
            message += msg

        self.logger.debug(f"Message: {message}")

        return message
Example #3
0
 def timelapse_check(self, camera, now):
     """ If time-lapses are active, take photo at predefined periods """
     try:
         if (camera.timelapse_started and now > camera.timelapse_end_time):
             with session_scope(MYCODO_DB_PATH) as new_session:
                 mod_camera = new_session.query(Camera).filter(
                     Camera.id == camera.id).first()
                 mod_camera.timelapse_started = False
                 mod_camera.timelapse_paused = False
                 mod_camera.timelapse_start_time = None
                 mod_camera.timelapse_end_time = None
                 mod_camera.timelapse_interval = None
                 mod_camera.timelapse_next_capture = None
                 mod_camera.timelapse_capture_number = None
                 new_session.commit()
             self.refresh_daemon_camera_settings()
             self.logger.debug(
                 "Camera {id}: End of time-lapse.".format(id=camera.id))
         elif ((camera.timelapse_started and not camera.timelapse_paused)
               and now > camera.timelapse_next_capture):
             # Ensure next capture is greater than now (in case of power failure/reboot)
             next_capture = camera.timelapse_next_capture
             capture_number = camera.timelapse_capture_number
             while now > next_capture:
                 # Update last capture and image number to latest before capture
                 next_capture += camera.timelapse_interval
                 capture_number += 1
             with session_scope(MYCODO_DB_PATH) as new_session:
                 mod_camera = new_session.query(Camera).filter(
                     Camera.id == camera.id).first()
                 mod_camera.timelapse_next_capture = next_capture
                 mod_camera.timelapse_capture_number = capture_number
                 new_session.commit()
             self.refresh_daemon_camera_settings()
             self.logger.debug(
                 "Camera {id}: Capturing time-lapse image".format(
                     id=camera.id))
             # Capture image
             camera_record('timelapse', camera.unique_id)
     except Exception as except_msg:
         message = "Could not execute timelapse:" \
                   " {err}".format(err=except_msg)
         self.logger.exception(message)
Example #4
0
 def timelapse_check(self, camera, now):
     """ If time-lapses are active, take photo at predefined periods """
     try:
         if (camera.timelapse_started and
                     now > camera.timelapse_end_time):
             with session_scope(MYCODO_DB_PATH) as new_session:
                 mod_camera = new_session.query(Camera).filter(
                     Camera.unique_id == camera.unique_id).first()
                 mod_camera.timelapse_started = False
                 mod_camera.timelapse_paused = False
                 mod_camera.timelapse_start_time = None
                 mod_camera.timelapse_end_time = None
                 mod_camera.timelapse_interval = None
                 mod_camera.timelapse_next_capture = None
                 mod_camera.timelapse_capture_number = None
                 new_session.commit()
             self.refresh_daemon_camera_settings()
             self.logger.debug(
                 "Camera {id}: End of time-lapse.".format(id=camera.id))
         elif ((camera.timelapse_started and not camera.timelapse_paused) and
                       now > camera.timelapse_next_capture):
             # Ensure next capture is greater than now (in case of power failure/reboot)
             next_capture = camera.timelapse_next_capture
             capture_number = camera.timelapse_capture_number
             while now > next_capture:
                 # Update last capture and image number to latest before capture
                 next_capture += camera.timelapse_interval
                 capture_number += 1
             with session_scope(MYCODO_DB_PATH) as new_session:
                 mod_camera = new_session.query(Camera).filter(
                     Camera.unique_id == camera.unique_id).first()
                 mod_camera.timelapse_next_capture = next_capture
                 mod_camera.timelapse_capture_number = capture_number
                 new_session.commit()
             self.refresh_daemon_camera_settings()
             self.logger.debug(
                 "Camera {id}: Capturing time-lapse image".format(id=camera.id))
             # Capture image
             camera_record('timelapse', camera.unique_id)
     except Exception as except_msg:
         message = "Could not execute timelapse:" \
                   " {err}".format(err=except_msg)
         self.logger.exception(message)
def action_photo(cond_action, message):
    this_camera = db_retrieve_table_daemon(
        Camera, unique_id=cond_action.do_unique_id, entry='first')
    message += "  Capturing photo with camera {unique_id} ({id}, {name}).".format(
        unique_id=cond_action.do_unique_id,
        id=this_camera.id,
        name=this_camera.name)
    camera_still = db_retrieve_table_daemon(
        Camera, unique_id=cond_action.do_unique_id)
    attachment_path_file = camera_record('photo', camera_still.unique_id)
    attachment_file = os.path.join(attachment_path_file[0], attachment_path_file[1])
    return message, attachment_file
Example #6
0
def camera_img_acquire(image_type, camera_unique_id, max_age):
    """Capture an image and return the filename"""
    if image_type == 'new':
        tmp_filename = None
    elif image_type == 'tmp':
        tmp_filename = '{id}_tmp.jpg'.format(id=camera_unique_id)
    else:
        return
    path, filename = camera_record('photo', camera_unique_id, tmp_filename=tmp_filename)
    image_path = os.path.join(path, filename)
    time_max_age = datetime.datetime.now() - datetime.timedelta(seconds=int(max_age))
    timestamp = os.path.getctime(image_path)
    if datetime.datetime.fromtimestamp(timestamp) > time_max_age:
        date_time = datetime.datetime.fromtimestamp(timestamp).strftime('%Y-%m-%d %H:%M:%S')
        return_values = '["{}","{}"]'.format(filename, date_time)
    else:
        return_values = '["max_age_exceeded"]'
    return Response(return_values, mimetype='text/json')
Example #7
0
def camera_img_acquire(image_type, camera_unique_id, max_age):
    """Capture an image and resturn the filename"""
    if image_type == 'new':
        tmp_filename = None
    elif image_type == 'tmp':
        tmp_filename = '{id}_tmp.jpg'.format(id=camera_unique_id)
    else:
        return
    path, filename = camera_record('photo', camera_unique_id, tmp_filename=tmp_filename)
    image_path = os.path.join(path, filename)
    time_max_age = datetime.datetime.now() - datetime.timedelta(seconds=int(max_age))
    timestamp = os.path.getctime(image_path)
    if datetime.datetime.fromtimestamp(timestamp) > time_max_age:
        date_time = datetime.datetime.fromtimestamp(timestamp).strftime('%Y-%m-%d %H:%M:%S')
        return_values = '["{}","{}"]'.format(filename, date_time)
    else:
        return_values = '["max_age_exceeded"]'
    return Response(return_values, mimetype='text/json')
Example #8
0
def action_video(cond_action, message):
    this_camera = db_retrieve_table_daemon(Camera,
                                           unique_id=cond_action.do_unique_id,
                                           entry='first')
    message += "  Capturing video with camera {unique_id} ({id}, {name}).".format(
        unique_id=cond_action.do_unique_id,
        id=this_camera.id,
        name=this_camera.name)
    camera_stream = db_retrieve_table_daemon(
        Camera, unique_id=cond_action.do_unique_id)
    path, filename = camera_record('video',
                                   camera_stream.unique_id,
                                   duration_sec=cond_action.do_camera_duration)
    if path and filename:
        attachment_file = os.path.join(path, filename)
        return message, attachment_file
    else:
        message += " A video could not be acquired."
        return message, None
Example #9
0
def trigger_action(cond_action_id,
                   message='',
                   note_tags=None,
                   email_recipients=None,
                   attachment_file=None,
                   attachment_type=None,
                   single_action=False):
    cond_action = db_retrieve_table_daemon(Actions, unique_id=cond_action_id)
    if not cond_action:
        return

    logger_actions = logging.getLogger("mycodo.trigger_action_{id}".format(
        id=cond_action.unique_id.split('-')[0]))

    control = DaemonControl()

    smtp_table = db_retrieve_table_daemon(SMTP, entry='first')
    smtp_max_count = smtp_table.hourly_max
    smtp_wait_timer = smtp_table.smtp_wait_timer
    email_count = smtp_table.email_count

    message += "\n[Action {id}]:".format(
        id=cond_action.unique_id.split('-')[0],
        action_type=cond_action.action_type)

    # Pause
    if cond_action.action_type == 'pause_actions':
        message += " [{id}] Pause actions for {sec} seconds.".format(
            id=cond_action.id, sec=cond_action.pause_duration)

        time.sleep(cond_action.pause_duration)

    # Actuate output (duration)
    if (cond_action.action_type == 'output' and cond_action.do_unique_id
            and cond_action.do_output_state in ['on', 'off']):
        this_output = db_retrieve_table_daemon(
            Output, unique_id=cond_action.do_unique_id, entry='first')
        message += " Turn output {unique_id} ({id}, {name}) {state}".format(
            unique_id=cond_action.do_unique_id,
            id=this_output.id,
            name=this_output.name,
            state=cond_action.do_output_state)
        if (cond_action.do_output_state == 'on'
                and cond_action.do_output_duration):
            message += " for {sec} seconds".format(
                sec=cond_action.do_output_duration)
        message += "."

        output_on_off = threading.Thread(
            target=control.output_on_off,
            args=(
                cond_action.do_unique_id,
                cond_action.do_output_state,
            ),
            kwargs={'duration': cond_action.do_output_duration})
        output_on_off.start()

    # Actuate output (PWM)
    if (cond_action.action_type == 'output_pwm' and cond_action.do_unique_id
            and cond_action.do_output_pwm):
        this_output = db_retrieve_table_daemon(
            Output, unique_id=cond_action.do_unique_id, entry='first')
        message += " Turn output {unique_id} ({id}, {name}) duty cycle to {duty_cycle}%.".format(
            unique_id=cond_action.do_unique_id,
            id=this_output.id,
            name=this_output.name,
            duty_cycle=cond_action.do_output_pwm)

        output_on = threading.Thread(
            target=control.output_on,
            args=(cond_action.do_unique_id, ),
            kwargs={'duty_cycle': cond_action.do_output_pwm})
        output_on.start()

    # Execute command in shell
    if cond_action.action_type == 'command':

        # Replace string variables with actual values
        command_str = cond_action.do_action_string

        # TODO: Maybe get this working again with the new measurement system
        # # Replace measurement variables
        # if last_measurement:
        #     command_str = command_str.replace(
        #         "((measure_{var}))".format(
        #             var=device_measurement), str(last_measurement))
        # if device and device.period:
        #     command_str = command_str.replace(
        #         "((measure_period))", str(device.period))
        # if input_dev:
        #     command_str = command_str.replace(
        #         "((measure_location))", str(input_dev.location))
        # if input_dev and device_measurement == input_dev.measurements:
        #     command_str = command_str.replace(
        #         "((measure_linux_command))", str(last_measurement))
        #
        # # Replace output variables
        # if output:
        #     if output.pin:
        #         command_str = command_str.replace(
        #             "((output_pin))", str(output.pin))
        #     if output_state:
        #         command_str = command_str.replace(
        #             "((output_action))", str(output_state))
        #     if on_duration:
        #         command_str = command_str.replace(
        #             "((output_duration))", str(on_duration))
        #     if duty_cycle:
        #         command_str = command_str.replace(
        #             "((output_pwm))", str(duty_cycle))
        #
        # # Replace edge variables
        # if edge:
        #     command_str = command_str.replace(
        #         "((edge_state))", str(edge))

        message += " Execute '{com}' ".format(com=command_str)

        _, _, cmd_status = cmd_output(command_str)

        message += "(return status: {stat}).".format(stat=cmd_status)

    # Create Note
    if cond_action.action_type == 'create_note':
        tag_name = db_retrieve_table_daemon(
            NoteTags, unique_id=cond_action.do_action_string).name

        message += " Create note with tag '{}'.".format(tag_name)
        if single_action and cond_action.do_action_string:
            list_tags = []
            check_tag = db_retrieve_table_daemon(
                NoteTags, unique_id=cond_action.do_action_string)
            if check_tag:
                list_tags.append(cond_action.do_action_string)

            if list_tags:
                with session_scope(MYCODO_DB_PATH) as db_session:
                    new_note = Notes()
                    new_note.name = 'Action'
                    new_note.tags = ','.join(list_tags)
                    new_note.note = message
                    db_session.add(new_note)
        else:
            note_tags.append(cond_action.do_action_string)

    # Capture photo
    # If emailing later, store location of photo as attachment_file
    if cond_action.action_type in ['photo', 'photo_email']:
        this_camera = db_retrieve_table_daemon(
            Camera, unique_id=cond_action.do_unique_id, entry='first')
        message += "  Capturing photo with camera {unique_id} ({id}, {name}).".format(
            unique_id=cond_action.do_unique_id,
            id=this_camera.id,
            name=this_camera.name)
        camera_still = db_retrieve_table_daemon(
            Camera, unique_id=cond_action.do_unique_id)
        attachment_path_file = camera_record('photo', camera_still.unique_id)
        attachment_file = os.path.join(attachment_path_file[0],
                                       attachment_path_file[1])

    # Capture video
    if cond_action.action_type in ['video', 'video_email']:
        this_camera = db_retrieve_table_daemon(
            Camera, unique_id=cond_action.do_unique_id, entry='first')
        message += "  Capturing video with camera {unique_id} ({id}, {name}).".format(
            unique_id=cond_action.do_unique_id,
            id=this_camera.id,
            name=this_camera.name)
        camera_stream = db_retrieve_table_daemon(
            Camera, unique_id=cond_action.do_unique_id)
        attachment_path_file = camera_record(
            'video',
            camera_stream.unique_id,
            duration_sec=cond_action.do_camera_duration)
        attachment_file = os.path.join(attachment_path_file[0],
                                       attachment_path_file[1])

    # Activate Controller
    if cond_action.action_type == 'activate_controller':
        (controller_type, controller_object,
         controller_entry) = which_controller(cond_action.do_unique_id)
        message += " Activate Controller {unique_id} ({id}, {name}).".format(
            unique_id=cond_action.do_unique_id,
            id=controller_entry.id,
            name=controller_entry.name)
        if controller_entry.is_activated:
            message += " Notice: Controller is already active!"
        else:
            # If controller is Conditional and is
            # run_pwm_method, activate method start
            is_conditional = db_retrieve_table_daemon(
                Conditional, unique_id=cond_action.do_unique_id, entry='first')
            if (is_conditional
                    and is_conditional.conditional_type == 'run_pwm_method'):
                with session_scope(MYCODO_DB_PATH) as new_session:
                    mod_cont_ready = new_session.query(Conditional).filter(
                        Conditional.unique_id ==
                        cond_action.do_unique_id).first()
                    mod_cont_ready.method_start_time = 'Ready'
                    new_session.commit()

            with session_scope(MYCODO_DB_PATH) as new_session:
                mod_cont = new_session.query(controller_object).filter(
                    controller_object.unique_id ==
                    cond_action.do_unique_id).first()
                mod_cont.is_activated = True
                new_session.commit()
            activate_controller = threading.Thread(
                target=control.controller_activate,
                args=(
                    controller_type,
                    cond_action.do_unique_id,
                ))
            activate_controller.start()

    # Deactivate Controller
    if cond_action.action_type == 'deactivate_controller':
        (controller_type, controller_object,
         controller_entry) = which_controller(cond_action.do_unique_id)
        message += " Deactivate Controller {unique_id} ({id}, {name}).".format(
            unique_id=cond_action.do_unique_id,
            id=controller_entry.id,
            name=controller_entry.name)
        if not controller_entry.is_activated:
            message += " Notice: Controller is already inactive!"
        else:
            with session_scope(MYCODO_DB_PATH) as new_session:
                mod_cont = new_session.query(controller_object).filter(
                    controller_object.unique_id ==
                    cond_action.do_unique_id).first()
                mod_cont.is_activated = False
                new_session.commit()
            deactivate_controller = threading.Thread(
                target=control.controller_deactivate,
                args=(
                    controller_type,
                    cond_action.do_unique_id,
                ))
            deactivate_controller.start()

    # Resume PID controller
    if cond_action.action_type == 'resume_pid':
        pid = db_retrieve_table_daemon(PID,
                                       unique_id=cond_action.do_unique_id,
                                       entry='first')
        message += " Resume PID {unique_id} ({id}, {name}).".format(
            unique_id=cond_action.do_unique_id, id=pid.id, name=pid.name)
        if not pid.is_paused:
            message += " Notice: PID is not paused!"
        elif pid.is_activated:
            with session_scope(MYCODO_DB_PATH) as new_session:
                mod_pid = new_session.query(PID).filter(
                    PID.unique_id == cond_action.do_unique_id).first()
                mod_pid.is_paused = False
                new_session.commit()
            resume_pid = threading.Thread(target=control.pid_resume,
                                          args=(cond_action.do_unique_id, ))
            resume_pid.start()

    # Pause PID controller
    if cond_action.action_type == 'pause_pid':
        pid = db_retrieve_table_daemon(PID,
                                       unique_id=cond_action.do_unique_id,
                                       entry='first')
        message += " Pause PID {unique_id} ({id}, {name}).".format(
            unique_id=cond_action.do_unique_id, id=pid.id, name=pid.name)
        if pid.is_paused:
            message += " Notice: PID is already paused!"
        elif pid.is_activated:
            with session_scope(MYCODO_DB_PATH) as new_session:
                mod_pid = new_session.query(PID).filter(
                    PID.unique_id == cond_action.do_unique_id).first()
                mod_pid.is_paused = True
                new_session.commit()
            pause_pid = threading.Thread(target=control.pid_pause,
                                         args=(cond_action.do_unique_id, ))
            pause_pid.start()

    # Set PID Setpoint
    if cond_action.action_type == 'setpoint_pid':
        pid = db_retrieve_table_daemon(PID,
                                       unique_id=cond_action.do_unique_id,
                                       entry='first')
        message += " Set Setpoint of PID {unique_id} ({id}, {name}).".format(
            unique_id=cond_action.do_unique_id, id=pid.id, name=pid.name)
        if pid.is_activated:
            setpoint_pid = threading.Thread(
                target=control.pid_set,
                args=(
                    pid.unique_id,
                    'setpoint',
                    float(cond_action.do_action_string),
                ))
            setpoint_pid.start()
        else:
            with session_scope(MYCODO_DB_PATH) as new_session:
                mod_pid = new_session.query(PID).filter(
                    PID.unique_id == cond_action.do_unique_id).first()
                mod_pid.setpoint = cond_action.do_action_string
                new_session.commit()

    # Set PID Method and start method from beginning
    if cond_action.action_type == 'method_pid':
        pid = db_retrieve_table_daemon(PID,
                                       unique_id=cond_action.do_unique_id,
                                       entry='first')
        message += " Set Method of PID {unique_id} ({id}, {name}).".format(
            unique_id=cond_action.do_unique_id, id=pid.id, name=pid.name)

        # Instruct method to start
        with session_scope(MYCODO_DB_PATH) as new_session:
            mod_pid = new_session.query(PID).filter(
                PID.unique_id == cond_action.do_unique_id).first()
            mod_pid.method_start_time = 'Ready'
            new_session.commit()

        pid = db_retrieve_table_daemon(PID,
                                       unique_id=cond_action.do_unique_id,
                                       entry='first')
        if pid.is_activated:
            method_pid = threading.Thread(target=control.pid_set,
                                          args=(
                                              pid.unique_id,
                                              'method',
                                              cond_action.do_action_string,
                                          ))
            method_pid.start()
        else:
            with session_scope(MYCODO_DB_PATH) as new_session:
                mod_pid = new_session.query(PID).filter(
                    PID.unique_id == cond_action.do_unique_id).first()
                mod_pid.method_id = cond_action.do_action_string
                new_session.commit()

    # Email the Conditional message. Optionally capture a photo or
    # video and attach to the email.
    if cond_action.action_type in ['email', 'photo_email', 'video_email']:

        if (email_count >= smtp_max_count and time.time() < smtp_wait_timer):
            allowed_to_send_notice = False
        else:
            if time.time() > smtp_wait_timer:
                with session_scope(MYCODO_DB_PATH) as new_session:
                    mod_smtp = new_session.query(SMTP).first()
                    mod_smtp.email_count = 0
                    mod_smtp.smtp_wait_timer = time.time() + 3600
                    new_session.commit()
            allowed_to_send_notice = True

        with session_scope(MYCODO_DB_PATH) as new_session:
            mod_smtp = new_session.query(SMTP).first()
            mod_smtp.email_count += 1
            new_session.commit()

        # If the emails per hour limit has not been exceeded
        if allowed_to_send_notice:
            message += " Notify {email}.".format(
                email=cond_action.do_action_string)
            # attachment_type != False indicates to
            # attach a photo or video
            if cond_action.action_type == 'photo_email':
                message += " Photo attached to email."
                attachment_type = 'still'
            elif cond_action.action_type == 'video_email':
                message += " Video attached to email."
                attachment_type = 'video'

            if single_action and cond_action.do_action_string:
                smtp = db_retrieve_table_daemon(SMTP, entry='first')
                send_email(smtp.host, smtp.ssl, smtp.port, smtp.user,
                           smtp.passw, smtp.email_from,
                           [cond_action.do_action_string], message,
                           attachment_file, attachment_type)
            else:
                email_recipients.append(cond_action.do_action_string)
        else:
            logger_actions.error(
                "Wait {sec:.0f} seconds to email again.".format(
                    sec=smtp_wait_timer - time.time()))

    if cond_action.action_type == 'flash_lcd_on':
        lcd = db_retrieve_table_daemon(LCD, unique_id=cond_action.do_unique_id)
        message += " LCD {unique_id} ({id}, {name}) Flash On.".format(
            unique_id=cond_action.do_unique_id, id=lcd.id, name=lcd.name)

        start_flashing = threading.Thread(target=control.lcd_flash,
                                          args=(
                                              cond_action.do_unique_id,
                                              True,
                                          ))
        start_flashing.start()

    if cond_action.action_type == 'flash_lcd_off':
        lcd = db_retrieve_table_daemon(LCD, unique_id=cond_action.do_unique_id)
        message += " LCD {unique_id} ({id}, {name}) Flash Off.".format(
            unique_id=cond_action.do_unique_id, id=lcd.id, name=lcd.name)

        start_flashing = threading.Thread(target=control.lcd_flash,
                                          args=(
                                              cond_action.do_unique_id,
                                              False,
                                          ))
        start_flashing.start()

    if cond_action.action_type == 'lcd_backlight_off':
        lcd = db_retrieve_table_daemon(LCD, unique_id=cond_action.do_unique_id)
        message += " LCD {unique_id} ({id}, {name}) Backlight Off.".format(
            unique_id=cond_action.do_unique_id, id=lcd.id, name=lcd.name)

        start_flashing = threading.Thread(target=control.lcd_backlight,
                                          args=(
                                              cond_action.do_unique_id,
                                              False,
                                          ))
        start_flashing.start()

    if cond_action.action_type == 'lcd_backlight_on':
        lcd = db_retrieve_table_daemon(LCD, unique_id=cond_action.do_unique_id)
        message += " LCD {unique_id} ({id}, {name}) Backlight On.".format(
            unique_id=cond_action.do_unique_id, id=lcd.id, name=lcd.name)

        start_flashing = threading.Thread(target=control.lcd_backlight,
                                          args=(
                                              cond_action.do_unique_id,
                                              True,
                                          ))
        start_flashing.start()

    if single_action:
        return message
    else:
        return message, note_tags, email_recipients, attachment_file, attachment_type
Example #10
0
def page_camera():
    """
    Page to start/stop video stream or time-lapse, or capture a still image.
    Displays most recent still image and time-lapse image.
    """
    if not flaskutils.user_has_permission('view_camera'):
        return redirect(url_for('general_routes.home'))

    form_camera = flaskforms.Camera()
    camera = Camera.query.all()

    # Check if a video stream is active
    for each_camera in camera:
        if each_camera.stream_started and not CameraStream().is_running():
            each_camera.stream_started = False
            db.session.commit()

    if request.method == 'POST':
        if not flaskutils.user_has_permission('edit_settings'):
            return redirect(url_for('page_routes.page_camera'))

        control = DaemonControl()
        mod_camera = Camera.query.filter(
            Camera.id == form_camera.camera_id.data).first()
        if form_camera.capture_still.data:
            if mod_camera.stream_started:
                flash(
                    gettext(
                        u"Cannot capture still image if stream is active."))
                return redirect('/camera')
            if CameraStream().is_running():
                CameraStream().terminate_controller()  # Stop camera stream
                time.sleep(2)
            camera_record('photo', mod_camera)
        elif form_camera.start_timelapse.data:
            if mod_camera.stream_started:
                flash(gettext(u"Cannot start time-lapse if stream is active."))
                return redirect('/camera')
            now = time.time()
            mod_camera.timelapse_started = True
            mod_camera.timelapse_start_time = now
            mod_camera.timelapse_end_time = now + float(
                form_camera.timelapse_runtime_sec.data)
            mod_camera.timelapse_interval = form_camera.timelapse_interval.data
            mod_camera.timelapse_next_capture = now
            mod_camera.timelapse_capture_number = 0
            db.session.commit()
            control.refresh_daemon_camera_settings()
        elif form_camera.pause_timelapse.data:
            mod_camera.timelapse_paused = True
            db.session.commit()
            control.refresh_daemon_camera_settings()
        elif form_camera.resume_timelapse.data:
            mod_camera.timelapse_paused = False
            db.session.commit()
            control.refresh_daemon_camera_settings()
        elif form_camera.stop_timelapse.data:
            mod_camera.timelapse_started = False
            mod_camera.timelapse_start_time = None
            mod_camera.timelapse_end_time = None
            mod_camera.timelapse_interval = None
            mod_camera.timelapse_next_capture = None
            mod_camera.timelapse_capture_number = None
            db.session.commit()
            control.refresh_daemon_camera_settings()
        elif form_camera.start_stream.data:
            if mod_camera.timelapse_started:
                flash(gettext(u"Cannot start stream if time-lapse is active."))
                return redirect('/camera')
            elif CameraStream().is_running():
                flash(
                    gettext(
                        u"Cannot start stream. The stream is already running.")
                )
                return redirect('/camera')
            elif (not (mod_camera.camera_type == 'Raspberry Pi'
                       and mod_camera.library == 'picamera')):
                flash(
                    gettext(u"Streaming is only supported with the Raspberry"
                            u" Pi camera using the picamera library."))
                return redirect('/camera')
            elif Camera.query.filter_by(stream_started=True).count():
                flash(
                    gettext(u"Cannot start stream if another stream is "
                            u"already in progress."))
                return redirect('/camera')
            else:
                mod_camera.stream_started = True
                db.session.commit()
        elif form_camera.stop_stream.data:
            if CameraStream().is_running():
                CameraStream().terminate_controller()
            mod_camera.stream_started = False
            db.session.commit()
        return redirect('/camera')

    # Get the full path and timestamps of latest still and time-lapse images
    latest_img_still_ts = {}
    latest_img_still = {}
    latest_img_tl_ts = {}
    latest_img_tl = {}
    for each_camera in camera:
        camera_path = os.path.join(
            PATH_CAMERAS, '{id}-{uid}'.format(id=each_camera.id,
                                              uid=each_camera.unique_id))
        try:
            latest_still_img_full_path = max(glob.iglob(
                '{path}/still/Still-{cam_id}-*.jpg'.format(
                    path=camera_path, cam_id=each_camera.id)),
                                             key=os.path.getmtime)
        except ValueError:
            latest_still_img_full_path = None
        if latest_still_img_full_path:
            ts = os.path.getmtime(latest_still_img_full_path)
            latest_img_still_ts[
                each_camera.id] = datetime.datetime.fromtimestamp(ts).strftime(
                    "%Y-%m-%d %H:%M:%S")
            latest_img_still[each_camera.id] = os.path.basename(
                latest_still_img_full_path)
        else:
            latest_img_still[each_camera.id] = None

        try:
            latest_time_lapse_img_full_path = max(glob.iglob(
                '{path}/timelapse/Timelapse-{cam_id}-*.jpg'.format(
                    path=camera_path, cam_id=each_camera.id)),
                                                  key=os.path.getmtime)
        except ValueError:
            latest_time_lapse_img_full_path = None
        if latest_time_lapse_img_full_path:
            ts = os.path.getmtime(latest_time_lapse_img_full_path)
            latest_img_tl_ts[each_camera.id] = datetime.datetime.fromtimestamp(
                ts).strftime("%Y-%m-%d %H:%M:%S")
            latest_img_tl[each_camera.id] = os.path.basename(
                latest_time_lapse_img_full_path)
        else:
            latest_img_tl[each_camera.id] = None

    time_now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')

    return render_template('pages/camera.html',
                           camera=camera,
                           form_camera=form_camera,
                           latest_img_still=latest_img_still,
                           latest_img_still_ts=latest_img_still_ts,
                           latest_img_tl=latest_img_tl,
                           latest_img_tl_ts=latest_img_tl_ts,
                           time_now=time_now)
Example #11
0
    def trigger_conditional_actions(self, cond_id,
                                    message='', last_measurement=None,
                                    device_id=None, device_measurement=None, edge=None,
                                    output_state=None, on_duration=None, duty_cycle=None):
        """
        If a Conditional has been triggered, this function will execute
        the Conditional Actions

        :param self: self from the Controller class
        :param cond_id: The ID of the Conditional
        :param device_id: The unique ID associated with the device_measurement
        :param message: The message generated from the conditional check
        :param last_measurement: The last measurement value
        :param device_measurement: The measurement (i.e. "temperature")
        :param edge: If edge conditional, rise/on (1) or fall/off (0)
        :param output_state: If output conditional, the output state (on/off) to trigger the action
        :param on_duration: If output conditional, the ON duration
        :param duty_cycle: If output conditional, the duty cycle
        :return:
        """
        logger_cond = logging.getLogger("mycodo.conditional_{id}".format(
            id=cond_id))

        # List of all email notification recipients
        # List is appended with TO email addresses when an email Action is
        # encountered. An email is sent to all recipients after all actions
        # have been executed.
        email_recipients = []

        attachment_file = False
        attachment_type = False
        input_dev = None
        output = None
        device = None

        cond_actions = db_retrieve_table_daemon(ConditionalActions)
        cond_actions = cond_actions.filter(
            ConditionalActions.conditional_id == cond_id).all()

        if device_id:
            input_dev = db_retrieve_table_daemon(
                Input, unique_id=device_id, entry='first')
            if input_dev:
                device = input_dev

            math = db_retrieve_table_daemon(
                Math, unique_id=device_id, entry='first')
            if math:
                device = math

            output = db_retrieve_table_daemon(
                Output, unique_id=device_id, entry='first')
            if output:
                device = output

            pid = db_retrieve_table_daemon(
                PID, unique_id=device_id, entry='first')
            if pid:
                device = pid

        for cond_action in cond_actions:
            message += "\n[Conditional Action {id}]:".format(
                id=cond_action.id, do_action=cond_action.do_action)

            # Actuate output
            if (cond_action.do_action == 'output' and cond_action.do_relay_id and
                    cond_action.do_relay_state in ['on', 'off']):
                message += " Turn output {id} {state}".format(
                    id=cond_action.do_relay_id,
                    state=cond_action.do_relay_state)
                if (cond_action.do_relay_state == 'on' and
                        cond_action.do_relay_duration):
                    message += " for {sec} seconds".format(
                        sec=cond_action.do_relay_duration)
                message += "."

                output_on_off = threading.Thread(
                    target=self.control.output_on_off,
                    args=(cond_action.do_relay_id,
                          cond_action.do_relay_state,),
                    kwargs={'duration': cond_action.do_relay_duration})
                output_on_off.start()

            # Execute command in shell
            elif cond_action.do_action == 'command':

                # Replace string variables with actual values
                command_str = cond_action.do_action_string

                # Replace measurement variables
                if last_measurement:
                    command_str = command_str.replace(
                        "((measure_{var}))".format(
                            var=device_measurement), str(last_measurement))
                if device and device.period:
                    command_str = command_str.replace(
                        "((measure_period))", str(device.period))
                if input_dev:
                    command_str = command_str.replace(
                        "((measure_location))", str(input_dev.location))
                if input_dev and device_measurement == input_dev.cmd_measurement:
                    command_str = command_str.replace(
                        "((measure_linux_command))", str(input_dev.location))

                # Replace output variables
                if output:
                    if output.pin:
                        command_str = command_str.replace(
                            "((output_pin))", str(output.pin))
                    if output_state:
                        command_str = command_str.replace(
                            "((output_action))", str(output_state))
                    if on_duration:
                        command_str = command_str.replace(
                            "((output_duration))", str(on_duration))
                    if duty_cycle:
                        command_str = command_str.replace(
                            "((output_pwm))", str(duty_cycle))

                # Replace edge variables
                if edge:
                    command_str = command_str.replace(
                        "((edge_state))", str(edge))

                message += " Execute '{com}' ".format(
                    com=command_str)

                _, _, cmd_status = cmd_output(command_str)

                message += "(return status: {stat}).".format(stat=cmd_status)

            # Capture photo
            elif cond_action.do_action in ['photo', 'photo_email']:
                message += "  Capturing photo with camera ({id}).".format(
                    id=cond_action.do_camera_id)
                camera_still = db_retrieve_table_daemon(
                    Camera, device_id=cond_action.do_camera_id)
                attachment_file = camera_record('photo', camera_still.unique_id)

            # Capture video
            elif cond_action.do_action in ['video', 'video_email']:
                message += "  Capturing video with camera ({id}).".format(
                    id=cond_action.do_camera_id)
                camera_stream = db_retrieve_table_daemon(
                    Camera, device_id=cond_action.do_camera_id)
                attachment_file = camera_record(
                    'video', camera_stream.unique_id,
                    duration_sec=cond_action.do_camera_duration)

            # Activate PID controller
            elif cond_action.do_action == 'activate_pid':
                message += " Activate PID ({id}).".format(
                    id=cond_action.do_pid_id)
                pid = db_retrieve_table_daemon(
                    PID, device_id=cond_action.do_pid_id, entry='first')
                if pid.is_activated:
                    message += " Notice: PID is already active!"
                else:
                    with session_scope(MYCODO_DB_PATH) as new_session:
                        mod_pid = new_session.query(PID).filter(
                            PID.id == cond_action.do_pid_id).first()
                        mod_pid.is_activated = True
                        new_session.commit()
                    activate_pid = threading.Thread(
                        target=self.control.controller_activate,
                        args=('PID',
                              cond_action.do_pid_id,))
                    activate_pid.start()

            # Deactivate PID controller
            elif cond_action.do_action == 'deactivate_pid':
                message += " Deactivate PID ({id}).".format(
                    id=cond_action.do_pid_id)
                pid = db_retrieve_table_daemon(
                    PID, device_id=cond_action.do_pid_id, entry='first')
                if not pid.is_activated:
                    message += " Notice: PID is already inactive!"
                else:
                    with session_scope(MYCODO_DB_PATH) as new_session:
                        mod_pid = new_session.query(PID).filter(
                            PID.id == cond_action.do_pid_id).first()
                        mod_pid.is_activated = False
                        new_session.commit()
                    deactivate_pid = threading.Thread(
                        target=self.control.controller_deactivate,
                        args=('PID',
                              cond_action.do_pid_id,))
                    deactivate_pid.start()

            # Resume PID controller
            elif cond_action.do_action == 'resume_pid':
                message += " Resume PID ({id}).".format(
                    id=cond_action.do_pid_id)
                pid = db_retrieve_table_daemon(
                    PID, device_id=cond_action.do_pid_id, entry='first')
                if not pid.is_paused:
                    message += " Notice: PID is not paused!"
                elif pid.is_activated:
                    with session_scope(MYCODO_DB_PATH) as new_session:
                        mod_pid = new_session.query(PID).filter(
                            PID.id == cond_action.do_pid_id).first()
                        mod_pid.is_paused = False
                        new_session.commit()
                    resume_pid = threading.Thread(
                        target=self.control.pid_resume,
                        args=(cond_action.do_pid_id,))
                    resume_pid.start()

            # Pause PID controller
            elif cond_action.do_action == 'pause_pid':
                message += " Pause PID ({id}).".format(
                    id=cond_action.do_pid_id)
                pid = db_retrieve_table_daemon(
                    PID, device_id=cond_action.do_pid_id, entry='first')
                if pid.is_paused:
                    message += " Notice: PID is already paused!"
                elif pid.is_activated:
                    with session_scope(MYCODO_DB_PATH) as new_session:
                        mod_pid = new_session.query(PID).filter(
                            PID.id == cond_action.do_pid_id).first()
                        mod_pid.is_paused = True
                        new_session.commit()
                    pause_pid = threading.Thread(
                        target=self.control.pid_pause,
                        args=(cond_action.do_pid_id,))
                    pause_pid.start()

            # Email the Conditional message. Optionally capture a photo or
            # video and attach to the email.
            elif cond_action.do_action in ['email',
                                           'photo_email',
                                           'video_email']:

                if (self.email_count >= self.smtp_max_count and
                        time.time() < self.smtp_wait_timer[cond_id]):
                    self.allowed_to_send_notice = False
                else:
                    if time.time() > self.smtp_wait_timer[cond_id]:
                        self.email_count = 0
                        self.smtp_wait_timer[cond_id] = time.time() + 3600
                    self.allowed_to_send_notice = True
                self.email_count += 1

                # If the emails per hour limit has not been exceeded
                if self.allowed_to_send_notice:
                    email_recipients.append(cond_action.do_action_string)
                    message += " Notify {email}.".format(
                        email=cond_action.do_action_string)
                    # attachment_type != False indicates to
                    # attach a photo or video
                    if cond_action.do_action == 'photo_email':
                        message += " Photo attached to email."
                        attachment_type = 'still'
                    elif cond_action.do_action == 'video_email':
                        message += " Video attached to email."
                        attachment_type = 'video'
                else:
                    logger_cond.error(
                        "Wait {sec:.0f} seconds to email again.".format(
                            sec=self.smtp_wait_timer[cond_id] - time.time()))

            # TODO: rename flash_lcd in user databases to flash_lcd_on
            elif cond_action.do_action in ['flash_lcd', 'flash_lcd_on']:
                lcd = db_retrieve_table_daemon(
                    LCD, device_id=cond_action.do_lcd_id)
                message += " Flash LCD On {id} ({name}).".format(
                    id=lcd.id,
                    name=lcd.name)

                start_flashing = threading.Thread(
                    target=self.control.lcd_flash,
                    args=(cond_action.do_lcd_id, True,))
                start_flashing.start()

            elif cond_action.do_action == 'flash_lcd_off':
                lcd = db_retrieve_table_daemon(
                    LCD, device_id=cond_action.do_lcd_id)
                message += " Flash LCD Off {id} ({name}).".format(
                    id=lcd.id,
                    name=lcd.name)

                start_flashing = threading.Thread(
                    target=self.control.lcd_flash,
                    args=(cond_action.do_lcd_id, False,))
                start_flashing.start()

            elif cond_action.do_action == 'lcd_backlight_off':
                lcd = db_retrieve_table_daemon(
                    LCD, device_id=cond_action.do_lcd_id)
                message += " LCD Backlight Off {id} ({name}).".format(
                    id=lcd.id,
                    name=lcd.name)

                start_flashing = threading.Thread(
                    target=self.control.lcd_backlight,
                    args=(cond_action.do_lcd_id, False,))
                start_flashing.start()

            elif cond_action.do_action == 'lcd_backlight_on':
                lcd = db_retrieve_table_daemon(
                    LCD, device_id=cond_action.do_lcd_id)
                message += " LCD Backlight On {id} ({name}).".format(
                    id=lcd.id,
                    name=lcd.name)

                start_flashing = threading.Thread(
                    target=self.control.lcd_backlight,
                    args=(cond_action.do_lcd_id, True,))
                start_flashing.start()

        # Send email after all conditional actions have been checked
        # In order to append all action messages to send in the email
        # send_email_at_end will be None or the TO email address
        if email_recipients:
            smtp = db_retrieve_table_daemon(SMTP, entry='first')
            send_email(smtp.host, smtp.ssl, smtp.port,
                       smtp.user, smtp.passw, smtp.email_from,
                       email_recipients, message,
                       attachment_file, attachment_type)

        logger_cond.debug(message)
Example #12
0
def trigger_action(
        cond_action_id,
        message='',
        note_tags=None,
        email_recipients=None,
        attachment_file=None,
        attachment_type=None,
        single_action=False):
    """
    Trigger individual action

    If single_action == False, message, note_tags, email_recipients,
    attachment_file, and attachment_type are returned and may be
    passed back to this function in order to append to those lists.

    :param cond_action_id: unique_id of action
    :param message: message string to append to that will be sent back
    :param note_tags: list of note tags to use if creating a note
    :param email_recipients: list of email addresses to notify if sending an email
    :param attachment_file: string location of a file to attach to an email
    :param attachment_type: string type of email attachment
    :param single_action: True if only one action is being triggered, False if only one of multiple actions
    :return: message or (message, note_tags, email_recipients, attachment_file, attachment_type)
    """
    cond_action = db_retrieve_table_daemon(Actions, unique_id=cond_action_id)
    if not cond_action:
        message += 'Error: Action with ID {} not found!'.format(cond_action_id)
        if single_action:
            return message
        else:
            return message, note_tags, email_recipients, attachment_file, attachment_type

    logger_actions = logging.getLogger("mycodo.trigger_action_{id}".format(
        id=cond_action.unique_id.split('-')[0]))

    message += "\n[Action {id}]:".format(
        id=cond_action.unique_id.split('-')[0], action_type=cond_action.action_type)

    try:
        control = DaemonControl()

        smtp_table = db_retrieve_table_daemon(
            SMTP, entry='first')
        smtp_max_count = smtp_table.hourly_max
        smtp_wait_timer = smtp_table.smtp_wait_timer
        email_count = smtp_table.email_count

        # Pause
        if cond_action.action_type == 'pause_actions':
            message += " [{id}] Pause actions for {sec} seconds.".format(
                id=cond_action.id,
                sec=cond_action.pause_duration)

            time.sleep(cond_action.pause_duration)

        # Actuate output (duration)
        if (cond_action.action_type == 'output' and
                cond_action.do_unique_id and
                cond_action.do_output_state in ['on', 'off']):
            this_output = db_retrieve_table_daemon(
                Output, unique_id=cond_action.do_unique_id, entry='first')
            message += " Turn output {unique_id} ({id}, {name}) {state}".format(
                unique_id=cond_action.do_unique_id,
                id=this_output.id,
                name=this_output.name,
                state=cond_action.do_output_state)
            if (cond_action.do_output_state == 'on' and
                    cond_action.do_output_duration):
                message += " for {sec} seconds".format(
                    sec=cond_action.do_output_duration)
            message += "."

            output_on_off = threading.Thread(
                target=control.output_on_off,
                args=(cond_action.do_unique_id,
                      cond_action.do_output_state,),
                kwargs={'duration': cond_action.do_output_duration})
            output_on_off.start()

        # Actuate output (PWM)
        if (cond_action.action_type == 'output_pwm' and
                cond_action.do_unique_id and
                0 <= cond_action.do_output_pwm <= 100):
            this_output = db_retrieve_table_daemon(
                Output, unique_id=cond_action.do_unique_id, entry='first')
            message += " Turn output {unique_id} ({id}, {name}) duty cycle to {duty_cycle}%.".format(
                unique_id=cond_action.do_unique_id,
                id=this_output.id,
                name=this_output.name,
                duty_cycle=cond_action.do_output_pwm)

            output_on = threading.Thread(
                target=control.output_on,
                args=(cond_action.do_unique_id,),
                kwargs={'duty_cycle': cond_action.do_output_pwm})
            output_on.start()

        # Execute command in shell
        if cond_action.action_type == 'command':

            # Replace string variables with actual values
            command_str = cond_action.do_action_string

            # TODO: Maybe get this working again with the new measurement system
            # # Replace measurement variables
            # if last_measurement:
            #     command_str = command_str.replace(
            #         "((measure_{var}))".format(
            #             var=device_measurement), str(last_measurement))
            # if device and device.period:
            #     command_str = command_str.replace(
            #         "((measure_period))", str(device.period))
            # if input_dev:
            #     command_str = command_str.replace(
            #         "((measure_location))", str(input_dev.location))
            # if input_dev and device_measurement == input_dev.measurements:
            #     command_str = command_str.replace(
            #         "((measure_linux_command))", str(last_measurement))
            #
            # # Replace output variables
            # if output:
            #     if output.pin:
            #         command_str = command_str.replace(
            #             "((output_pin))", str(output.pin))
            #     if output_state:
            #         command_str = command_str.replace(
            #             "((output_action))", str(output_state))
            #     if on_duration:
            #         command_str = command_str.replace(
            #             "((output_duration))", str(on_duration))
            #     if duty_cycle:
            #         command_str = command_str.replace(
            #             "((output_pwm))", str(duty_cycle))
            #
            # # Replace edge variables
            # if edge:
            #     command_str = command_str.replace(
            #         "((edge_state))", str(edge))

            message += " Execute '{com}' ".format(
                com=command_str)

            _, _, cmd_status = cmd_output(command_str)

            message += "(return status: {stat}).".format(stat=cmd_status)

        # Create Note
        if cond_action.action_type == 'create_note':
            tag_name = db_retrieve_table_daemon(
                NoteTags, unique_id=cond_action.do_action_string).name

            message += " Create note with tag '{}'.".format(tag_name)
            if single_action and cond_action.do_action_string:
                    list_tags = []
                    check_tag = db_retrieve_table_daemon(
                        NoteTags, unique_id=cond_action.do_action_string)
                    if check_tag:
                        list_tags.append(cond_action.do_action_string)

                    if list_tags:
                        with session_scope(MYCODO_DB_PATH) as db_session:
                            new_note = Notes()
                            new_note.name = 'Action'
                            new_note.tags = ','.join(list_tags)
                            new_note.note = message
                            db_session.add(new_note)
            else:
                note_tags.append(cond_action.do_action_string)

        # Capture photo
        # If emailing later, store location of photo as attachment_file
        if cond_action.action_type in ['photo', 'photo_email']:
            this_camera = db_retrieve_table_daemon(
                Camera, unique_id=cond_action.do_unique_id, entry='first')
            message += "  Capturing photo with camera {unique_id} ({id}, {name}).".format(
                unique_id=cond_action.do_unique_id,
                id=this_camera.id,
                name=this_camera.name)
            camera_still = db_retrieve_table_daemon(
                Camera, unique_id=cond_action.do_unique_id)
            attachment_path_file = camera_record('photo', camera_still.unique_id)
            attachment_file = os.path.join(attachment_path_file[0], attachment_path_file[1])

        # Capture video
        if cond_action.action_type in ['video', 'video_email']:
            this_camera = db_retrieve_table_daemon(
                Camera, unique_id=cond_action.do_unique_id, entry='first')
            message += "  Capturing video with camera {unique_id} ({id}, {name}).".format(
                unique_id=cond_action.do_unique_id,
                id=this_camera.id,
                name=this_camera.name)
            camera_stream = db_retrieve_table_daemon(
                Camera, unique_id=cond_action.do_unique_id)
            attachment_path_file = camera_record(
                'video', camera_stream.unique_id,
                duration_sec=cond_action.do_camera_duration)
            attachment_file = os.path.join(attachment_path_file[0], attachment_path_file[1])

        # Activate Controller
        if cond_action.action_type == 'activate_controller':
            (controller_type,
             controller_object,
             controller_entry) = which_controller(
                cond_action.do_unique_id)
            message += " Activate Controller {unique_id} ({id}, {name}).".format(
                unique_id=cond_action.do_unique_id,
                id=controller_entry.id,
                name=controller_entry.name)
            if controller_entry.is_activated:
                message += " Notice: Controller is already active!"
            else:
                with session_scope(MYCODO_DB_PATH) as new_session:
                    mod_cont = new_session.query(controller_object).filter(
                        controller_object.unique_id == cond_action.do_unique_id).first()
                    mod_cont.is_activated = True
                    new_session.commit()
                activate_controller = threading.Thread(
                    target=control.controller_activate,
                    args=(controller_type,
                          cond_action.do_unique_id,))
                activate_controller.start()

        # Deactivate Controller
        if cond_action.action_type == 'deactivate_controller':
            (controller_type,
             controller_object,
             controller_entry) = which_controller(
                cond_action.do_unique_id)
            message += " Deactivate Controller {unique_id} ({id}, {name}).".format(
                unique_id=cond_action.do_unique_id,
                id=controller_entry.id,
                name=controller_entry.name)
            if not controller_entry.is_activated:
                message += " Notice: Controller is already inactive!"
            else:
                with session_scope(MYCODO_DB_PATH) as new_session:
                    mod_cont = new_session.query(controller_object).filter(
                        controller_object.unique_id == cond_action.do_unique_id).first()
                    mod_cont.is_activated = False
                    new_session.commit()
                deactivate_controller = threading.Thread(
                    target=control.controller_deactivate,
                    args=(controller_type,
                          cond_action.do_unique_id,))
                deactivate_controller.start()

        # Resume PID controller
        if cond_action.action_type == 'resume_pid':
            pid = db_retrieve_table_daemon(
                PID, unique_id=cond_action.do_unique_id, entry='first')
            message += " Resume PID {unique_id} ({id}, {name}).".format(
                unique_id=cond_action.do_unique_id,
                id=pid.id,
                name=pid.name)
            if not pid.is_paused:
                message += " Notice: PID is not paused!"
            elif pid.is_activated:
                with session_scope(MYCODO_DB_PATH) as new_session:
                    mod_pid = new_session.query(PID).filter(
                        PID.unique_id == cond_action.do_unique_id).first()
                    mod_pid.is_paused = False
                    new_session.commit()
                resume_pid = threading.Thread(
                    target=control.pid_resume,
                    args=(cond_action.do_unique_id,))
                resume_pid.start()

        # Pause PID controller
        if cond_action.action_type == 'pause_pid':
            pid = db_retrieve_table_daemon(
                PID, unique_id=cond_action.do_unique_id, entry='first')
            message += " Pause PID {unique_id} ({id}, {name}).".format(
                unique_id=cond_action.do_unique_id,
                id=pid.id,
                name=pid.name)
            if pid.is_paused:
                message += " Notice: PID is already paused!"
            elif pid.is_activated:
                with session_scope(MYCODO_DB_PATH) as new_session:
                    mod_pid = new_session.query(PID).filter(
                        PID.unique_id == cond_action.do_unique_id).first()
                    mod_pid.is_paused = True
                    new_session.commit()
                pause_pid = threading.Thread(
                    target=control.pid_pause,
                    args=(cond_action.do_unique_id,))
                pause_pid.start()

        # Set PID Setpoint
        if cond_action.action_type == 'setpoint_pid':
            pid = db_retrieve_table_daemon(
                PID, unique_id=cond_action.do_unique_id, entry='first')
            message += " Set Setpoint of PID {unique_id} ({id}, {name}).".format(
                unique_id=cond_action.do_unique_id,
                id=pid.id,
                name=pid.name)
            if pid.is_activated:
                setpoint_pid = threading.Thread(
                    target=control.pid_set,
                    args=(pid.unique_id,
                          'setpoint',
                          float(cond_action.do_action_string),))
                setpoint_pid.start()
            else:
                with session_scope(MYCODO_DB_PATH) as new_session:
                    mod_pid = new_session.query(PID).filter(
                        PID.unique_id == cond_action.do_unique_id).first()
                    mod_pid.setpoint = cond_action.do_action_string
                    new_session.commit()

        # Set PID Method and start method from beginning
        if cond_action.action_type == 'method_pid':
            pid = db_retrieve_table_daemon(
                PID, unique_id=cond_action.do_unique_id, entry='first')
            message += " Set Method of PID {unique_id} ({id}, {name}).".format(
                unique_id=cond_action.do_unique_id,
                id=pid.id,
                name=pid.name)

            # Instruct method to start
            with session_scope(MYCODO_DB_PATH) as new_session:
                mod_pid = new_session.query(PID).filter(
                    PID.unique_id == cond_action.do_unique_id).first()
                mod_pid.method_start_time = 'Ready'
                new_session.commit()

            pid = db_retrieve_table_daemon(
                PID, unique_id=cond_action.do_unique_id, entry='first')
            if pid.is_activated:
                method_pid = threading.Thread(
                    target=control.pid_set,
                    args=(pid.unique_id,
                          'method',
                          cond_action.do_action_string,))
                method_pid.start()
            else:
                with session_scope(MYCODO_DB_PATH) as new_session:
                    mod_pid = new_session.query(PID).filter(
                        PID.unique_id == cond_action.do_unique_id).first()
                    mod_pid.method_id = cond_action.do_action_string
                    new_session.commit()

        # Email the Conditional message. Optionally capture a photo or
        # video and attach to the email.
        if cond_action.action_type in [
                'email', 'photo_email', 'video_email']:

            if (email_count >= smtp_max_count and
                    time.time() < smtp_wait_timer):
                allowed_to_send_notice = False
            else:
                if time.time() > smtp_wait_timer:
                    with session_scope(MYCODO_DB_PATH) as new_session:
                        mod_smtp = new_session.query(SMTP).first()
                        mod_smtp.email_count = 0
                        mod_smtp.smtp_wait_timer = time.time() + 3600
                        new_session.commit()
                allowed_to_send_notice = True

            with session_scope(MYCODO_DB_PATH) as new_session:
                mod_smtp = new_session.query(SMTP).first()
                mod_smtp.email_count += 1
                new_session.commit()

            # If the emails per hour limit has not been exceeded
            if allowed_to_send_notice:
                message += " Notify {email}.".format(
                    email=cond_action.do_action_string)
                # attachment_type != False indicates to
                # attach a photo or video
                if cond_action.action_type == 'photo_email':
                    message += " Photo attached to email."
                    attachment_type = 'still'
                elif cond_action.action_type == 'video_email':
                    message += " Video attached to email."
                    attachment_type = 'video'

                if single_action and cond_action.do_action_string:
                    smtp = db_retrieve_table_daemon(SMTP, entry='first')
                    send_email(smtp.host, smtp.ssl, smtp.port,
                               smtp.user, smtp.passw, smtp.email_from,
                               [cond_action.do_action_string], message,
                               attachment_file, attachment_type)
                else:
                    email_recipients.append(cond_action.do_action_string)
            else:
                logger_actions.error(
                    "Wait {sec:.0f} seconds to email again.".format(
                        sec=smtp_wait_timer - time.time()))

        if cond_action.action_type == 'flash_lcd_on':
            lcd = db_retrieve_table_daemon(
                LCD, unique_id=cond_action.do_unique_id)
            message += " LCD {unique_id} ({id}, {name}) Flash On.".format(
                unique_id=cond_action.do_unique_id,
                id=lcd.id,
                name=lcd.name)

            start_flashing = threading.Thread(
                target=control.lcd_flash,
                args=(cond_action.do_unique_id, True,))
            start_flashing.start()

        if cond_action.action_type == 'flash_lcd_off':
            lcd = db_retrieve_table_daemon(
                LCD, unique_id=cond_action.do_unique_id)
            message += " LCD {unique_id} ({id}, {name}) Flash Off.".format(
                unique_id=cond_action.do_unique_id,
                id=lcd.id,
                name=lcd.name)

            start_flashing = threading.Thread(
                target=control.lcd_flash,
                args=(cond_action.do_unique_id, False,))
            start_flashing.start()

        if cond_action.action_type == 'lcd_backlight_off':
            lcd = db_retrieve_table_daemon(
                LCD, unique_id=cond_action.do_unique_id)
            message += " LCD {unique_id} ({id}, {name}) Backlight Off.".format(
                unique_id=cond_action.do_unique_id,
                id=lcd.id,
                name=lcd.name)

            start_flashing = threading.Thread(
                target=control.lcd_backlight,
                args=(cond_action.do_unique_id, False,))
            start_flashing.start()

        if cond_action.action_type == 'lcd_backlight_on':
            lcd = db_retrieve_table_daemon(
                LCD, unique_id=cond_action.do_unique_id)
            message += " LCD {unique_id} ({id}, {name}) Backlight On.".format(
                unique_id=cond_action.do_unique_id,
                id=lcd.id,
                name=lcd.name)

            start_flashing = threading.Thread(
                target=control.lcd_backlight,
                args=(cond_action.do_unique_id, True,))
            start_flashing.start()
    except Exception:
        logger_actions.exception("Error triggering action:")
        message += " Error while executing action! See Daemon log for details."

    if single_action:
        return message
    else:
        return message, note_tags, email_recipients, attachment_file, attachment_type
Example #13
0
def trigger_action(cond_action_id,
                   message='',
                   note_tags=None,
                   email_recipients=None,
                   attachment_file=None,
                   attachment_type=None,
                   single_action=False,
                   debug=False):
    """
    Trigger individual action

    If single_action == False, message, note_tags, email_recipients,
    attachment_file, and attachment_type are returned and may be
    passed back to this function in order to append to those lists.

    :param cond_action_id: unique_id of action
    :param message: message string to append to that will be sent back
    :param note_tags: list of note tags to use if creating a note
    :param email_recipients: list of email addresses to notify if sending an email
    :param attachment_file: string location of a file to attach to an email
    :param attachment_type: string type of email attachment
    :param single_action: True if only one action is being triggered, False if only one of multiple actions
    :param debug: determine if logging level should be DEBUG

    :return: message or (message, note_tags, email_recipients, attachment_file, attachment_type)
    """
    cond_action = db_retrieve_table_daemon(Actions, unique_id=cond_action_id)
    if not cond_action:
        message += 'Error: Action with ID {} not found!'.format(cond_action_id)
        if single_action:
            return message
        else:
            return message, note_tags, email_recipients, attachment_file, attachment_type

    logger_actions = logging.getLogger("mycodo.trigger_action_{id}".format(
        id=cond_action.unique_id.split('-')[0]))

    if debug:
        logger_actions.setLevel(logging.DEBUG)
    else:
        logger_actions.setLevel(logging.INFO)

    message += "\n[Action {id}]:".format(
        id=cond_action.unique_id.split('-')[0],
        action_type=cond_action.action_type)

    try:
        control = DaemonControl()

        # Pause
        if cond_action.action_type == 'pause_actions':
            message += " [{id}] Pause actions for {sec} seconds.".format(
                id=cond_action.id, sec=cond_action.pause_duration)

            time.sleep(cond_action.pause_duration)

        # Infrared Send
        if cond_action.action_type == 'infrared_send':
            command = 'irsend SEND_ONCE {remote} {code}'.format(
                remote=cond_action.remote, code=cond_action.code)
            output, err, stat = cmd_output(command)

            # Send more than once
            if cond_action.send_times > 1:
                for _ in range(cond_action.send_times - 1):
                    time.sleep(0.5)
                    output, err, stat = cmd_output(command)

            message += " [{id}] Infrared Send " \
                       "code '{code}', remote '{remote}', times: {times}:" \
                       "\nOutput: {out}" \
                       "\nError: {err}" \
                       "\nStatus: {stat}'.".format(
                            id=cond_action.id,
                            code=cond_action.code,
                            remote=cond_action.remote,
                            times=cond_action.send_times,
                            out=output,
                            err=err,
                            stat=stat)

        # Actuate output (duration)
        if (cond_action.action_type == 'output' and cond_action.do_unique_id
                and cond_action.do_output_state in ['on', 'off']):
            this_output = db_retrieve_table_daemon(
                Output, unique_id=cond_action.do_unique_id, entry='first')
            message += " Turn output {unique_id} ({id}, {name}) {state}".format(
                unique_id=cond_action.do_unique_id,
                id=this_output.id,
                name=this_output.name,
                state=cond_action.do_output_state)
            if (cond_action.do_output_state == 'on'
                    and cond_action.do_output_duration):
                message += " for {sec} seconds".format(
                    sec=cond_action.do_output_duration)
            message += "."

            output_on_off = threading.Thread(
                target=control.output_on_off,
                args=(
                    cond_action.do_unique_id,
                    cond_action.do_output_state,
                ),
                kwargs={'duration': cond_action.do_output_duration})
            output_on_off.start()

        # Actuate output (PWM)
        if (cond_action.action_type == 'output_pwm'
                and cond_action.do_unique_id
                and 0 <= cond_action.do_output_pwm <= 100):
            this_output = db_retrieve_table_daemon(
                Output, unique_id=cond_action.do_unique_id, entry='first')
            message += " Turn output {unique_id} ({id}, {name}) duty cycle to {duty_cycle}%.".format(
                unique_id=cond_action.do_unique_id,
                id=this_output.id,
                name=this_output.name,
                duty_cycle=cond_action.do_output_pwm)

            output_on = threading.Thread(
                target=control.output_on,
                args=(cond_action.do_unique_id, ),
                kwargs={'duty_cycle': cond_action.do_output_pwm})
            output_on.start()

        # Execute command in shell
        if cond_action.action_type == 'command':

            # Replace string variables with actual values
            command_str = cond_action.do_action_string

            # TODO: Maybe get this working again with the new measurement system
            # # Replace measurement variables
            # if last_measurement:
            #     command_str = command_str.replace(
            #         "((measure_{var}))".format(
            #             var=device_measurement), str(last_measurement))
            # if device and device.period:
            #     command_str = command_str.replace(
            #         "((measure_period))", str(device.period))
            # if input_dev:
            #     command_str = command_str.replace(
            #         "((measure_location))", str(input_dev.location))
            # if input_dev and device_measurement == input_dev.measurements:
            #     command_str = command_str.replace(
            #         "((measure_linux_command))", str(last_measurement))
            #
            # # Replace output variables
            # if output:
            #     if output.pin:
            #         command_str = command_str.replace(
            #             "((output_pin))", str(output.pin))
            #     if output_state:
            #         command_str = command_str.replace(
            #             "((output_action))", str(output_state))
            #     if on_duration:
            #         command_str = command_str.replace(
            #             "((output_duration))", str(on_duration))
            #     if duty_cycle:
            #         command_str = command_str.replace(
            #             "((output_pwm))", str(duty_cycle))
            #
            # # Replace edge variables
            # if edge:
            #     command_str = command_str.replace(
            #         "((edge_state))", str(edge))

            message += " Execute '{com}' ".format(com=command_str)

            _, _, cmd_status = cmd_output(command_str)

            message += "(return status: {stat}).".format(stat=cmd_status)

        # Create Note
        if cond_action.action_type == 'create_note':
            tag_name = db_retrieve_table_daemon(
                NoteTags, unique_id=cond_action.do_action_string).name

            message += " Create note with tag '{}'.".format(tag_name)
            if single_action and cond_action.do_action_string:
                list_tags = []
                check_tag = db_retrieve_table_daemon(
                    NoteTags, unique_id=cond_action.do_action_string)
                if check_tag:
                    list_tags.append(cond_action.do_action_string)

                if list_tags:
                    with session_scope(MYCODO_DB_PATH) as db_session:
                        new_note = Notes()
                        new_note.name = 'Action'
                        new_note.tags = ','.join(list_tags)
                        new_note.note = message
                        db_session.add(new_note)
            else:
                note_tags.append(cond_action.do_action_string)

        # Capture photo
        # If emailing later, store location of photo as attachment_file
        if cond_action.action_type in ['photo', 'photo_email']:
            this_camera = db_retrieve_table_daemon(
                Camera, unique_id=cond_action.do_unique_id, entry='first')
            message += "  Capturing photo with camera {unique_id} ({id}, {name}).".format(
                unique_id=cond_action.do_unique_id,
                id=this_camera.id,
                name=this_camera.name)
            camera_still = db_retrieve_table_daemon(
                Camera, unique_id=cond_action.do_unique_id)
            attachment_path_file = camera_record('photo',
                                                 camera_still.unique_id)
            attachment_file = os.path.join(attachment_path_file[0],
                                           attachment_path_file[1])

        # Capture video
        if cond_action.action_type in ['video', 'video_email']:
            this_camera = db_retrieve_table_daemon(
                Camera, unique_id=cond_action.do_unique_id, entry='first')
            message += "  Capturing video with camera {unique_id} ({id}, {name}).".format(
                unique_id=cond_action.do_unique_id,
                id=this_camera.id,
                name=this_camera.name)
            camera_stream = db_retrieve_table_daemon(
                Camera, unique_id=cond_action.do_unique_id)
            attachment_path_file = camera_record(
                'video',
                camera_stream.unique_id,
                duration_sec=cond_action.do_camera_duration)
            attachment_file = os.path.join(attachment_path_file[0],
                                           attachment_path_file[1])

        # Activate Controller
        if cond_action.action_type == 'activate_controller':
            (controller_type, controller_object,
             controller_entry) = which_controller(cond_action.do_unique_id)
            message += " Activate Controller {unique_id} ({id}, {name}).".format(
                unique_id=cond_action.do_unique_id,
                id=controller_entry.id,
                name=controller_entry.name)
            if controller_entry.is_activated:
                message += " Notice: Controller is already active!"
            else:
                with session_scope(MYCODO_DB_PATH) as new_session:
                    mod_cont = new_session.query(controller_object).filter(
                        controller_object.unique_id ==
                        cond_action.do_unique_id).first()
                    mod_cont.is_activated = True
                    new_session.commit()
                activate_controller = threading.Thread(
                    target=control.controller_activate,
                    args=(
                        controller_type,
                        cond_action.do_unique_id,
                    ))
                activate_controller.start()

        # Deactivate Controller
        if cond_action.action_type == 'deactivate_controller':
            (controller_type, controller_object,
             controller_entry) = which_controller(cond_action.do_unique_id)
            message += " Deactivate Controller {unique_id} ({id}, {name}).".format(
                unique_id=cond_action.do_unique_id,
                id=controller_entry.id,
                name=controller_entry.name)
            if not controller_entry.is_activated:
                message += " Notice: Controller is already inactive!"
            else:
                with session_scope(MYCODO_DB_PATH) as new_session:
                    mod_cont = new_session.query(controller_object).filter(
                        controller_object.unique_id ==
                        cond_action.do_unique_id).first()
                    mod_cont.is_activated = False
                    new_session.commit()
                deactivate_controller = threading.Thread(
                    target=control.controller_deactivate,
                    args=(
                        controller_type,
                        cond_action.do_unique_id,
                    ))
                deactivate_controller.start()

        # Resume PID controller
        if cond_action.action_type == 'resume_pid':
            pid = db_retrieve_table_daemon(PID,
                                           unique_id=cond_action.do_unique_id,
                                           entry='first')
            message += " Resume PID {unique_id} ({id}, {name}).".format(
                unique_id=cond_action.do_unique_id, id=pid.id, name=pid.name)
            if not pid.is_paused:
                message += " Notice: PID is not paused!"
            elif pid.is_activated:
                with session_scope(MYCODO_DB_PATH) as new_session:
                    mod_pid = new_session.query(PID).filter(
                        PID.unique_id == cond_action.do_unique_id).first()
                    mod_pid.is_paused = False
                    new_session.commit()
                resume_pid = threading.Thread(
                    target=control.pid_resume,
                    args=(cond_action.do_unique_id, ))
                resume_pid.start()

        # Pause PID controller
        if cond_action.action_type == 'pause_pid':
            pid = db_retrieve_table_daemon(PID,
                                           unique_id=cond_action.do_unique_id,
                                           entry='first')
            message += " Pause PID {unique_id} ({id}, {name}).".format(
                unique_id=cond_action.do_unique_id, id=pid.id, name=pid.name)
            if pid.is_paused:
                message += " Notice: PID is already paused!"
            elif pid.is_activated:
                with session_scope(MYCODO_DB_PATH) as new_session:
                    mod_pid = new_session.query(PID).filter(
                        PID.unique_id == cond_action.do_unique_id).first()
                    mod_pid.is_paused = True
                    new_session.commit()
                pause_pid = threading.Thread(target=control.pid_pause,
                                             args=(cond_action.do_unique_id, ))
                pause_pid.start()

        # Set PID Setpoint
        if cond_action.action_type == 'setpoint_pid':
            pid = db_retrieve_table_daemon(PID,
                                           unique_id=cond_action.do_unique_id,
                                           entry='first')
            message += " Set Setpoint of PID {unique_id} ({id}, {name}).".format(
                unique_id=cond_action.do_unique_id, id=pid.id, name=pid.name)
            if pid.is_activated:
                setpoint_pid = threading.Thread(
                    target=control.pid_set,
                    args=(
                        pid.unique_id,
                        'setpoint',
                        float(cond_action.do_action_string),
                    ))
                setpoint_pid.start()
            else:
                with session_scope(MYCODO_DB_PATH) as new_session:
                    mod_pid = new_session.query(PID).filter(
                        PID.unique_id == cond_action.do_unique_id).first()
                    mod_pid.setpoint = float(cond_action.do_action_string)
                    new_session.commit()

        # Raise PID Setpoint
        if cond_action.action_type == 'setpoint_pid_raise':
            pid = db_retrieve_table_daemon(PID,
                                           unique_id=cond_action.do_unique_id,
                                           entry='first')
            new_setpoint = pid.setpoint + float(cond_action.do_action_string)
            message += " Raise Setpoint of PID {unique_id} by {amt}, to {sp} ({id}, {name}).".format(
                unique_id=cond_action.do_unique_id,
                amt=float(cond_action.do_action_string),
                sp=new_setpoint,
                id=pid.id,
                name=pid.name)
            if pid.is_activated:
                setpoint_pid = threading.Thread(target=control.pid_set,
                                                args=(
                                                    pid.unique_id,
                                                    'setpoint',
                                                    new_setpoint,
                                                ))
                setpoint_pid.start()
            else:
                with session_scope(MYCODO_DB_PATH) as new_session:
                    mod_pid = new_session.query(PID).filter(
                        PID.unique_id == cond_action.do_unique_id).first()
                    mod_pid.setpoint = new_setpoint
                    new_session.commit()

        # Lower PID Setpoint
        if cond_action.action_type == 'setpoint_pid_lower':
            pid = db_retrieve_table_daemon(PID,
                                           unique_id=cond_action.do_unique_id,
                                           entry='first')
            new_setpoint = pid.setpoint - float(cond_action.do_action_string)
            message += " Lower Setpoint of PID {unique_id} by {amt}, to {sp} ({id}, {name}).".format(
                unique_id=cond_action.do_unique_id,
                amt=float(cond_action.do_action_string),
                sp=new_setpoint,
                id=pid.id,
                name=pid.name)
            if pid.is_activated:
                setpoint_pid = threading.Thread(target=control.pid_set,
                                                args=(
                                                    pid.unique_id,
                                                    'setpoint',
                                                    new_setpoint,
                                                ))
                setpoint_pid.start()
            else:
                with session_scope(MYCODO_DB_PATH) as new_session:
                    mod_pid = new_session.query(PID).filter(
                        PID.unique_id == cond_action.do_unique_id).first()
                    mod_pid.setpoint = new_setpoint
                    new_session.commit()

        # Set PID Method and start method from beginning
        if cond_action.action_type == 'method_pid':
            pid = db_retrieve_table_daemon(PID,
                                           unique_id=cond_action.do_unique_id,
                                           entry='first')
            message += " Set Method of PID {unique_id} ({id}, {name}).".format(
                unique_id=cond_action.do_unique_id, id=pid.id, name=pid.name)

            # Instruct method to start
            with session_scope(MYCODO_DB_PATH) as new_session:
                mod_pid = new_session.query(PID).filter(
                    PID.unique_id == cond_action.do_unique_id).first()
                mod_pid.method_start_time = 'Ready'
                new_session.commit()

            pid = db_retrieve_table_daemon(PID,
                                           unique_id=cond_action.do_unique_id,
                                           entry='first')
            if pid.is_activated:
                method_pid = threading.Thread(target=control.pid_set,
                                              args=(
                                                  pid.unique_id,
                                                  'method',
                                                  cond_action.do_action_string,
                                              ))
                method_pid.start()
            else:
                with session_scope(MYCODO_DB_PATH) as new_session:
                    mod_pid = new_session.query(PID).filter(
                        PID.unique_id == cond_action.do_unique_id).first()
                    mod_pid.method_id = cond_action.do_action_string
                    new_session.commit()

        # Email the Conditional message to a single recipient.
        # Optionally capture a photo or video and attach to the email.
        if cond_action.action_type in ['email', 'photo_email', 'video_email']:

            message += " Notify {email}.".format(
                email=cond_action.do_action_string)
            # attachment_type != False indicates to
            # attach a photo or video
            if cond_action.action_type == 'photo_email':
                message += " Photo attached to email."
                attachment_type = 'still'
            elif cond_action.action_type == 'video_email':
                message += " Video attached to email."
                attachment_type = 'video'

            if single_action:
                # If the emails per hour limit has not been exceeded
                smtp_wait_timer, allowed_to_send_notice = check_allowed_to_email(
                )
                if allowed_to_send_notice and cond_action.do_action_string:
                    smtp = db_retrieve_table_daemon(SMTP, entry='first')
                    send_email(smtp.host, smtp.ssl, smtp.port, smtp.user,
                               smtp.passw, smtp.email_from,
                               [cond_action.do_action_string], message,
                               attachment_file, attachment_type)
                else:
                    logger_actions.error(
                        "Wait {sec:.0f} seconds to email again.".format(
                            sec=smtp_wait_timer - time.time()))
            else:
                email_recipients.append(cond_action.do_action_string)

            # Email the Conditional message to multiple recipients
            # Optionally capture a photo or video and attach to the email.
            if cond_action.action_type in ['email_multiple']:

                message += " Notify {email}.".format(
                    email=cond_action.do_action_string)
                # attachment_type != False indicates to
                # attach a photo or video
                if cond_action.action_type == 'photo_email':
                    message += " Photo attached to email."
                    attachment_type = 'still'
                elif cond_action.action_type == 'video_email':
                    message += " Video attached to email."
                    attachment_type = 'video'

                if single_action:
                    # If the emails per hour limit has not been exceeded
                    smtp_wait_timer, allowed_to_send_notice = check_allowed_to_email(
                    )
                    if allowed_to_send_notice and cond_action.do_action_string:
                        smtp = db_retrieve_table_daemon(SMTP, entry='first')
                        send_email(smtp.host, smtp.ssl, smtp.port, smtp.user,
                                   smtp.passw, smtp.email_from,
                                   cond_action.do_action_string.split(','),
                                   message, attachment_file, attachment_type)
                    else:
                        logger_actions.error(
                            "Wait {sec:.0f} seconds to email again.".format(
                                sec=smtp_wait_timer - time.time()))
                else:
                    email_recipients.extend(
                        cond_action.do_action_string.split(','))

        if cond_action.action_type == 'flash_lcd_on':
            lcd = db_retrieve_table_daemon(LCD,
                                           unique_id=cond_action.do_unique_id)
            message += " LCD {unique_id} ({id}, {name}) Flash On.".format(
                unique_id=cond_action.do_unique_id, id=lcd.id, name=lcd.name)

            start_flashing = threading.Thread(target=control.lcd_flash,
                                              args=(
                                                  cond_action.do_unique_id,
                                                  True,
                                              ))
            start_flashing.start()

        if cond_action.action_type == 'flash_lcd_off':
            lcd = db_retrieve_table_daemon(LCD,
                                           unique_id=cond_action.do_unique_id)
            message += " LCD {unique_id} ({id}, {name}) Flash Off.".format(
                unique_id=cond_action.do_unique_id, id=lcd.id, name=lcd.name)

            start_flashing = threading.Thread(target=control.lcd_flash,
                                              args=(
                                                  cond_action.do_unique_id,
                                                  False,
                                              ))
            start_flashing.start()

        if cond_action.action_type == 'lcd_backlight_off':
            lcd = db_retrieve_table_daemon(LCD,
                                           unique_id=cond_action.do_unique_id)
            message += " LCD {unique_id} ({id}, {name}) Backlight Off.".format(
                unique_id=cond_action.do_unique_id, id=lcd.id, name=lcd.name)

            start_flashing = threading.Thread(target=control.lcd_backlight,
                                              args=(
                                                  cond_action.do_unique_id,
                                                  False,
                                              ))
            start_flashing.start()

        if cond_action.action_type == 'lcd_backlight_on':
            lcd = db_retrieve_table_daemon(LCD,
                                           unique_id=cond_action.do_unique_id)
            message += " LCD {unique_id} ({id}, {name}) Backlight On.".format(
                unique_id=cond_action.do_unique_id, id=lcd.id, name=lcd.name)

            start_flashing = threading.Thread(target=control.lcd_backlight,
                                              args=(
                                                  cond_action.do_unique_id,
                                                  True,
                                              ))
            start_flashing.start()
    except Exception:
        logger_actions.exception("Error triggering action:")
        message += " Error while executing action! See Daemon log for details."

    logger_actions.debug("Message: {}".format(message))
    logger_actions.debug("Note Tags: {}".format(note_tags))
    logger_actions.debug("Email Recipients: {}".format(email_recipients))
    logger_actions.debug("Attachment Files: {}".format(attachment_file))
    logger_actions.debug("Attachment Type: {}".format(attachment_type))

    if single_action:
        return message
    else:
        return message, note_tags, email_recipients, attachment_file, attachment_type
Example #14
0
    def run_action(self, message, dict_vars):
        try:
            controller_id = dict_vars["value"]["camera_id"]
        except:
            controller_id = self.controller_id

        try:
            email_recipients = dict_vars["value"]["email_address"]
        except:
            if "," in self.email:
                email_recipients = self.email.split(",")
            else:
                email_recipients = [self.email]

        if not email_recipients:
            msg = f" Error: No recipients specified."
            self.logger.error(msg)
            message += msg
            return message

        try:
            message_send = dict_vars["value"]["message"]
        except:
            message_send = None

        this_camera = db_retrieve_table_daemon(Camera,
                                               unique_id=controller_id,
                                               entry='first')

        if not this_camera:
            msg = f" Error: Camera with ID '{controller_id}' not found."
            message += msg
            self.logger.error(msg)
            return message

        path, filename = camera_record('photo', this_camera.unique_id)
        if path and filename:
            attachment_file = os.path.join(path, filename)
            # If the emails per hour limit has not been exceeded
            smtp_wait_timer, allowed_to_send_notice = check_allowed_to_email()
            if allowed_to_send_notice:
                message += f" Email '{','.join(email_recipients)}' with photo attached."
                if not message_send:
                    message_send = message
                smtp = db_retrieve_table_daemon(SMTP, entry='first')
                send_email(smtp.host,
                           smtp.protocol,
                           smtp.port,
                           smtp.user,
                           smtp.passw,
                           smtp.email_from,
                           email_recipients,
                           message_send,
                           attachment_file=attachment_file,
                           attachment_type="still")
            else:
                self.logger.error(
                    f"Wait {smtp_wait_timer - time.time():.0f} seconds to email again."
                )
        else:
            message += " An image could not be acquired. Not sending email."

        self.logger.debug(f"Message: {message}")

        return message