def upgrade():
    with session_scope(MYCODO_DB_PATH) as conditional_sess:
        for each_conditional in conditional_sess.query(Conditional).all():
            if each_conditional.conditional_statement:

                # Get conditions for this conditional
                with session_scope(MYCODO_DB_PATH) as condition_sess:
                    for each_condition in condition_sess.query(ConditionalConditions).all():
                        # Replace {ID} with measure("{ID}")
                        id_str = '{{{id}}}'.format(id=each_condition.unique_id.split('-')[0])
                        new_str = 'measure("{{{id}}}")'.format(id=each_condition.unique_id.split('-')[0])
                        if id_str in each_conditional.conditional_statement:
                            each_conditional.conditional_statement = each_conditional.conditional_statement.replace(id_str, new_str)

                        # Replace print(1) with run_all_actions()
                        new_str = 'run_all_actions()'
                        if id_str in each_conditional.conditional_statement:
                            each_conditional.conditional_statement = each_conditional.conditional_statement.replace(
                                'print(1)', new_str)
                            each_conditional.conditional_statement = each_conditional.conditional_statement.replace(
                                'print("1")', new_str)
                            each_conditional.conditional_statement = each_conditional.conditional_statement.replace(
                                "print('1')", new_str)

        conditional_sess.commit()
Example #2
0
    def get_method_output(self, method_id):
        """ Get output variable from method """
        this_controller = db_retrieve_table_daemon(
            Trigger, unique_id=self.function_id)
        setpoint, ended = calculate_method_setpoint(
            method_id,
            Trigger,
            this_controller,
            Method,
            MethodData,
            self.logger)

        if setpoint is not None:
            if setpoint > 100:
                setpoint = 100
            elif setpoint < 0:
                setpoint = 0

        if ended:
            with session_scope(MYCODO_DB_PATH) as db_session:
                mod_conditional = db_session.query(Trigger)
                mod_conditional = mod_conditional.filter(
                    Trigger.unique_id == self.function_id).first()
                mod_conditional.is_activated = False
                db_session.commit()
            self.is_activated = False
            self.stop_controller()

        return setpoint, ended
Example #3
0
    def check_mycodo_upgrade_exists(self, now):
        """Check for any new Mycodo releases on github"""
        releases = []
        upgrade_available = False
        try:
            maj_version = int(MYCODO_VERSION.split('.')[0])
            releases = github_releases(maj_version)
        except Exception:
            self.logger.error("Could not determine local mycodo version or "
                              "online release versions. Upgrade checks can "
                              "be disabled in the Mycodo configuration.")

        try:
            if len(releases):
                if parse_version(releases[0]) > parse_version(MYCODO_VERSION):
                    upgrade_available = True
                    if now > self.timer_upgrade_message:
                        # Only display message in log every 10 days
                        self.timer_upgrade_message = time.time() + 864000
                        self.logger.info(
                            "A new version of Mycodo is available. Upgrade "
                            "through the web interface under Config -> Upgrade. "
                            "This message will repeat every 10 days unless "
                            "Mycodo is upgraded or upgrade checks are disabled.")

            with session_scope(MYCODO_DB_PATH) as new_session:
                mod_misc = new_session.query(Misc).first()
                if mod_misc.mycodo_upgrade_available != upgrade_available:
                    mod_misc.mycodo_upgrade_available = upgrade_available
                    new_session.commit()
        except Exception:
            self.logger.exception("Mycodo Upgrade Check ERROR")
Example #4
0
 def set_kd(self, d):
     """ Set Kd gain of the controller """
     self.Kd = float(d)
     with session_scope(MYCODO_DB_PATH) as db_session:
         mod_pid = db_session.query(PID).filter(
             PID.unique_id == self.pid_id).first()
         mod_pid.d = d
         db_session.commit()
     return "Kd set to {kd}".format(kd=self.Kd)
Example #5
0
 def set_ki(self, i):
     """ Set Ki gain of the controller """
     self.Ki = float(i)
     with session_scope(MYCODO_DB_PATH) as db_session:
         mod_pid = db_session.query(PID).filter(
             PID.unique_id == self.pid_id).first()
         mod_pid.i = i
         db_session.commit()
     return "Ki set to {ki}".format(ki=self.Ki)
Example #6
0
 def set_kp(self, p):
     """ Set Kp gain of the controller """
     self.Kp = float(p)
     with session_scope(MYCODO_DB_PATH) as db_session:
         mod_pid = db_session.query(PID).filter(
             PID.unique_id == self.pid_id).first()
         mod_pid.p = p
         db_session.commit()
     return "Kp set to {kp}".format(kp=self.Kp)
Example #7
0
 def set_setpoint(self, setpoint):
     """ Set the setpoint of PID """
     self.setpoint = float(setpoint)
     with session_scope(MYCODO_DB_PATH) as db_session:
         mod_pid = db_session.query(PID).filter(
             PID.unique_id == self.pid_id).first()
         mod_pid.setpoint = setpoint
         db_session.commit()
     return "Setpoint set to {sp}".format(sp=setpoint)
Example #8
0
def create_admin_user(user_db_uri):
    """ mycodo_flask exits if there is no user called admin. So we create one """

    with session_scope(user_db_uri) as db_session:
        if not db_session.query(Users).filter_by(user_restriction='admin').count():
            logging.info("--> Creating new 'test' user as an admin")
            db_session.add(Users(user_name='test', user_restriction='admin'))
            db_session.commit()
        else:
            logging.warning("--> Dirty User DB: Admin user was already setup in: '{uri}'".format(uri=user_db_uri))
Example #9
0
 def timelapse_check(self, camera, now):
     """ If time-lapses are active, take photo at predefined periods """
     try:
         if (camera.timelapse_started and
                     now > camera.timelapse_end_time):
             with session_scope(MYCODO_DB_PATH) as new_session:
                 mod_camera = new_session.query(Camera).filter(
                     Camera.unique_id == camera.unique_id).first()
                 mod_camera.timelapse_started = False
                 mod_camera.timelapse_paused = False
                 mod_camera.timelapse_start_time = None
                 mod_camera.timelapse_end_time = None
                 mod_camera.timelapse_interval = None
                 mod_camera.timelapse_next_capture = None
                 mod_camera.timelapse_capture_number = None
                 new_session.commit()
             self.refresh_daemon_camera_settings()
             self.logger.debug(
                 "Camera {id}: End of time-lapse.".format(id=camera.id))
         elif ((camera.timelapse_started and not camera.timelapse_paused) and
                       now > camera.timelapse_next_capture):
             # Ensure next capture is greater than now (in case of power failure/reboot)
             next_capture = camera.timelapse_next_capture
             capture_number = camera.timelapse_capture_number
             while now > next_capture:
                 # Update last capture and image number to latest before capture
                 next_capture += camera.timelapse_interval
                 capture_number += 1
             with session_scope(MYCODO_DB_PATH) as new_session:
                 mod_camera = new_session.query(Camera).filter(
                     Camera.unique_id == camera.unique_id).first()
                 mod_camera.timelapse_next_capture = next_capture
                 mod_camera.timelapse_capture_number = capture_number
                 new_session.commit()
             self.refresh_daemon_camera_settings()
             self.logger.debug(
                 "Camera {id}: Capturing time-lapse image".format(id=camera.id))
             # Capture image
             camera_record('timelapse', camera.unique_id)
     except Exception as except_msg:
         message = "Could not execute timelapse:" \
                   " {err}".format(err=except_msg)
         self.logger.exception(message)
Example #10
0
    def setup_method(self, method_id):
        """ Initialize method variables to start running a method """
        self.method_id = ''

        method = db_retrieve_table_daemon(Method, unique_id=method_id)
        method_data = db_retrieve_table_daemon(MethodData)
        method_data = method_data.filter(MethodData.method_id == method_id)
        method_data_repeat = method_data.filter(MethodData.duration_sec == 0).first()
        pid = db_retrieve_table_daemon(PID, unique_id=self.pid_id)
        self.method_type = method.method_type
        self.method_start_act = pid.method_start_time
        self.method_start_time = None
        self.method_end_time = None

        if self.method_type == 'Duration':
            if self.method_start_act == 'Ended':
                # Method has ended and hasn't been instructed to begin again
                pass
            elif (self.method_start_act == 'Ready' or
                    self.method_start_act is None):
                # Method has been instructed to begin
                now = datetime.datetime.now()
                self.method_start_time = now
                if method_data_repeat and method_data_repeat.duration_end:
                    self.method_end_time = now + datetime.timedelta(
                        seconds=float(method_data_repeat.duration_end))

                with session_scope(MYCODO_DB_PATH) as db_session:
                    mod_pid = db_session.query(PID).filter(
                        PID.unique_id == self.pid_id).first()
                    mod_pid.method_start_time = self.method_start_time
                    mod_pid.method_end_time = self.method_end_time
                    db_session.commit()
            else:
                # Method neither instructed to begin or not to
                # Likely there was a daemon restart ot power failure
                # Resume method with saved start_time
                self.method_start_time = datetime.datetime.strptime(
                    str(pid.method_start_time), '%Y-%m-%d %H:%M:%S.%f')
                if method_data_repeat and method_data_repeat.duration_end:
                    self.method_end_time = datetime.datetime.strptime(
                        str(pid.method_end_time), '%Y-%m-%d %H:%M:%S.%f')
                    if self.method_end_time > datetime.datetime.now():
                        self.logger.warning(
                            "Resuming method {id}: started {start}, "
                            "ends {end}".format(
                                id=method_id,
                                start=self.method_start_time,
                                end=self.method_end_time))
                    else:
                        self.method_start_act = 'Ended'
                else:
                    self.method_start_act = 'Ended'

        self.method_id = method_id
