Example #1
0
def action_email(logger_actions, cond_action, message, single_action,
                 attachment_file, email_recipients, attachment_type):
    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.protocol, 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.protocol, smtp.port, smtp.user,
                           smtp.passw, smtp.email_from,
                           cond_action.do_action_string.split(','), message,
                           attachment_file, attachment_type)
            else:
                logger.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(','))
    return message, email_recipients, attachment_type
Example #2
0
def settings_alert_mod(form_mod_alert):
    """ Modify Alert settings """
    action = '{action} {controller}'.format(
        action=gettext("Modify"), controller=gettext("Alert Settings"))
    error = []

    try:
        if form_mod_alert.validate():
            mod_smtp = SMTP.query.one()
            if form_mod_alert.send_test.data:
                send_email(mod_smtp.host, mod_smtp.ssl, mod_smtp.port,
                           mod_smtp.user, mod_smtp.passw, mod_smtp.email_from,
                           form_mod_alert.send_test_to_email.data,
                           "This is a test email from Mycodo")
                flash(
                    gettext(
                        "Test email sent to %(recip)s. Check your "
                        "inbox to see if it was successful.",
                        recip=form_mod_alert.send_test_to_email.data),
                    "success")
                return redirect(url_for('routes_settings.settings_alerts'))
            else:
                mod_smtp.host = form_mod_alert.smtp_host.data
                mod_smtp.port = form_mod_alert.smtp_port.data
                mod_smtp.ssl = form_mod_alert.smtp_ssl.data
                mod_smtp.user = form_mod_alert.smtp_user.data
                if form_mod_alert.smtp_password.data:
                    mod_smtp.passw = form_mod_alert.smtp_password.data
                mod_smtp.email_from = form_mod_alert.smtp_from_email.data
                mod_smtp.hourly_max = form_mod_alert.smtp_hourly_max.data
                db.session.commit()
        else:
            flash_form_errors(form_mod_alert)
    except Exception as except_msg:
        error.append(except_msg)

    flash_success_errors(error, action,
                         url_for('routes_settings.settings_alerts'))
Example #3
0
    def run_action(self, message, dict_vars):
        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 = False

        # 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 '{self.email}'."
            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)
        else:
            self.logger.error(
                f"Wait {smtp_wait_timer - time.time():.0f} seconds to email again.")

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

        return message
def trigger_function_actions(function_id, message='', debug=False):
    """
    Execute the Actions belonging to a particular Function

    :param function_id:
    :param message: The message generated from the conditional check
    :param debug: determine if logging level should be DEBUG
    :return:
    """
    logger_actions = logging.getLogger(
        "mycodo.trigger_function_actions_{id}".format(
            id=function_id.split('-')[0]))

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

    # 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 = []

    # List of tags to add to a note
    note_tags = []

    attachment_file = None
    attachment_type = None

    actions = db_retrieve_table_daemon(Actions)
    actions = actions.filter(
        Actions.function_id == function_id).all()

    for cond_action in actions:
        (message,
         note_tags,
         email_recipients,
         attachment_file,
         attachment_type) = trigger_action(
            cond_action.unique_id,
            message=message,
            single_action=False,
            note_tags=note_tags,
            email_recipients=email_recipients,
            attachment_file=attachment_file,
            attachment_type=attachment_type,
            debug=debug)

    # 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:
        # 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:
            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)
        else:
            logger_actions.error(
                "Wait {sec:.0f} seconds to email again.".format(
                    sec=smtp_wait_timer - time.time()))

    # Create a note with the tags from the unique_ids in the list note_tags
    if note_tags:
        list_tags = []
        for each_note_tag_id in note_tags:
            check_tag = db_retrieve_table_daemon(
                NoteTags, unique_id=each_note_tag_id)
            if check_tag:
                list_tags.append(each_note_tag_id)
        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)

    logger_actions.debug("Message: {}".format(message))