def upgrade():
    with session_scope(MYCODO_DB_PATH) as new_session:
        for each_input in new_session.query(Input).all():
            if each_input.device in ['DS18B20', 'DS18S20']:
                if 'library' not in each_input.custom_options:
                    if each_input.custom_options in [None, '']:
                        each_input.custom_options = 'library,w1thermsensor'
                    else:
                        each_input.custom_options += ';library,w1thermsensor'

        new_session.commit()
Example #12
0
def delete_user(username):
    if query_yes_no("Confirm delete user '{}' from user database.".format(username)):
        try:
            with session_scope(USER_DB_PATH) as db_session:
                user = db_session.query(Users).filter(Users.user_name == username).one()
                db_session.delete(user)
                print("User deleted.")
                sys.exit(0)
        except sqlalchemy.orm.exc.NoResultFound:
            print("No user found with this name.")
            sys.exit(1)
Example #13
0
    def stop_controller(self, ended_normally=True, deactivate_pid=False):
        self.thread_shutdown_timer = timeit.default_timer()
        self.running = False

        # Unset method start time
        if self.method_id != '' and ended_normally:
            with session_scope(MYCODO_DB_PATH) as db_session:
                mod_pid = db_session.query(PID).filter(
                    PID.unique_id == self.pid_id).first()
                mod_pid.method_start_time = 'Ended'
                mod_pid.method_end_time = None
                db_session.commit()

        # Deactivate PID and Autotune
        if deactivate_pid:
            with session_scope(MYCODO_DB_PATH) as db_session:
                mod_pid = db_session.query(PID).filter(
                    PID.unique_id == self.pid_id).first()
                mod_pid.is_activated = False
                mod_pid.autotune_activated = False
                db_session.commit()
Example #14
0
    def start_method(self, method_id):
        """ Instruct a method to start running """
        if method_id:
            method = db_retrieve_table_daemon(Method, unique_id=method_id)
            method_data = db_retrieve_table_daemon(MethodData)
            method_data = method_data.filter(MethodData.method_id == method_id)
            method_data_repeat = method_data.filter(MethodData.duration_sec == 0).first()
            self.method_start_act = self.method_start_time
            self.method_start_time = None
            self.method_end_time = None

            if method.method_type == 'Duration':
                if self.method_start_act == 'Ended':
                    with session_scope(MYCODO_DB_PATH) as db_session:
                        mod_conditional = db_session.query(Trigger)
                        mod_conditional = mod_conditional.filter(
                            Trigger.unique_id == self.function_id).first()
                        mod_conditional.is_activated = False
                        db_session.commit()
                    self.stop_controller()
                    self.logger.warning(
                        "Method has ended. "
                        "Activate the Trigger controller to start it again.")
                elif (self.method_start_act == 'Ready' or
                        self.method_start_act is None):
                    # Method has been instructed to begin
                    now = datetime.datetime.now()
                    self.method_start_time = now
                    if method_data_repeat and method_data_repeat.duration_end:
                        self.method_end_time = now + datetime.timedelta(
                            seconds=float(method_data_repeat.duration_end))

                    with session_scope(MYCODO_DB_PATH) as db_session:
                        mod_conditional = db_session.query(Trigger)
                        mod_conditional = mod_conditional.filter(
                            Trigger.unique_id == self.function_id).first()
                        mod_conditional.method_start_time = self.method_start_time
                        mod_conditional.method_end_time = self.method_end_time
                        db_session.commit()
Example #15
0
    def deactivate_self(self):
        self.logger.info("Deactivating Autotune Function")

        from mycodo.databases.utils import session_scope
        from mycodo.config import SQL_DATABASE_MYCODO
        MYCODO_DB_PATH = 'sqlite:///' + SQL_DATABASE_MYCODO
        with session_scope(MYCODO_DB_PATH) as new_session:
            mod_cont = new_session.query(CustomController).filter(
                CustomController.unique_id == self.unique_id).first()
            mod_cont.is_activated = False
            new_session.commit()

        deactivate_controller = threading.Thread(
            target=self.control.controller_deactivate, args=(self.unique_id, ))
        deactivate_controller.start()
Example #16
0
def check_allowed_to_email():
    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

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

    return smtp_wait_timer, allowed_to_send_notice
Example #17
0
 def stop_method(self):
     self.method_start_time = None
     self.method_end_time = None
     with session_scope(MYCODO_DB_PATH) as db_session:
         this_controller = db_session.query(PID)
         this_controller = this_controller.filter(PID.unique_id == self.unique_id).first()
         this_controller.is_activated = False
         this_controller.method_start_time = None
         this_controller.method_end_time = None
         db_session.commit()
     self.stop_controller()
     self.is_activated = False
     self.logger.warning(
         "Method has ended. "
         "Activate the Trigger controller to start it again.")
 def _delete_custom_option(self, controller, unique_id, option):
     try:
         with session_scope(MYCODO_DB_PATH) as new_session:
             mod_function = new_session.query(controller).filter(
                 controller.unique_id == unique_id).first()
             try:
                 dict_custom_options = json.loads(
                     mod_function.custom_options)
             except:
                 dict_custom_options = {}
             dict_custom_options.pop(option)
             mod_function.custom_options = json.dumps(dict_custom_options)
             new_session.commit()
     except Exception:
         self.logger.exception("delete_custom_option")
Example #19
0
def db_retrieve_table_daemon(table, entry=None, device_id=None, unique_id=None):
    """
    Return SQL database query object with optional filtering
    Used in daemon (For Flask, see db_retrieve_table() above)

    If entry='first', only the first table entry is returned.
    If entry='all', all table entries are returned.
    If device_id is set, the first entry with that device ID is returned.
    Otherwise, the table object is returned.
    """
    tries = 5
    while tries > 0:
        try:
            with session_scope(MYCODO_DB_PATH) as new_session:
                if device_id:
                    return_table = new_session.query(table).filter(
                        table.id == int(device_id))
                elif unique_id:
                    return_table = new_session.query(table).filter(
                        table.unique_id == unique_id)
                else:
                    return_table = new_session.query(table)

                if entry == 'first' or device_id or unique_id:
                    return_table = return_table.first()
                elif entry == 'all':
                    return_table = return_table.all()

                new_session.expunge_all()
                new_session.close()
            return return_table
        except OperationalError:
            pass
        except sqlalchemy.exc.OperationalError:
            pass

        if tries == 1:
            logger.exception(
                "Could not read the Mycodo database. "
                "Please submit a New Issue at "
                "https://github.com/kizniche/Mycodo/issues/new")
        else:
            logger.error(
                "The Mycodo database is locked. "
                "Trying to access again in 1 second...")

        time.sleep(1)
        tries -= 1
Example #20
0
    def set_method(self, method_id):
        """ Set the method of PID """
        with session_scope(MYCODO_DB_PATH) as db_session:
            mod_pid = db_session.query(PID).filter(PID.unique_id == self.unique_id).first()
            mod_pid.setpoint_tracking_id = method_id

            if method_id == '':
                self.setpoint_tracking_id = ''
                db_session.commit()
            else:
                mod_pid.method_start_time = 'Ready'
                mod_pid.method_end_time = None
                db_session.commit()
                self.setup_method(method_id)

        return "Method set to {me}".format(me=method_id)
Example #21
0
    def set_method(self, method_id):
        """ Set the method of PID """
        self.method_id = int(method_id)

        with session_scope(MYCODO_DB_PATH) as db_session:
            mod_pid = db_session.query(PID).filter(
                PID.id == self.pid_id).first()
            mod_pid.method_id = method_id
            mod_pid.method_start_time = 'Ready'
            mod_pid.method_end_time = None
            db_session.commit()

        if self.method_id:
            self.setup_method(method_id)

        return "Method set to {me} and started".format(me=method_id)
Example #22
0
    def set_method(self, method_id):
        """ Set the method of PID """
        with session_scope(MYCODO_DB_PATH) as db_session:
            mod_pid = db_session.query(PID).filter(
                PID.unique_id == self.pid_id).first()
            mod_pid.method_id = method_id

            if method_id == '':
                self.method_id = ''
                db_session.commit()
            else:
                mod_pid.method_start_time = 'Ready'
                mod_pid.method_end_time = None
                db_session.commit()
                self.setup_method(method_id)

        return "Method set to {me}".format(me=method_id)
Example #23
0
def save_conditional_code():
    with session_scope(MYCODO_DB_PATH) as conditional_sess:
        for each_conditional in conditional_sess.query(Conditional).all():
            try:
                indented_code = textwrap.indent(
                    each_conditional.conditional_statement, ' ' * 8)

                cond_statement_run = pre_statement_run + indented_code
                cond_statement_run = cond_statement_replace(cond_statement_run)

                assure_path_exists(PATH_PYTHON_CODE_USER)
                file_run = '{}/conditional_{}.py'.format(
                    PATH_PYTHON_CODE_USER, each_conditional.unique_id)
                with open(file_run, 'w') as fw:
                    fw.write('{}\n'.format(cond_statement_run))
                    fw.close()
            except Exception as msg:
                print("Exception: {}".format(msg))
Example #24
0
def add_user(admin=False):
    new_user = Users()

    print('\nAdd user to database')

    while True:
        user_name = raw_input('User (a-z, A-Z, 2-64 chars): ')
        if test_username(user_name):
            new_user.user_name = user_name
            break

    while True:
        user_password = getpass.getpass('Password: '******'Password (again): ')
        if user_password != user_password_again:
            print("Passwords don't match")
        else:
            if test_password(user_password):
                new_user.set_password(user_password)
                break

    while True:
        user_email = raw_input('Email: ')
        if is_email(user_email):
            new_user.user_email = user_email
            break

    if admin:
        new_user.user_restriction = 'admin'
    else:
        new_user.user_restriction = 'guest'

    new_user.user_theme = 'dark'
    try:
        with session_scope(USER_DB_PATH) as db_session:
            db_session.add(new_user)
        sys.exit(0)
    except sqlalchemy.exc.OperationalError:
        print("Failed to create user.  You most likely need to "
              "create the DB before trying to create users.")
        sys.exit(1)
    except sqlalchemy.exc.IntegrityError:
        print("Username already exists.")
        sys.exit(1)
Example #25
0
def add_user(admin=False):
    new_user = Users()

    print('\nAdd user to database')

    while True:
        user_name = raw_input('User (a-z, A-Z, 2-64 chars): ')
        if test_username(user_name):
            new_user.user_name = user_name
            break

    while True:
        user_password = getpass.getpass('Password: '******'Password (again): ')
        if user_password != user_password_again:
            print("Passwords don't match")
        else:
            if test_password(user_password):
                new_user.set_password(user_password)
                break

    while True:
        user_email = raw_input('Email: ')
        if is_email(user_email):
            new_user.user_email = user_email
            break

    if admin:
        new_user.user_restriction = 'admin'
    else:
        new_user.user_restriction = 'guest'

    new_user.user_theme = 'slate'
    try:
        with session_scope(USER_DB_PATH) as db_session:
            db_session.add(new_user)
        sys.exit(0)
    except sqlalchemy.exc.OperationalError:
        print("Failed to create user.  You most likely need to "
              "create the DB before trying to create users.")
        sys.exit(1)
    except sqlalchemy.exc.IntegrityError:
        print("Username already exists.")
        sys.exit(1)
Example #26
0
def cond_statement_replace(cond_statement):
    cond_statement_replaced = cond_statement
    with session_scope(MYCODO_DB_PATH) as conditional_sess:
        for each_condition in conditional_sess.query(
                ConditionalConditions).all():
            condition_id_short = each_condition.unique_id.split('-')[0]
            cond_statement_replaced = cond_statement_replaced.replace(
                '{{{id}}}'.format(id=condition_id_short),
                each_condition.unique_id)

        for each_action in conditional_sess.query(Actions).all():
            action_id_short = each_action.unique_id.split('-')[0]
            cond_statement_replaced = cond_statement_replaced.replace(
                '{{{id}}}'.format(id=action_id_short), each_action.unique_id)

        conditional_sess.expunge_all()
        conditional_sess.close()

    return cond_statement_replaced
Example #27
0
def action_pause_pid(cond_action, message):
    control = DaemonControl()
    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()
    return message
Example #28
0
def change_password(username):
    print('Changing password for {}'.format(username))

    with session_scope(USER_DB_PATH) as db_session:
        user = db_session.query(Users).filter(Users.user_name == username).one()

        while True:
            user_password = getpass.getpass('Password: '******'Password (again): ')
            if user_password != user_password_again:
                print("Passwords don't match")
            else:
                try:
                    if test_password(user_password):
                        user.set_password(user_password)
                        sys.exit(0)
                except sqlalchemy.orm.exc.NoResultFound:
                    print("No user found with this name.")
                    sys.exit(1)
Example #29
0
def execute_at_modification(messages, mod_input, request_form,
                            custom_options_dict_presave,
                            custom_options_channels_dict_presave,
                            custom_options_dict_postsave,
                            custom_options_channels_dict_postsave):
    try:
        if (custom_options_dict_postsave['adc_channel_ph'] ==
                custom_options_dict_postsave['adc_channel_ec']):
            messages["error"].append(
                "Cannot set pH and EC to be measured from the same channel.")
        else:
            with session_scope(MYCODO_DB_PATH) as new_session:
                measurements = new_session.query(DeviceMeasurements).filter(
                    DeviceMeasurements.device_id == mod_input.unique_id).all()
                for each_measure in measurements:
                    if each_measure.channel == int(
                            custom_options_dict_postsave['adc_channel_ph']):
                        if each_measure.measurement != 'ion_concentration':
                            messages["page_refresh"] = True
                            each_measure.conversion_id = ''
                        each_measure.measurement = 'ion_concentration'
                        each_measure.unit = 'pH'
                    elif each_measure.channel == int(
                            custom_options_dict_postsave['adc_channel_ec']):
                        if each_measure.measurement != 'electrical_conductivity':
                            messages["page_refresh"] = True
                            each_measure.conversion_id = ''
                        each_measure.measurement = 'electrical_conductivity'
                        each_measure.unit = 'uS_cm'
                    else:
                        if each_measure.measurement != 'electrical_potential':
                            messages["page_refresh"] = True
                            each_measure.conversion_id = ''
                        each_measure.measurement = 'electrical_potential'
                        each_measure.unit = 'V'
                    new_session.commit()
    except Exception:
        messages["error"].append("execute_at_modification() Error: {}".format(
            traceback.print_exc()))

    return (messages, mod_input, custom_options_dict_postsave,
            custom_options_channels_dict_postsave)
Example #30
0
    def run_action(self, message, dict_vars):
        try:
            controller_id = dict_vars["value"]["pid_id"]
        except:
            controller_id = self.controller_id

        try:
            amount = dict_vars["value"]["amount"]
        except:
            amount = self.amount

        pid = db_retrieve_table_daemon(PID,
                                       unique_id=controller_id,
                                       entry='first')

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

        new_setpoint = pid.setpoint - amount
        message += f" Lower Setpoint of PID {controller_id} ({pid.name}) by {amount}, to {new_setpoint}."

        if pid.is_activated:
            setpoint_pid = threading.Thread(target=self.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 == controller_id).first()
                mod_pid.setpoint = new_setpoint
                new_session.commit()

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

        return message
Example #31
0
def change_password(username):
    print('Changing password for {}'.format(username))

    with session_scope(USER_DB_PATH) as db_session:
        user = db_session.query(Users).filter(
            Users.user_name == username).one()

        while True:
            user_password = getpass.getpass('Password: '******'Password (again): ')
            if user_password != user_password_again:
                print("Passwords don't match")
            else:
                try:
                    if test_password(user_password):
                        user.set_password(user_password)
                        sys.exit(0)
                except sqlalchemy.orm.exc.NoResultFound:
                    print("No user found with this name.")
                    sys.exit(1)
Example #32
0
 def pid_set(self, pid_unique_id, setting, value):
     try:
         with session_scope(MYCODO_DB_PATH) as new_session:
             pid = new_session.query(PID).filter(
                 PID.unique_id == pid_unique_id).first()
             pid_id = pid.id
         if setting == 'setpoint':
             return self.controller['PID'][pid_id].set_setpoint(value)
         elif setting == 'integrator':
             return self.controller['PID'][pid_id].set_integrator(value)
         elif setting == 'derivator':
             return self.controller['PID'][pid_id].set_derivator(value)
         elif setting == 'kp':
             return self.controller['PID'][pid_id].set_kp(value)
         elif setting == 'ki':
             return self.controller['PID'][pid_id].set_ki(value)
         elif setting == 'kd':
             return self.controller['PID'][pid_id].set_kd(value)
     except Exception as except_msg:
         message = "Could not set PID {option}:" \
                   " {err}".format(option=setting, err=except_msg)
         self.logger.exception(message)
Example #33
0
def action_setpoint_pid(cond_action, message):
    control = DaemonControl()
    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()
    return message
Example #34
0
def action_deactivate_controller(cond_action, message):
    control = DaemonControl()
    (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=(cond_action.do_unique_id, ))
        deactivate_controller.start()
    return message
Example #35
0
def action_create_note(cond_action, message, single_action, note_tags):
    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)
    return message, note_tags
    def start_method(self, method_id):
        """ Instruct a method to start running """
        if method_id:
            this_controller = db_retrieve_table_daemon(
                Trigger, unique_id=self.unique_id)

            method = load_method_handler(method_id, self.logger)

            if parse_db_time(this_controller.method_start_time) is None:
                self.method_start_time = datetime.datetime.now()
                self.method_end_time = method.determine_end_time(self.method_start_time)

                self.logger.info("Starting method {} {}".format(self.method_start_time, self.method_end_time))

                with session_scope(MYCODO_DB_PATH) as db_session:
                    this_controller = db_session.query(Trigger)
                    this_controller = this_controller.filter(Trigger.unique_id == self.unique_id).first()
                    this_controller.method_start_time = self.method_start_time
                    this_controller.method_end_time = self.method_end_time
                    db_session.commit()
            else:
                # already running, potentially the daemon has been restarted
                self.method_start_time = this_controller.method_start_time
                self.method_end_time = this_controller.method_end_time
    else:
        user_valid = True

email = input("Email Address: ")

while not passwords_match and not password_valid:
    password = getpass("Password: "******"Repeat Password: "******"Password don't math. Try again.")
    else:
        passwords_match = True

try:
    with session_scope(MYCODO_DB_PATH) as db_session:
        new_user = User()
        new_user.unique_id = set_uuid()
        new_user.name = user_name.lower()
        new_user.password_hash = set_password(password)
        new_user.email = email
        new_user.role_id = 1
        new_user.theme = 'slate'
        new_user.landing_page = 'live'
        new_user.language = 'en'
        db_session.add(new_user)

    print("Admin user '{}' successfully created.".format(user_name.lower()))