Example #5
0
def trigger_function_actions(function_id,
                             message='',
                             last_measurement=None,
                             device_id=None,
                             device_measurement=None,
                             edge=None,
                             output_state=None,
                             on_duration=None,
                             duty_cycle=None):
    """
    Execute the Actions belonging to a particular Function

    :param function_id:
    :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_actions = logging.getLogger(
        "mycodo.trigger_function_actions_{id}".format(
            id=function_id.split('-')[0]))

    # 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 = []

    # List of tags to add to a note
    note_tags = []

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

    actions = db_retrieve_table_daemon(Actions)
    actions = actions.filter(Actions.function_id == function_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 actions:
        (message, note_tags, email_recipients, attachment_file,
         attachment_type) = trigger_action(cond_action.unique_id,
                                           message=message,
                                           single_action=False,
                                           note_tags=note_tags,
                                           email_recipients=email_recipients,
                                           attachment_file=attachment_file,
                                           attachment_type=attachment_type)

    # 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)

    # Create a note with the tags from the unique_ids in the list note_tags
    if note_tags:
        list_tags = []
        for each_note_tag_id in note_tags:
            check_tag = db_retrieve_table_daemon(NoteTags,
                                                 unique_id=each_note_tag_id)
            if check_tag:
                list_tags.append(each_note_tag_id)
        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)

    logger_actions.debug(message)
Example #6
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 #7
0
def forgot_password():
    """Page to send password reset email"""
    error = []
    form_forgot_password = forms_authentication.ForgotPassword()

    if request.method == 'POST':
        if form_forgot_password.submit.data:
            if not form_forgot_password.username.data:
                user = None
                error.append("User name cannot be left blank")
            else:
                user = User.query.filter(User.name == form_forgot_password.
                                         username.data.lower()).first()

            if not error:
                # test if user name exists
                if user:
                    # check last time requested
                    if user.password_reset_last_request:
                        difference = datetime.datetime.now(
                        ) - user.password_reset_last_request
                        if difference.seconds < 1800:
                            error.append("Requesting too many password resets")

                    role = Role.query.filter(Role.id == user.role_id).first()
                    if not role or not role.reset_password:
                        error.append("Cannot reset password of this user")

            if not error:
                user.password_reset_code = generate_reset_code(30)
                user.password_reset_code_expiration = datetime.datetime.now(
                ) + datetime.timedelta(minutes=30)
                db.session.commit()

                smtp = SMTP.query.one()
                hostname = socket.gethostname()
                subject = "Mycodo Password Reset ({})".format(hostname)
                msg = "A password reset has been requested for user {user} on host {host} with your email " \
                      "address.\n\nIf you did not initiate this, you can disregard this email.\n\nIf you would " \
                      "like to reset your password, the password reset code below will be good for the next 30 " \
                      "minutes.\n\n{code}".format(
                        user=user.name, host=hostname, code=user.password_reset_code)
                send_email(smtp.host,
                           smtp.protocol,
                           smtp.port,
                           smtp.user,
                           smtp.passw,
                           smtp.email_from,
                           user.email,
                           msg,
                           subject=subject)

            if error:
                for each_error in error:
                    flash(each_error, "error")
            else:
                flash(
                    gettext(
                        "If the user name exists, it has a valid email associated with it, and the email "
                        "server settings are configured correctly, an email will be sent with instructions "
                        "for resetting your password."), "success")
                return redirect(
                    url_for('routes_password_reset.reset_password'))

    return render_template('forgot_password.html',
                           form_forgot_password=form_forgot_password)
Example #8
0
def trigger_function_actions(function_id, message=''):
    """
    Execute the Actions belonging to a particular Function

    :param function_id:
    :param message: The message generated from the conditional check
    :return:
    """
    logger_actions = logging.getLogger(
        "mycodo.trigger_function_actions_{id}".format(
            id=function_id.split('-')[0]))

    # 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 = []

    # List of tags to add to a note
    note_tags = []

    attachment_file = None
    attachment_type = None

    actions = db_retrieve_table_daemon(Actions)
    actions = actions.filter(Actions.function_id == function_id).all()

    for cond_action in actions:
        (message, note_tags, email_recipients, attachment_file,
         attachment_type) = trigger_action(cond_action.unique_id,
                                           message=message,
                                           single_action=False,
                                           note_tags=note_tags,
                                           email_recipients=email_recipients,
                                           attachment_file=attachment_file,
                                           attachment_type=attachment_type)

    # 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)

    # Create a note with the tags from the unique_ids in the list note_tags
    if note_tags:
        list_tags = []
        for each_note_tag_id in note_tags:
            check_tag = db_retrieve_table_daemon(NoteTags,
                                                 unique_id=each_note_tag_id)
            if check_tag:
                list_tags.append(each_note_tag_id)
        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)

    logger_actions.debug(message)
Example #9
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 #10
0
def trigger_function_actions(function_id, message=''):
    """
    Execute the Actions belonging to a particular Function

    :param function_id:
    :param message: The message generated from the conditional check
    :return:
    """
    logger_actions = logging.getLogger("mycodo.trigger_function_actions_{id}".format(
        id=function_id.split('-')[0]))

    # 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 = []

    # List of tags to add to a note
    note_tags = []

    attachment_file = None
    attachment_type = None

    actions = db_retrieve_table_daemon(Actions)
    actions = actions.filter(
        Actions.function_id == function_id).all()

    for cond_action in actions:
        (message,
         note_tags,
         email_recipients,
         attachment_file,
         attachment_type) = trigger_action(cond_action.unique_id,
                                           message=message,
                                           single_action=False,
                                           note_tags=note_tags,
                                           email_recipients=email_recipients,
                                           attachment_file=attachment_file,
                                           attachment_type=attachment_type)

    # 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)

    # Create a note with the tags from the unique_ids in the list note_tags
    if note_tags:
        list_tags = []
        for each_note_tag_id in note_tags:
            check_tag = db_retrieve_table_daemon(
                NoteTags, unique_id=each_note_tag_id)
            if check_tag:
                list_tags.append(each_note_tag_id)
        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)

    logger_actions.debug(message)
Example #11
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 #12
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 #13
0
def forgot_password():
    """Page to send password reset email"""
    error = []
    form_forgot_password = forms_authentication.ForgotPassword()

    if request.method == 'POST':
        if form_forgot_password.submit.data:
            if not form_forgot_password.username.data:
                user = None
                error.append("User name cannot be left blank")
            else:
                user = User.query.filter(User.name == form_forgot_password.username.data.lower()).first()

            if not error:
                # test if user name exists
                if user:
                    # check last time requested
                    if user.password_reset_last_request:
                        difference = datetime.datetime.now() - user.password_reset_last_request
                        if difference.seconds < 1800:
                            error.append("Requesting too many password resets")

                    role = Role.query.filter(Role.id == user.role_id).first()
                    if not role or not role.reset_password:
                        error.append("Cannot reset password of this user")

            if not error:
                if user:
                    user.password_reset_code = generate_reset_code(30)
                    user.password_reset_code_expiration = datetime.datetime.now() + datetime.timedelta(minutes=30)
                    db.session.commit()

                hostname = socket.gethostname()
                now = datetime.datetime.now()

                if form_forgot_password.reset_method.data == 'email':
                    smtp = SMTP.query.first()
                    if user and smtp.host and smtp.protocol and smtp.port and smtp.user and smtp.passw:
                        subject = "Mycodo Password Reset ({})".format(hostname)
                        msg = "A password reset has been requested for user {user} on host {host} at {time} " \
                              "with your email address.\n\nIf you did not initiate this, you can disregard " \
                              "this email.\n\nIf you would like to reset your password, the password reset " \
                              "code below will be good for the next 30 minutes.\n\n{code}".format(
                                user=user.name,
                                host=hostname,
                                time=now.strftime("%d/%m/%Y %H:%M"),
                                code=user.password_reset_code)
                        send_email(
                            smtp.host, smtp.protocol, smtp.port,
                            smtp.user, smtp.passw, smtp.email_from,
                            user.email, msg, subject=subject)
                    flash("If the user name exists, it has a valid email associated with it, and the email "
                          "server settings are configured correctly, an email will be sent with instructions "
                          "for resetting your password.", "success")
                elif form_forgot_password.reset_method.data == 'file':
                    save_path = os.path.join(INSTALL_DIRECTORY, "password_reset.txt")
                    if user:
                        with open(save_path, 'w') as out_file:
                            msg = "A password reset has been requested for user {user} on host {host} at " \
                                  "{time}.\n\nIf you would like to reset your password, the password reset " \
                                  "code below will be good for the next 30 minutes.\n\n{code}\n".format(
                                    user=user.name,
                                    host=hostname,
                                    time=now.strftime("%d/%m/%Y %H:%M"),
                                    code=user.password_reset_code)
                            out_file.write(msg)
                    flash("If the user name exists, a file will be created at {} with instructions "
                          "for resetting your password.".format(save_path), "success")
            if error:
                for each_error in error:
                    flash(each_error, "error")
            else:
                return redirect(url_for('routes_password_reset.reset_password'))

    return render_template('forgot_password.html',
                           form_forgot_password=form_forgot_password)
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