except Exception:
    print(
        "Error creating admin user. Refer the the traceback, below, for the error."
    def run(self):
        try:
            self.running = True

            # This log line will appear in the Daemon log under Config -> Mycodo Logs
            self.logger.info("Function running")

            # Make sure the option "Log Level: Debug" is enabled for these debug
            # log lines to appear in the Daemon log.
            self.logger.debug(
                "Custom controller started with options: "
                "{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}".format(
                    self.text_1, self.integer_1, self.float_1, self.bool_1,
                    self.select_1, self.select_measurement_1_device_id,
                    self.select_measurement_1_measurement_id,
                    self.output_1_device_id, self.output_1_measurement_id,
                    self.output_1_channel_id, self.select_device_1_id))

            # You can specify different log levels to indicate things such as errors
            self.logger.error(
                "This is an error line that will appear in the log")

            # And Warnings
            self.logger.warning(
                "This is a warning line that will appear in the log")

            # Get last measurement for select_measurement_1
            last_measurement = self.get_last_measurement(
                self.select_measurement_1_device_id,
                self.select_measurement_1_measurement_id)

            if last_measurement:
                self.logger.debug(
                    "Most recent timestamp and measurement for "
                    "select_measurement_1: {timestamp}, {meas}".format(
                        timestamp=last_measurement[0],
                        meas=last_measurement[1]))
            else:
                self.logger.debug(
                    "Could not find a measurement in the database for "
                    "select_measurement_1 device ID {} and measurement "
                    "ID {}".format(self.select_measurement_1_device_id,
                                   self.select_measurement_1_measurement_id))

            # Turn Output select_device_1 on for 15 seconds
            self.logger.debug(
                "Turning select_device_1 with ID {} on for 15 seconds...".
                format(self.select_device_1_id))
            self.control.output_on(self.select_device_1_id,
                                   output_type='sec',
                                   output_channel=self.output_1_channel,
                                   amount=15)

            # Deactivate controller in the SQL database
            self.logger.debug(
                "Deactivating (SQL) Custom controller select_device_2 with ID {}"
                .format(self.select_device_2_id))
            from mycodo.databases.utils import session_scope
            from mycodo.config import SQL_DATABASE_MYCODO
            MYCODO_DB_PATH = 'sqlite:///' + SQL_DATABASE_MYCODO
            with session_scope(MYCODO_DB_PATH) as new_session:
                mod_cont = new_session.query(CustomController).filter(
                    CustomController.unique_id ==
                    self.select_device_2_id).first()
                mod_cont.is_activated = False
                new_session.commit()

            # Deactivate select_device_1_id in the dameon
            # Since we're deactivating this controller (itself), we need to thread this command
            # Note: this command will only deactivate the controller in the Daemon. It will still
            # be activated in the database, so the next restart of the daemon, this controller
            # will start back up again. This is why the previous action deactivated the controller
            # in the database prior to deactivating it in the daemon.
            self.logger.debug(
                "Deactivating (Daemon) Custom controller select_device_2 with"
                " ID {} ...".format(self.select_device_2_id))
            deactivate_controller = threading.Thread(
                target=self.control.controller_deactivate,
                args=(self.select_device_2_id, ))
            deactivate_controller.start()

            # Start a loop
            while self.running:
                time.sleep(1)
        except:
            self.logger.exception("Run Error")
        finally:
            self.running = False
            self.logger.error("Deactivated unexpectedly")
Example #39
0
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 #40
0
    def controller_activate(self, cont_type, cont_id):
        """
        Activate currently-inactive controller

        :return: 0 for success, 1 for fail, with success or error message
        :rtype: int, str

        :param cont_type: Which controller type is to be activated?
        :type cont_type: str
        :param cont_id: Unique ID for controller
        :type cont_id: str
        """
        try:
            if cont_id in self.controller[cont_type]:
                if self.controller[cont_type][cont_id].is_running():
                    message = "Cannot activate {type} controller with ID {id}: " \
                              "It's already active.".format(type=cont_type,
                                                            id=cont_id)
                    self.logger.warning(message)
                    return 1, message

            controller_manage = {}
            ready = threading.Event()

            if cont_type == 'Conditional':
                controller_manage['type'] = Conditional
                controller_manage['function'] = ConditionalController
            elif cont_type == 'LCD':
                controller_manage['type'] = LCD
                controller_manage['function'] = LCDController
            elif cont_type == 'Input':
                controller_manage['type'] = Input
                controller_manage['function'] = InputController
            elif cont_type == 'Math':
                controller_manage['type'] = Math
                controller_manage['function'] = MathController
            elif cont_type == 'PID':
                controller_manage['type'] = PID
                controller_manage['function'] = PIDController
            elif cont_type == 'Trigger':
                controller_manage['type'] = Trigger
                controller_manage['function'] = TriggerController
            else:
                return 1, "'{type}' not a valid controller type.".format(
                    type=cont_type)

            # Check if the controller actually exists
            controller = db_retrieve_table_daemon(controller_manage['type'],
                                                  unique_id=cont_id)
            if not controller:
                return 1, "{type} controller with ID {id} not found.".format(
                    type=cont_type, id=cont_id)

            # set as active in SQL database
            with session_scope(MYCODO_DB_PATH) as new_session:
                mod_cont = new_session.query(controller_manage['type']).filter(
                    controller_manage['type'].unique_id == cont_id).first()
                mod_cont.is_activated = True
                new_session.commit()

            self.controller[cont_type][cont_id] = controller_manage['function'](
                ready, cont_id)
            self.controller[cont_type][cont_id].daemon = True
            self.controller[cont_type][cont_id].start()
            ready.wait()  # wait for thread to return ready

            return 0, "{type} controller with ID {id} " \
                "activated.".format(type=cont_type, id=cont_id)

        except Exception as except_msg:
            message = "Could not activate {type} controller with ID {id}:" \
                      " {err}".format(type=cont_type, id=cont_id,
                                      err=except_msg)
            self.logger.exception(message)
            return 1, message
Example #41
0
    def get_new_data(self, past_seconds):
        # Basic implementation. Future development may use more complex library to access API
        endpoint = "https://{app}.data.thethingsnetwork.org/api/v2/query/{dev}?last={time}".format(
            app=self.application_id,
            dev=self.device_id,
            time="{}s".format(int(past_seconds)))
        headers = {"Authorization": "key {k}".format(k=self.app_api_key)}
        timestamp_format = '%Y-%m-%dT%H:%M:%S.%f'

        response = requests.get(endpoint, headers=headers)
        try:
            response.json()
        except ValueError:  # No data returned
            self.logger.debug(
                "Response Error. Response: {}. Likely there is no data to be retrieved on TTN"
                .format(response.content))
            return

        for each_resp in response.json():
            if not self.running:
                break

            try:
                datetime_utc = datetime.datetime.strptime(
                    each_resp['time'][:-7], timestamp_format)
            except Exception:
                # Sometimes the original timestamp is in milliseconds
                # instead of nanoseconds. Therefore, remove 3 less digits
                # past the decimal and try again to parse.
                try:
                    datetime_utc = datetime.datetime.strptime(
                        each_resp['time'][:-4], timestamp_format)
                except Exception as e:
                    self.logger.error(
                        "Could not parse timestamp '{}': {}".format(
                            each_resp['time'], e))
                    continue  # Malformed timestamp encountered. Discard measurement.

            if (not self.latest_datetime
                    or self.latest_datetime < datetime_utc):
                self.latest_datetime = datetime_utc

            measurements = {}
            for channel in self.channels_measurement:
                if (self.is_enabled(channel)
                        and self.options_channels['variable_name'][channel]
                        in each_resp
                        and each_resp[self.options_channels['variable_name']
                                      [channel]] is not None):

                    # Original value/unit
                    measurements[channel] = {}
                    measurements[channel][
                        'measurement'] = self.channels_measurement[
                            channel].measurement
                    measurements[channel]['unit'] = self.channels_measurement[
                        channel].unit
                    measurements[channel]['value'] = each_resp[
                        self.options_channels['variable_name'][channel]]
                    measurements[channel]['timestamp_utc'] = datetime_utc

                    # Convert value/unit is conversion_id present and valid
                    if self.channels_conversion[channel]:
                        conversion = db_retrieve_table_daemon(
                            Conversion,
                            unique_id=self.channels_measurement[channel].
                            conversion_id)
                        if conversion:
                            meas = parse_measurement(
                                self.channels_conversion[channel],
                                self.channels_measurement[channel],
                                measurements,
                                channel,
                                measurements[channel],
                                timestamp=datetime_utc)

                            measurements[channel]['measurement'] = meas[
                                channel]['measurement']
                            measurements[channel]['unit'] = meas[channel][
                                'unit']
                            measurements[channel]['value'] = meas[channel][
                                'value']

            if measurements:
                self.logger.debug(
                    "Adding measurements to influxdb: {}".format(measurements))
                add_measurements_influxdb(
                    self.unique_id,
                    measurements,
                    use_same_timestamp=INPUT_INFORMATION[
                        'measurements_use_same_timestamp'])
            else:
                self.logger.debug("No measurements to add to influxdb.")

        # set datetime to latest timestamp
        if self.running:
            with session_scope(MYCODO_DB_PATH) as new_session:
                mod_input = new_session.query(Input).filter(
                    Input.unique_id == self.unique_id).first()
                if not mod_input.datetime or mod_input.datetime < self.latest_datetime:
                    mod_input.datetime = self.latest_datetime
                    new_session.commit()
Example #42
0
def do_admin_login():
    """Authenticate users of the web-UI"""
    # Check if the user is banned from logging in
    if flaskutils.banned_from_login():
        return redirect('/')

    form = flaskforms.Login()
    form_notice = flaskforms.InstallNotice()

    with session_scope(current_app.config['MYCODO_DB_PATH']) as db_session:
        misc = db_session.query(Misc).first()
        dismiss_notification = misc.dismiss_notification
        stats_opt_out = misc.stats_opt_out

    if request.method == 'POST':
        form_name = request.form['form-name']
        if form_name == 'acknowledge':
            try:
                with session_scope(
                        current_app.config['MYCODO_DB_PATH']) as db_session:
                    mod_misc = db_session.query(Misc).first()
                    mod_misc.dismiss_notification = 1
                    db_session.commit()
            except Exception as except_msg:
                flash("Acknowledgement not saved: {}".format(except_msg),
                      "error")
        elif form_name == 'login' and form.validate_on_submit():
            with session_scope(
                    current_app.config['USER_DB_PATH']) as new_session:
                user = new_session.query(Users).filter(
                    Users.user_name == form.username.data).first()
                new_session.expunge_all()
                new_session.close()
            if not user:
                flaskutils.login_log(
                    form.username.data, 'NA',
                    request.environ.get('REMOTE_ADDR', 'unknown address'),
                    'NOUSER')
                flaskutils.failed_login()
            elif Users().check_password(
                    form.password.data,
                    user.user_password_hash) == user.user_password_hash:
                flaskutils.login_log(
                    user.user_name, user.user_restriction,
                    request.environ.get('REMOTE_ADDR', 'unknown address'),
                    'LOGIN')
                session['logged_in'] = True
                session['user_group'] = user.user_restriction
                session['user_name'] = user.user_name
                session['user_theme'] = user.user_theme
                if form.remember.data:
                    response = make_response(redirect('/'))
                    expire_date = datetime.datetime.now()
                    expire_date = expire_date + datetime.timedelta(days=90)
                    response.set_cookie('user_name',
                                        user.user_name,
                                        expires=expire_date)
                    response.set_cookie('user_pass_hash',
                                        user.user_password_hash,
                                        expires=expire_date)
                    return response
                return redirect('/')
            else:
                flaskutils.login_log(
                    user.user_name, user.user_restriction,
                    request.environ.get('REMOTE_ADDR', 'unknown address'),
                    'FAIL')
                flaskutils.failed_login()
        else:
            flaskutils.login_log(
                form.username.data, 'NA',
                request.environ.get('REMOTE_ADDR', 'unknown address'), 'FAIL')
            flaskutils.failed_login()

        return redirect('/login')

    return render_template('login.html',
                           form=form,
                           formNotice=form_notice,
                           dismiss_notification=dismiss_notification,
                           stats_opt_out=stats_opt_out)
Example #43
0
def calculate_method_setpoint(method_id, table, controller, Method, MethodData,
                              logger):
    """
    Calculates the setpoint from a method
    :param method_id: ID of Method to be used
    :param table: Table of the controller using this function
    :param controller: The controller using this function
    :param logger: The logger to use
    :return: 0 (success) or 1 (error) and a setpoint value
    """
    method = db_retrieve_table_daemon(Method)

    method_key = method.filter(Method.id == method_id).first()

    method_data = db_retrieve_table_daemon(MethodData)
    method_data = method_data.filter(MethodData.method_id == method_id)

    method_data_all = method_data.filter(MethodData.relay_id == None).all()
    method_data_first = method_data.filter(MethodData.relay_id == None).first()

    now = datetime.datetime.now()

    # Calculate where the current time/date is within the time/date method
    if method_key.method_type == 'Date':
        for each_method in method_data_all:
            start_time = datetime.datetime.strptime(each_method.time_start,
                                                    '%Y-%m-%d %H:%M:%S')
            end_time = datetime.datetime.strptime(each_method.time_end,
                                                  '%Y-%m-%d %H:%M:%S')
            if start_time < now < end_time:
                setpoint_start = each_method.setpoint_start
                if each_method.setpoint_end:
                    setpoint_end = each_method.setpoint_end
                else:
                    setpoint_end = each_method.setpoint_start

                setpoint_diff = abs(setpoint_end - setpoint_start)
                total_seconds = (end_time - start_time).total_seconds()
                part_seconds = (now - start_time).total_seconds()
                percent_total = part_seconds / total_seconds

                if setpoint_start < setpoint_end:
                    new_setpoint = setpoint_start + (setpoint_diff *
                                                     percent_total)
                else:
                    new_setpoint = setpoint_start - (setpoint_diff *
                                                     percent_total)

                logger.debug("[Method] Start: {start} End: {end}".format(
                    start=start_time, end=end_time))
                logger.debug("[Method] Start: {start} End: {end}".format(
                    start=setpoint_start, end=setpoint_end))
                logger.debug(
                    "[Method] Total: {tot} Part total: {par} ({per}%)".format(
                        tot=total_seconds, par=part_seconds,
                        per=percent_total))
                logger.debug(
                    "[Method] New Setpoint: {sp}".format(sp=new_setpoint))
                return new_setpoint, False

    # Calculate where the current Hour:Minute:Seconds is within the Daily method
    elif method_key.method_type == 'Daily':
        daily_now = datetime.datetime.now().strftime('%H:%M:%S')
        daily_now = datetime.datetime.strptime(str(daily_now), '%H:%M:%S')
        for each_method in method_data_all:
            start_time = datetime.datetime.strptime(each_method.time_start,
                                                    '%H:%M:%S')
            end_time = datetime.datetime.strptime(each_method.time_end,
                                                  '%H:%M:%S')
            if start_time < daily_now < end_time:
                setpoint_start = each_method.setpoint_start
                if each_method.setpoint_end:
                    setpoint_end = each_method.setpoint_end
                else:
                    setpoint_end = each_method.setpoint_start

                setpoint_diff = abs(setpoint_end - setpoint_start)
                total_seconds = (end_time - start_time).total_seconds()
                part_seconds = (daily_now - start_time).total_seconds()
                percent_total = part_seconds / total_seconds

                if setpoint_start < setpoint_end:
                    new_setpoint = setpoint_start + (setpoint_diff *
                                                     percent_total)
                else:
                    new_setpoint = setpoint_start - (setpoint_diff *
                                                     percent_total)

                logger.debug("[Method] Start: {start} End: {end}".format(
                    start=start_time.strftime('%H:%M:%S'),
                    end=end_time.strftime('%H:%M:%S')))
                logger.debug("[Method] Start: {start} End: {end}".format(
                    start=setpoint_start, end=setpoint_end))
                logger.debug(
                    "[Method] Total: {tot} Part total: {par} ({per}%)".format(
                        tot=total_seconds, par=part_seconds,
                        per=percent_total))
                logger.debug(
                    "[Method] New Setpoint: {sp}".format(sp=new_setpoint))
                return new_setpoint, False

    # Calculate sine y-axis value from the x-axis (seconds of the day)
    elif method_key.method_type == 'DailySine':
        new_setpoint = sine_wave_y_out(method_data_first.amplitude,
                                       method_data_first.frequency,
                                       method_data_first.shift_angle,
                                       method_data_first.shift_y)
        return new_setpoint, False

    # Calculate Bezier curve y-axis value from the x-axis (seconds of the day)
    elif method_key.method_type == 'DailyBezier':
        new_setpoint = bezier_curve_y_out(
            method_data_first.shift_angle,
            (method_data_first.x0, method_data_first.y0),
            (method_data_first.x1, method_data_first.y1),
            (method_data_first.x2, method_data_first.y2),
            (method_data_first.x3, method_data_first.y3))
        return new_setpoint, False

    # Calculate the duration in the method based on self.method_start_time
    elif method_key.method_type == 'Duration':
        start_time = datetime.datetime.strptime(
            str(controller.method_start_time), '%Y-%m-%d %H:%M:%S.%f')
        ended = False

        # Check if method_end_time is not None
        if controller.method_end_time:
            # Convert time string to datetime object
            end_time = datetime.datetime.strptime(
                str(controller.method_end_time), '%Y-%m-%d %H:%M:%S.%f')
            if now > start_time:
                ended = True

        seconds_from_start = (now - start_time).total_seconds()
        total_sec = 0
        previous_total_sec = 0
        previous_end = None
        method_restart = False

        for each_method in method_data_all:
            # If duration_sec is 0, method has instruction to restart
            if each_method.duration_sec == 0:
                method_restart = True
            else:
                previous_end = each_method.setpoint_end

            total_sec += each_method.duration_sec
            if previous_total_sec <= seconds_from_start < total_sec:
                row_start_time = float(
                    start_time.strftime('%s')) + previous_total_sec
                row_since_start_sec = (now - (start_time + datetime.timedelta(
                    0, previous_total_sec))).total_seconds()
                percent_row = row_since_start_sec / each_method.duration_sec

                setpoint_start = each_method.setpoint_start
                if each_method.setpoint_end:
                    setpoint_end = each_method.setpoint_end
                else:
                    setpoint_end = each_method.setpoint_start
                setpoint_diff = abs(setpoint_end - setpoint_start)
                if setpoint_start < setpoint_end:
                    new_setpoint = setpoint_start + (setpoint_diff *
                                                     percent_row)
                else:
                    new_setpoint = setpoint_start - (setpoint_diff *
                                                     percent_row)

                logger.debug(
                    "[Method] Start: {start} Seconds Since: {sec}".format(
                        start=start_time, sec=seconds_from_start))
                logger.debug("[Method] Start time of row: {start}".format(
                    start=datetime.datetime.fromtimestamp(row_start_time)))
                logger.debug("[Method] Sec since start of row: {sec}".format(
                    sec=row_since_start_sec))
                logger.debug(
                    "[Method] Percent of row: {per}".format(per=percent_row))
                logger.debug(
                    "[Method] New Setpoint: {sp}".format(sp=new_setpoint))
                return new_setpoint, False
            previous_total_sec = total_sec

        if controller.method_start_time:
            if method_restart:
                if end_time and now > end_time:
                    ended = True
                else:
                    # Method has been instructed to restart
                    controller.method_start_time = datetime.datetime.now()
                    with session_scope(MYCODO_DB_PATH) as db_session:
                        mod_method = db_session.query(table)
                        mod_method = mod_method.filter(
                            table.method_id == method_id).first()
                        mod_method.method_start_time = controller.method_start_time
                        db_session.commit()
                        return previous_end, False
            else:
                ended = True

            if ended:
                # Duration method has ended, reset method_start_time locally and in DB
                with session_scope(MYCODO_DB_PATH) as db_session:
                    mod_method = db_session.query(table).filter(
                        table.method_id == method_id).first()
                    mod_method.method_start_time = 'Ended'
                    mod_method.method_end_time = None
                    db_session.commit()
                return None, True

    # Setpoint not needing to be calculated, use default setpoint
    return None, False
Example #44
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 #45
0
    else:
        user_valid = True

email = input("Email Address: ")

while not passwords_match and not password_valid:
    password = getpass("Password: "******"Repeat Password: "******"Password don't math. Try again.")
    else:
        passwords_match = True

try:
    with session_scope(MYCODO_DB_PATH) as db_session:
        new_user = User()
        new_user.unique_id = set_uuid()
        new_user.name = user_name.lower()
        new_user.password_hash = set_password(password)
        new_user.email = email
        new_user.role_id = 1
        new_user.theme = 'slate'
        new_user.landing_page = 'live'
        new_user.language = 'en'
        db_session.add(new_user)

    print("Admin user '{}' successfully created.".format(user_name.lower()))
except Exception:
    print("Error creating admin user. Refer the the traceback, below, for the error.")
    traceback.print_exc()
    def get_new_data(self, past_seconds):
        # Basic implementation. Future development may use more complex library to access API
        endpoint = "https://nam1.cloud.thethings.network/api/v3/as/applications/{app}/devices/{dev}/packages/storage/uplink_message?last={time}&field_mask=up.uplink_message.decoded_payload".format(
            app=self.application_id,
            dev=self.device_id,
            time="{}s".format(int(past_seconds)))
        headers = {
            "Authorization": "Bearer {k}".format(k=self.app_api_key),
            'Content-Type': 'application/json'
        }
        timestamp_format = '%Y-%m-%dT%H:%M:%S.%f'

        response = requests.get(endpoint, headers=headers)
        if response.status_code != 200:
            self.logger.info("response.status_code != 200: {}".format(
                response.reason))
        self.logger.debug("response.content: {}".format(response.content))

        list_dicts = response.content.decode().split("\n")
        self.logger.debug("list_dicts: {}".format(list_dicts))

        cpm_value = None
        cpm_ts = None
        usv_h_value = None
        usv_h_ts = None

        for each_resp in list_dicts:
            if not each_resp:
                continue
            self.logger.debug("each_resp: {}".format(each_resp))

            cpm_value = None
            usv_h_value = None

            try:
                resp_json = json.loads(each_resp)
            except:
                resp_json = {}
            self.logger.debug("resp_json: {}".format(resp_json))

            self.return_dict = measurements_dict.copy()

            try:
                datetime_utc = datetime.datetime.strptime(
                    resp_json['result']['received_at'][:-7], timestamp_format)
            except:
                # Sometimes the original timestamp is in milliseconds
                # instead of nanoseconds. Therefore, remove 3 less digits
                # past the decimal and try again to parse.
                try:
                    datetime_utc = datetime.datetime.strptime(
                        resp_json['result']['received_at'][:-4],
                        timestamp_format)
                except:
                    self.logger.error("Could not parse timestamp: {}".format(
                        resp_json['result']['received_at']))
                    return

            if (not self.latest_datetime
                    or self.latest_datetime < datetime_utc):
                self.latest_datetime = datetime_utc

            for channel in self.return_dict:
                if (self.is_enabled(channel)
                        and self.return_dict[channel]['name']
                        in resp_json['result']['uplink_message']
                    ['decoded_payload']
                        and resp_json['result']['uplink_message']
                    ['decoded_payload'][self.return_dict[channel]['name']]
                        is not None):

                    self.return_dict[channel]['value'] = resp_json['result'][
                        'uplink_message']['decoded_payload'][
                            self.return_dict[channel]['name']]
                    self.return_dict[channel]['timestamp_utc'] = datetime_utc

                    if self.return_dict[channel]['unit'] == 'cpm':
                        cpm_value = float(self.return_dict[channel]['value'])
                        cpm_ts = self.return_dict[channel]['timestamp_utc']
                    elif self.return_dict[channel]['unit'] == 'uSv_hr':
                        usv_h_value = float(self.return_dict[channel]['value'])
                        usv_h_ts = self.return_dict[channel]['timestamp_utc']

                    # Convert value/unit if conversion_id present and valid
                    if self.channels_conversion[channel]:
                        conversion = db_retrieve_table_daemon(
                            Conversion,
                            unique_id=self.channels_measurement[channel].
                            conversion_id)
                        if conversion:
                            meas = parse_measurement(
                                self.channels_conversion[channel],
                                self.channels_measurement[channel],
                                self.return_dict,
                                channel,
                                self.return_dict[channel],
                                timestamp=datetime_utc)

                            self.return_dict[channel]['unit'] = meas[channel][
                                'unit']
                            self.return_dict[channel]['value'] = meas[channel][
                                'value']

            if 'value' in self.return_dict[0] and 'value' in self.return_dict[
                    1]:
                self.logger.debug("Adding measurements to influxdb: {}".format(
                    self.return_dict))
                add_measurements_influxdb(
                    self.unique_id,
                    self.return_dict,
                    use_same_timestamp=INPUT_INFORMATION[
                        'measurements_use_same_timestamp'])
            else:
                self.logger.debug("No measurements to add to influxdb.")

            # Send uSv/hr to Safecast
            if self.send_safecast and cpm_value and usv_h_value:
                try:
                    safecast = self.safecastpy.SafecastPy(
                        api_key=self.safecast_api_key)
                    measurement_usv = safecast.add_measurement(
                        json={
                            'latitude': self.safecast_latitude,
                            'longitude': self.safecast_longitude,
                            'value': usv_h_value,
                            'unit': self.safecastpy.UNIT_USV,
                            'captured_at': usv_h_ts.isoformat() + '+00:00',
                            'device_id': self.safecast_device_id,
                            'location_name': self.safecast_location_name
                        })
                    measurement_cpm = safecast.add_measurement(
                        json={
                            'latitude': self.safecast_latitude,
                            'longitude': self.safecast_longitude,
                            'value': cpm_value,
                            'unit': self.safecastpy.UNIT_CPM,
                            'captured_at': cpm_ts.isoformat() + '+00:00',
                            'device_id': self.safecast_device_id,
                            'location_name': self.safecast_location_name
                        })
                    self.logger.debug('uSv/hr measurement id: {0}'.format(
                        measurement_usv['id']))
                    self.logger.debug('CPM measurement id: {0}'.format(
                        measurement_cpm['id']))
                except Exception as e:
                    self.logger.error(
                        "Error adding data to Safecast: {}".format(e))

        # Send to GMC Map (doesn't accept time, so can only send the latest measurement)
        if (self.send_gmcmap and cpm_value and usv_h_value):
            try:
                gmcmap = 'http://www.GMCmap.com/log2.asp?AID=02376&GID=22044260632&CPM={cpm:.0f}&uSV={usv:.3f}'.format(
                    aid=self.gmcmap_account_id,
                    gcid=self.gmcmap_geiger_counter_id,
                    cpm=cpm_value,
                    usv=usv_h_value)
                contents = urllib.request.urlopen(gmcmap).read()
                self.logger.debug("GMCMap: {}".format(contents))
            except Exception as e:
                self.logger.error("Error adding data to GMC Map: {}".format(e))

        # set datetime to latest timestamp
        if self.running:
            with session_scope(MYCODO_DB_PATH) as new_session:
                mod_input = new_session.query(Input).filter(
                    Input.unique_id == self.unique_id).first()
                if not mod_input.datetime or mod_input.datetime < self.latest_datetime:
                    mod_input.datetime = self.latest_datetime
                    new_session.commit()
Example #47
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 #48
0
def do_admin_login():
    """Authenticate users of the web-UI"""
    # Check if the user is banned from logging in
    if flaskutils.banned_from_login():
        return redirect('/')

    form = flaskforms.Login()
    form_notice = flaskforms.InstallNotice()

    with session_scope(current_app.config['MYCODO_DB_PATH']) as db_session:
        misc = db_session.query(Misc).first()
        dismiss_notification = misc.dismiss_notification
        stats_opt_out = misc.stats_opt_out

    if request.method == 'POST':
        form_name = request.form['form-name']
        if form_name == 'acknowledge':
            try:
                with session_scope(current_app.config['MYCODO_DB_PATH']) as db_session:
                    mod_misc = db_session.query(Misc).first()
                    mod_misc.dismiss_notification = 1
                    db_session.commit()
            except Exception as except_msg:
                flash("Acknowledgement not saved: {}".format(except_msg), "error")
        elif form_name == 'login' and form.validate_on_submit():
            with session_scope(current_app.config['USER_DB_PATH']) as new_session:
                user = new_session.query(Users).filter(Users.user_name == form.username.data).first()
                new_session.expunge_all()
                new_session.close()
            if not user:
                flaskutils.login_log(form.username.data, 'NA',
                                     request.environ.get('REMOTE_ADDR', 'unknown address'), 'NOUSER')
                flaskutils.failed_login()
            elif Users().check_password(form.password.data, user.user_password_hash) == user.user_password_hash:
                flaskutils.login_log(user.user_name, user.user_restriction,
                                     request.environ.get('REMOTE_ADDR', 'unknown address'), 'LOGIN')
                session['logged_in'] = True
                session['user_group'] = user.user_restriction
                session['user_name'] = user.user_name
                session['user_theme'] = user.user_theme
                if form.remember.data:
                    response = make_response(redirect('/'))
                    expire_date = datetime.datetime.now()
                    expire_date = expire_date + datetime.timedelta(days=90)
                    response.set_cookie('user_name',
                                        user.user_name,
                                        expires=expire_date)
                    response.set_cookie('user_pass_hash',
                                        user.user_password_hash,
                                        expires=expire_date)
                    return response
                return redirect('/')
            else:
                flaskutils.login_log(user.user_name, user.user_restriction,
                                     request.environ.get('REMOTE_ADDR', 'unknown address'), 'FAIL')
                flaskutils.failed_login()
        else:
            flaskutils.login_log(form.username.data, 'NA',
                                 request.environ.get('REMOTE_ADDR', 'unknown address'), 'FAIL')
            flaskutils.failed_login()

        return redirect('/login')

    return render_template('login.html',
                           form=form,
                           formNotice=form_notice,
                           dismiss_notification=dismiss_notification,
                           stats_opt_out=stats_opt_out)
Example #49
0
        #     try:
        #         pass  # Code goes here
        #     except Exception:
        #         msg = "ERROR: post-alembic revision {}: {}".format(
        #             each_revision, traceback.format_exc())
        #         error.append(msg)
        #         print(msg)

        elif each_revision == 'd66e33093e8e':
            # convert database entries to JSON string for custom_options entry
            print("Executing post-alembic code for revision {}".format(
                each_revision))
            import json
            from mycodo.databases.models import Widget
            try:
                with session_scope(MYCODO_DB_PATH) as session:
                    for each_widget in session.query(Widget).all():
                        custom_options = {}
                        if each_widget.graph_type == 'graph':
                            each_widget.graph_type = 'widget_graph_synchronous'
                            custom_options['measurements_math'] = each_widget.math_ids.split(";")
                            custom_options['measurements_note_tag'] = each_widget.note_tag_ids.split(";")
                            custom_options['measurements_input'] = each_widget.input_ids_measurements.split(";")
                            custom_options['measurements_output'] = each_widget.output_ids.split(";")
                            custom_options['measurements_pid'] = each_widget.pid_ids.split(";")
                        elif each_widget.graph_type == 'spacer':
                            each_widget.graph_type = 'widget_spacer'
                        elif each_widget.graph_type == 'gauge_angular':
                            each_widget.graph_type = 'widget_gauge_angular'
                            custom_options['measurement'] = each_widget.input_ids_measurements
                        elif each_widget.graph_type == 'gauge_solid':
Example #50
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 #51
0
def register_extensions(app):
    """ register extensions to the app """
    app.jinja_env.add_extension('jinja2.ext.do')  # Global values in jinja

    # Uncomment to enable profiler
    # See scripts/profile_analyzer.py to analyze output
    # app = setup_profiler(app)

    # Compress app responses with gzip
    compress = Compress()
    compress.init_app(app)

    # Influx db time-series database
    db.init_app(app)
    influx_db.init_app(app)

    # Limit authentication blueprint requests to 200 per minute
    limiter = Limiter(app, key_func=get_ip_address)
    limiter.limit("200/minute")(routes_authentication.blueprint)

    # Language translations
    babel = Babel(app)

    @babel.localeselector
    def get_locale():
        try:
            user = User.query.filter(
                User.id == flask_login.current_user.id).first()
            if user and user.language != '':
                for key in LANGUAGES:
                    if key == user.language:
                        return key
        # Bypass endpoint test error "'AnonymousUserMixin' object has no attribute 'id'"
        except AttributeError:
            pass
        return request.accept_languages.best_match(LANGUAGES.keys())

    # User login management
    login_manager = flask_login.LoginManager()
    login_manager.init_app(app)

    @login_manager.user_loader
    def user_loader(user_id):
        user = User.query.filter(User.id == user_id).first()
        if not user:
            return
        return user

    @login_manager.unauthorized_handler
    def unauthorized():
        flash(gettext('Please log in to access this page'), "error")
        return redirect(url_for('routes_authentication.do_login'))

    # Create and populate database if it doesn't exist
    with app.app_context():
        db.create_all()
        populate_db()

        # This is disabled because there's a bug that messes up user databases
        # The upgrade script will execute alembic to upgrade the database
        # alembic_upgrade_db()

    # Check user option to force all web connections to use SSL
    # Fail if the URI is empty (pytest is running)
    if app.config['SQLALCHEMY_DATABASE_URI'] != 'sqlite://':
        with session_scope(app.config['SQLALCHEMY_DATABASE_URI']) as new_session:
            misc = new_session.query(Misc).first()
            if misc and misc.force_https:
                SSLify(app)
Example #52
0
                name_str]:
            # Multiple sets of dependencies, append library
            inputs_info[name_str]['dependencies_module'].append(
                input_data['dependencies_module'])
        else:
            # Only one set of dependencies
            inputs_info[name_str] = input_data
            if 'dependencies_module' in input_data:
                inputs_info[name_str]['dependencies_module'] = [
                    input_data['dependencies_module']
                ]  # turn into list

    inputs_info = dict(
        OrderedDict(sorted(inputs_info.items(), key=lambda t: t[0])))

    with session_scope(MYCODO_DB_PATH) as new_session:
        dict_measurements = add_custom_measurements(
            new_session.query(Measurement).all())
        dict_units = add_custom_units(new_session.query(Unit).all())

    dict_inputs = {}
    for name, data in inputs_info.items():
        if 'measurements_dict' not in data:
            continue

        for channel, measure in data['measurements_dict'].items():
            if measure["measurement"]:
                if measure["measurement"] not in dict_inputs:
                    dict_inputs[measure["measurement"]] = {}
                dict_inputs[measure["measurement"]][name] = data
#
#  Contact at kylegabriel.com

import RPi.GPIO as GPIO

from mycodo.config import SQL_DATABASE_MYCODO
from mycodo.databases.mycodo_db.models import Relays
from mycodo.databases.utils import session_scope

MYCODO_DB_PATH = 'sqlite:///' + SQL_DATABASE_MYCODO

GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)

# Start the session
with session_scope(MYCODO_DB_PATH) as the_session:
    # Get all them relays
    relays = the_session.query(Relays).all()

    for each_relay in relays:
        # Setup all the pins as an output
        GPIO.setup(each_relay.pin, GPIO.OUT)

        # First turn the relay's off that need to be off
        if not each_relay.start_state:
            GPIO.output(each_relay.pin, not each_relay.trigger)

        # Turn on relays that are set to startup on
        if each_relay.start_state:
            # Only push the trigger to the relay with the start_state
            GPIO.output(each_relay.pin, each_relay.trigger)
def upgrade():
    # Add note and tag tables
    op.create_table(
        'notes',
        sa.Column('id', sa.Integer, nullable=False, unique=True),
        sa.Column('unique_id', sa.String, nullable=False, unique=True),
        sa.Column('date_time', sa.DateTime),
        sa.Column('name', sa.Text),
        sa.Column('tags', sa.Text),
        sa.Column('files', sa.Text),
        sa.Column('note', sa.Text),
        sa.PrimaryKeyConstraint('id'),
        keep_existing=True)

    op.create_table(
        'note_tags',
        sa.Column('id', sa.Integer, nullable=False, unique=True),
        sa.Column('unique_id', sa.String, nullable=False, unique=True),
        sa.Column('name', sa.Text),
        sa.PrimaryKeyConstraint('id'),
        keep_existing=True)

    # Add note column to graphs table
    with op.batch_alter_table("dashboard") as batch_op:
        batch_op.add_column(sa.Column('note_tag_ids', sa.Text))

    # New input options
    with op.batch_alter_table("input") as batch_op:
        batch_op.add_column(sa.Column('i2c_location', sa.Text))
        batch_op.add_column(sa.Column('uart_location', sa.Text))
        batch_op.add_column(sa.Column('gpio_location', sa.Integer))

    # Rename input names
    with session_scope(MYCODO_DB_PATH) as new_session:
        for each_input in new_session.query(Input).all():
            if each_input.device == 'ATLAS_PT1000_I2C':
                each_input.interface = 'I2C'
                each_input.device = 'ATLAS_PT1000'
            elif each_input.device == 'ATLAS_PT1000_UART':
                each_input.interface = 'UART'
                each_input.device = 'ATLAS_PT1000'

            elif each_input.device == 'ATLAS_EC_I2C':
                each_input.interface = 'I2C'
                each_input.device = 'ATLAS_EC'
            elif each_input.device == 'ATLAS_EC_UART':
                each_input.interface = 'UART'
                each_input.device = 'ATLAS_EC'

            elif each_input.device == 'ATLAS_PH_I2C':
                each_input.interface = 'I2C'
                each_input.device = 'ATLAS_PH'
            elif each_input.device == 'ATLAS_PH_UART':
                each_input.interface = 'UART'
                each_input.device = 'ATLAS_PH'

            elif each_input.device == 'K30_I2C':
                each_input.interface = 'I2C'
                each_input.device = 'K30'
            elif each_input.device == 'K30_UART':
                each_input.interface = 'UART'
                each_input.device = 'K30'

            elif each_input.device == 'MH_Z16_I2C':
                each_input.interface = 'I2C'
                each_input.device = 'MH_Z16'
            elif each_input.device == 'MH_Z16_UART':
                each_input.interface = 'UART'
                each_input.device = 'MH_Z16'

            elif each_input.device == 'MH_Z19_I2C':
                each_input.interface = 'I2C'
                each_input.device = 'MH_Z19'
            elif each_input.device == 'MH_Z19_UART':
                each_input.interface = 'UART'
                each_input.device = 'MH_Z19'

            elif each_input.device == 'COZIR_CO2':
                each_input.measurements = '{meas},dewpoint'.format(meas=each_input.measurements)
                each_input.convert_to_unit = '{meas};dewpoint,C'.format(meas=each_input.convert_to_unit)

            if (each_input.location == 'RPi' or
                    each_input.device == 'RPiFreeSpace'):
                each_input.interface = 'RPi'
            elif each_input.location == 'Mycodo_daemon':
                each_input.interface = 'Mycodo'

            # Move values to new respective device locations
            if each_input.device_loc:
                each_input.uart_location = each_input.device_loc

            if each_input.device in [
                    'MCP3008', 'MAX31855', 'MAX31856', 'MAX31865']:
                each_input.interface = 'UART'

            if each_input.location and each_input.device in [
                    'ATLAS_EC', 'TSL2591', 'ATLAS_PH', 'BH1750', 'SHT2x',
                    'MH_Z16', 'CHIRP', 'BMP280', 'TMP006', 'AM2315', 'BME280',
                    'ATLAS_PT1000', 'BMP180', 'TSL2561', 'HTU21D', 'HDC1000',
                    'CCS811', 'MCP342x', 'ADS1x15']:
                each_input.i2c_location = each_input.location
                each_input.interface = 'I2C'

            if each_input.location and each_input.device in [
                    'DHT11', 'DHT22', 'SIGNAL_PWM', 'SIGNAL_RPM', 'SHT1x_7x',
                    'GPIO_STATE']:
                try:
                    each_input.gpio_location = int(each_input.location)
                except:
                    pass

        new_session.commit()
Example #55
0
def calculate_method_setpoint(method_id, table, this_controller, Method, MethodData, logger):
    """
    Calculates the setpoint from a method
    :param method_id: ID of Method to be used
    :param table: Table of the this_controller using this function
    :param this_controller: The this_controller using this function
    :param logger: The logger to use
    :return: 0 (success) or 1 (error) and a setpoint value
    """
    method = db_retrieve_table_daemon(Method)

    method_key = method.filter(Method.unique_id == method_id).first()

    method_data = db_retrieve_table_daemon(MethodData)
    method_data = method_data.filter(MethodData.method_id == method_id)

    method_data_all = method_data.filter(MethodData.output_id == None).all()
    method_data_first = method_data.filter(MethodData.output_id == None).first()

    now = datetime.datetime.now()

    # Calculate where the current time/date is within the time/date method
    if method_key.method_type == 'Date':
        for each_method in method_data_all:
            start_time = datetime.datetime.strptime(each_method.time_start, '%Y-%m-%d %H:%M:%S')
            end_time = datetime.datetime.strptime(each_method.time_end, '%Y-%m-%d %H:%M:%S')
            if start_time < now < end_time:
                setpoint_start = each_method.setpoint_start
                if each_method.setpoint_end:
                    setpoint_end = each_method.setpoint_end
                else:
                    setpoint_end = each_method.setpoint_start

                setpoint_diff = abs(setpoint_end - setpoint_start)
                total_seconds = (end_time - start_time).total_seconds()
                part_seconds = (now - start_time).total_seconds()
                percent_total = part_seconds / total_seconds

                if setpoint_start < setpoint_end:
                    new_setpoint = setpoint_start + (setpoint_diff * percent_total)
                else:
                    new_setpoint = setpoint_start - (setpoint_diff * percent_total)

                logger.debug("[Method] Start: {start} End: {end}".format(
                    start=start_time, end=end_time))
                logger.debug("[Method] Start: {start} End: {end}".format(
                    start=setpoint_start, end=setpoint_end))
                logger.debug("[Method] Total: {tot} Part total: {par} ({per}%)".format(
                    tot=total_seconds, par=part_seconds, per=percent_total))
                logger.debug("[Method] New Setpoint: {sp}".format(
                    sp=new_setpoint))
                return new_setpoint, False

    # Calculate where the current Hour:Minute:Seconds is within the Daily method
    elif method_key.method_type == 'Daily':
        daily_now = datetime.datetime.now().strftime('%H:%M:%S')
        daily_now = datetime.datetime.strptime(str(daily_now), '%H:%M:%S')
        for each_method in method_data_all:
            start_time = datetime.datetime.strptime(each_method.time_start, '%H:%M:%S')
            end_time = datetime.datetime.strptime(each_method.time_end, '%H:%M:%S')
            if start_time < daily_now < end_time:
                setpoint_start = each_method.setpoint_start
                if each_method.setpoint_end:
                    setpoint_end = each_method.setpoint_end
                else:
                    setpoint_end = each_method.setpoint_start

                setpoint_diff = abs(setpoint_end-setpoint_start)
                total_seconds = (end_time-start_time).total_seconds()
                part_seconds = (daily_now-start_time).total_seconds()
                percent_total = part_seconds/total_seconds

                if setpoint_start < setpoint_end:
                    new_setpoint = setpoint_start+(setpoint_diff*percent_total)
                else:
                    new_setpoint = setpoint_start-(setpoint_diff*percent_total)

                logger.debug("[Method] Start: {start} End: {end}".format(
                    start=start_time.strftime('%H:%M:%S'),
                    end=end_time.strftime('%H:%M:%S')))
                logger.debug("[Method] Start: {start} End: {end}".format(
                    start=setpoint_start, end=setpoint_end))
                logger.debug("[Method] Total: {tot} Part total: {par} ({per}%)".format(
                    tot=total_seconds, par=part_seconds, per=percent_total))
                logger.debug("[Method] New Setpoint: {sp}".format(
                    sp=new_setpoint))
                return new_setpoint, False

    # Calculate sine y-axis value from the x-axis (seconds of the day)
    elif method_key.method_type == 'DailySine':
        new_setpoint = sine_wave_y_out(method_data_first.amplitude,
                                       method_data_first.frequency,
                                       method_data_first.shift_angle,
                                       method_data_first.shift_y)
        return new_setpoint, False

    # Calculate Bezier curve y-axis value from the x-axis (seconds of the day)
    elif method_key.method_type == 'DailyBezier':
        new_setpoint = bezier_curve_y_out(
            method_data_first.shift_angle,
            (method_data_first.x0, method_data_first.y0),
            (method_data_first.x1, method_data_first.y1),
            (method_data_first.x2, method_data_first.y2),
            (method_data_first.x3, method_data_first.y3))
        return new_setpoint, False

    # Calculate the duration in the method based on self.method_start_time
    elif method_key.method_type == 'Duration':
        start_time = datetime.datetime.strptime(
            str(this_controller.method_start_time), '%Y-%m-%d %H:%M:%S.%f')
        ended = False

        # Check if method_end_time is not None
        if this_controller.method_end_time:
            # Convert time string to datetime object
            end_time = datetime.datetime.strptime(
                str(this_controller.method_end_time), '%Y-%m-%d %H:%M:%S.%f')
            if now > start_time:
                ended = True

        seconds_from_start = (now - start_time).total_seconds()
        total_sec = 0
        previous_total_sec = 0
        previous_end = None
        method_restart = False

        for each_method in method_data_all:
            # If duration_sec is 0, method has instruction to restart
            if each_method.duration_sec == 0:
                method_restart = True
            else:
                previous_end = each_method.setpoint_end

            total_sec += each_method.duration_sec
            if previous_total_sec <= seconds_from_start < total_sec:
                row_start_time = float(start_time.strftime('%s')) + previous_total_sec
                row_since_start_sec = (now - (start_time + datetime.timedelta(0, previous_total_sec))).total_seconds()
                percent_row = row_since_start_sec / each_method.duration_sec

                setpoint_start = each_method.setpoint_start
                if each_method.setpoint_end:
                    setpoint_end = each_method.setpoint_end
                else:
                    setpoint_end = each_method.setpoint_start
                setpoint_diff = abs(setpoint_end - setpoint_start)
                if setpoint_start < setpoint_end:
                    new_setpoint = setpoint_start + (setpoint_diff * percent_row)
                else:
                    new_setpoint = setpoint_start - (setpoint_diff * percent_row)

                logger.debug(
                    "[Method] Start: {start} Seconds Since: {sec}".format(
                        start=start_time, sec=seconds_from_start))
                logger.debug(
                    "[Method] Start time of row: {start}".format(
                        start=datetime.datetime.fromtimestamp(row_start_time)))
                logger.debug(
                    "[Method] Sec since start of row: {sec}".format(
                        sec=row_since_start_sec))
                logger.debug(
                    "[Method] Percent of row: {per}".format(
                        per=percent_row))
                logger.debug(
                    "[Method] New Setpoint: {sp}".format(
                        sp=new_setpoint))
                return new_setpoint, False
            previous_total_sec = total_sec

        if this_controller.method_start_time:
            if method_restart:
                if end_time and now > end_time:
                    ended = True
                else:
                    # Method has been instructed to restart
                    with session_scope(MYCODO_DB_PATH) as db_session:
                        mod_method = db_session.query(table)
                        mod_method = mod_method.filter(
                            table.unique_id == this_controller.unique_id).first()
                        mod_method.method_start_time = datetime.datetime.now()
                        db_session.commit()
                        return previous_end, False
            else:
                ended = True

            if ended:
                # Duration method has ended, reset method_start_time locally and in DB
                with session_scope(MYCODO_DB_PATH) as db_session:
                    mod_method = db_session.query(table).filter(
                        table.unique_id == this_controller.unique_id).first()
                    mod_method.method_start_time = 'Ended'
                    mod_method.method_end_time = None
                    db_session.commit()
                return None, True

    # Setpoint not needing to be calculated, use default setpoint
    return None, False