Пример #1
0
    def __init__(self, value, unit, utc):
        """constructor.

        Parameters
        ----------
        value:  float (required)
            the measurement reading numeric value
        value:  string (required)
            the measurement reading unit
        utc: string (required)
            the UTC timestamp
        """
        if not is_number(value):
            raise IllegalArgumentException("value is invalid.")

        if is_blank(unit):
            raise IllegalArgumentException("unit is required.")

        if is_blank(utc):
            raise IllegalArgumentException("utc is required.")

        self.value = value
        self.unit = unit
        self.utc = utc
        return
Пример #2
0
    def convert(from_value, from_unit, to_unit):
        """convert length units.

        Parameters
        ----------
        from_value:  str (required)
            numeric value
        from_unit:  str (required)
            length unit
        to_unit:  str (required)
            length unit

        Returns
        -------
        float:
            the converted value
        """
        if not is_number(from_value):
            raise IllegalArgumentException(
                ("Parameter from_value=[{0}] not valid. "
                 "A numeric value must be provided.").format(from_value))

        if is_blank(from_unit):
            raise IllegalArgumentException(
                ("Parameter from_unit=[{0}] not valid. A unit be provided.")
                .format(from_unit))

        if is_blank(to_unit):
            raise IllegalArgumentException(
                ("Parameter to_unit=[{0}] not valid. A unit be provided.")
                .format(to_unit))

        if from_unit == to_unit:
            raise IllegalArgumentException(
                ("from_unit=[{0}] and to_unit=[{1}] units cannot be equal")
                .format(from_unit, to_unit))

        # pint temperature units need to be lower-cased or degC, degF, degK
        from_unit = from_unit.lower().strip()
        if from_unit == "degc":
            from_unit = "degC"
        elif from_unit == "degf":
            from_unit = "degF"
        elif from_unit == "degk":
            from_unit = "degK"

        # pint temperature units need to be lower-cased or degC, degF, degK
        to_unit = to_unit.lower().strip()
        if to_unit == "degc":
            to_unit = "degC"
        elif to_unit == "degf":
            to_unit = "degF"
        elif to_unit == "degk":
            to_unit = "degK"

        result = convert_unit(UNIT_TYPES_ENUM.temperature, from_unit,
                              from_value, to_unit)
        return result
Пример #3
0
    def send_text(phone_number, message):
        """Sends given SMS message using http://textbelt.com/

        Parameters
        ----------
        phone_number:  str (required)
            recipient numeric string for a valid phone number. It cannot be
            greater than 20 digits
        message: str (optional)
            message to send. It cannot be greater than 40 characters.

        Returns
        -------
            nothing.
        """

        if is_blank(phone_number):
            raise IllegalArgumentException("phone_number is required")

        if not is_number(phone_number):
            raise IllegalArgumentException(
                "phone_number [{0}] needs to be a number."
                .format(phone_number))

        if len(phone_number.strip()) > SMS.MAX_PHONE_LENGTH:
            raise IllegalArgumentException(
                 "phone_number [{0}] cannot be greater than [{1}]."
                .format(phone_number, SMS.MAX_PHONE_LENGTH))

        if is_blank(message) :
            raise IllegalArgumentException ("message cannot blank")


        if len(message.strip()) > SMS.MAX_MSG_LENGTH:
            raise IllegalArgumentException(
                "message [{0}] cannot be greater than [{1}]."
                .format(message, SMS.MAX_MSG_LENGTH))

        url = 'http://textbelt.com/text'
        payload = {'number': phone_number,
                   'message': "iotgw.rubens.home: " + message.strip()}

        logging.debug("Sending SMS to [{0}] using url [{1}]"
                      .format(phone_number, url))

        r = requests.post(url, data=payload)

        if (r.status_code != 200):
            logging.debug("Failed SMS to [{0}] using url [{1}]: status [{2}]"
                          .format(phone_number, url, r.status_code))
            r.raise_for_status()

        return
Пример #4
0
    def add_sensor(serial, geolocation, location, address, state, name,
                    sensor_type, description):
        """
        Adds given sensor data to sensor database

        Parameters
        ----------
        serial:  str (required)
            sensor unique serial number.
        geolocation:  str (optional)
            GEO Location: LATITUDE, LONGITUDE 
        location: str (optional)
            ENGINE, HOME, PATIO, ...
        address: str (optional)
            Address where sensor is located
        state: str (required)
            UP, DOWN, ...
        name: str (required)
            name to help identify this sensor
        sensor_type: str (required)
            HUMIDITY, PRESSURE, TEMPERATURE, VELOCITY,  ...
        description: str(optional)
            helps describe this sensor

        Returns
        -------
            nothing.
        """

        if is_blank(serial):
            raise IllegalArgumentException("serial is required.")

        if is_blank(state):
            raise IllegalArgumentException("state is required.")

        if is_blank(name):
            raise IllegalArgumentException("name is required.")

        if is_blank(sensor_type):
            raise IllegalArgumentException("sensor_type is required.")

        test_sensor = SensorDAO.get_sensor(serial)
        if test_sensor is not None:
            raise IllegalArgumentException("sensor with serial [{0}] " 
                                            "is already registered."
                                            .format(serial))

        SensorDAO.SENSOR_DB.add_sensor(serial, geolocation, location, address, 
                                       state, name, sensor_type, description)

        return
Пример #5
0
    def handle_request_timeout(self, error):
        """
        This method handles an exception error when the request times out.

        Parameters
        ----------
        error:  Exception object (required)
            the exception error object.

        Returns
        -------
            an HTTP response object with appropriate error message.
        """
        logging.debug("inside handle_request_timeout")

        code = ""

        if hasattr(error, 'code'):
            code = getattr(error, 'code')

        logging.debug("Handling a status_code [{0}] error [{1}]"
                      .format(code, error))

        # if a description was found on the error, use that one instead
        description = get_error_description(error)
        if is_blank(description):
            description = "URL [{0}] request timed out".format(request.url)

        response = ErrorResponse.get_response(408,
                                              description,
                                              "application/json")
        response.headers["WWW-Authenticate"] = 'Basic realm = "IoT Gateway"'
        return response
Пример #6
0
    def handle_internal_server_error(self, error):
        """
        This method handles an internal server exception error.

        Parameters
        ----------
        error:  Exception object (required)
            the exception error object.

        Returns
        -------
            an HTTP response object with appropriate error message.
        """
        logging.debug("inside handle_internal_server_error")

        code = 500

        if hasattr(error, 'code'):
            code = getattr(error, 'code')

        logging.debug("Handling a status_code [{0}] error [{1}]"
                       .format(code, error))

        # if a description was found on the error, use that one instead
        description = get_error_description(error)
        if is_blank(description):
            description = "An internal server error occurred"

        if isinstance(error, Exception):
            logging.exception(error)

        response = ErrorResponse.get_response(code, description,
                                               "application/json")
        return response
Пример #7
0
    def method_not_allowed(self, error):
        """
        This method handles an exception error when the HTTP method
        sent is not supported.

        Parameters
        ----------
        error:  Exception object (required)
            the exception error object.

        Returns
        -------
            an HTTP response object with appropriate error message.
        """
        logging.debug("inside method_not_allowed")

        code = ""

        if hasattr(error, 'code'):
            code = getattr(error, 'code')

        logging.debug("Handling a status_code [{0}] error [{1}]"
                                 .format(code, error))

        method = request.method

        # if a description was found on the error, use that one instead
        description = get_error_description(error)
        if is_blank(description):
            description = "The HTTP method [{0}] is not supported.".format(method)

        response = ErrorResponse.get_response(405,
                                              description,
                                              "application/json")
        return response
Пример #8
0
    def handle_not_expected(self, error):
        """
        This method handles an exception error for an HTTP status code that
        is currently not supported, and it was not expected.

        Parameters
        ----------
        error:  Exception object (required)
            the exception error object.

        Returns
        -------
            an HTTP response object with appropriate error message.
        """
        logging.debug("inside handle_not_expected")

        code = ""

        if hasattr(error, 'code'):
            code = getattr(error, 'code')

        logging.debug("Handling a status_code [{0}] error [{1}]"
                      .format(code, error))

        # if a description was found on the error, use that one instead
        description = get_error_description(error)
        if is_blank(description):
            description = ("URL [{0}] request generate a not expected status"
                            .format(request.url))

        response = ErrorResponse.get_response(401,
                                              description,
                                              "application/json")
        response.headers["WWW-Authenticate"] = 'Basic realm = "IoT Gateway"'
        return response
Пример #9
0
    def bad_request(self, error):
        """
        This method handles an exception error when the HTTP request
        could not be served due to a malformed request (e.g., some
        parameters are not valid).

        Parameters
        ----------
        error:  Exception object (required)
            the exception error object.

        Returns
        -------
            an HTTP response object with appropriate error message.

        """
        logging.debug("inside bad_request")

        code = ""

        if hasattr(error, 'code'):
            code = getattr(error, 'code')

        logging.debug("Handling a status_code [{0}] error [{1}]"
                      .format(code, error))

        # if a description was found on the error, use that one instead
        description = get_error_description(error)
        if is_blank(description):
            description = "Bad URL [{0}] request".format(request.url)

        response = ErrorResponse.get_response(400,
                                              description,
                                              "application/json")
        return response
Пример #10
0
    def convert(from_value, from_unit, to_unit):
        """convert length units.

        Parameters
        ----------
        from_value:  float (required)
            numeric value
        from_unit:  str (required)
            length unit
        to_unit:  str (required)
            length unit

        Returns
        -------
        float:
            the converted value

        """
        if not is_number(from_value):
            raise IllegalArgumentException(
                ("Parameter from_value=[{0}] not valid. "
                 "A numeric value must be provided.").format(from_value))

        if is_blank(from_unit):
            raise IllegalArgumentException(
                ("Parameter from_unit=[{0}] not valid. A unit be provided.")
                .format(from_unit))

        if is_blank(to_unit):
            raise IllegalArgumentException(
                ("Parameter to_unit=[{0}] not valid. A unit be provided.")
                .format(to_unit))

        if from_unit == to_unit:
            raise IllegalArgumentException(
                ("from_unit=[{0}] and to_unit=[{1}] units cannot be equal")
                .format(from_unit, to_unit))

        # pint unit registry only accepts lower case letters for length units
        from_unit = from_unit.lower().strip()
        to_unit = to_unit.lower().strip()

        result = convert_unit(UNIT_TYPES_ENUM.length, from_unit,
                              from_value, to_unit)
        return result
Пример #11
0
    def add_reading(unit, value, utc, serial):
        """
        Adds given measurement data to sensor database

        Parameters
        ----------
        unit:  str (required)
            degF, degC, degF.
        value:  float (required)
            a float number
        utc: str (required)
            UTC timestamp of the reading
        serial: str (required)
            sensor unique serial number

        Returns
        -------
            nothing.
        """

        if is_blank(unit):
            raise IllegalArgumentException("unit is required.")

        if not value:
            raise IllegalArgumentException("utc is required.")

        if not is_number(value):
            raise IllegalArgumentException("value is not a numeric value.")

        if is_blank(utc):
            raise IllegalArgumentException("utc is required.")

        if is_blank(serial):
            raise IllegalArgumentException("serial is required.")

        test_sensor = SensorDAO.get_sensor(serial)
        if test_sensor is None:
            raise IllegalArgumentException("sensor with serial [{0}] " 
                                            "is not registered in the system."
                                            .format(serial))

        SensorDAO.SENSOR_DB.add_reading(unit, value, utc, serial)

        return
Пример #12
0
    def __init__(self, serial):
        """constructor.
        Parameters
        ----------
        serial:  str (required)
            sensor serial or identification string
        """
        if is_blank(serial):
            raise IllegalArgumentException("serial is required.")

        self.serial = serial
Пример #13
0
    def get_response(code, description, mime_type, title=None):
        """Returns an HTTP flask response

        Parameters:
        ----------
        code:  int (required)
            The HTTP status code between 400 and 500 (inclusive)
        description: string (required)
            The text description related to the status
        mime_type: string (optional)
            The text corresponding to the mime-type
        title: string (optional)
            The text of a title to be rendered at an HTML page

        Returns:
        -------
        A Flask HTTP Response object

        Raises:
        ------
        Raises TypeError or ValueError if argument is not valid.
        """

        if not code or not isinstance(code, (int)):
            raise TypeError("code [{0}] is not valid".format(code))
        elif (code < 400) or (code > 500):
            raise ValueError("code [{0}] is not valid. "
                             "It must be between 400 and 500"
                             .format(code))
        elif is_blank(description):
            logging.warn("error description is blank")
            description = "no error description found"

        if re.search("html", mime_type, flags=re.IGNORECASE):
            body = ErrorResponse.get_html_response_body(
                code,
                description,
                "HTTP Status Code:{0}".format(code))
            response = Response(body, status=code, mimetype="text/html")
        elif re.search("xml", mime_type, flags=re.IGNORECASE):
            body = ErrorResponse.get_xml_response_body(code, description)
            response = Response(body, status=code, mimetype="application/xml")
        elif re.search("csv", mime_type, flags=re.IGNORECASE):
            body = ErrorResponse.get_csv_response_body(code, description)
            response = Response(body, status=code, mimetype="text/csv")
        elif re.search("json", mime_type, flags=re.IGNORECASE):
            response = ErrorResponse.get_json_response(code, description)
        else:  # default to JSON
            response = ErrorResponse.get_json_response(code, description)

        return response
Пример #14
0
    def database(name):
        """
        Returns a valid and connected Mongo Database object with
        the given name.

        Parameters:
        ----------
        name: str (required)
            the name of the MongoDB database

        Returns
        -------
        A Mongo Database instance.

        Raises
        ------
        Raises MongoDB exception if MongoDB is not up and running, and the
        client could NOT connect to the database
        """

        if is_blank(name):
            raise IllegalArgumentException("name is required.")

        client = MongoDB._client()

        db_found = False
        dbs = client.database_names()

        for db_name in dbs:
            if (db_name == name):
                db_found = True
                break

        if not db_found:
            client.close()
            raise IllegalArgumentException("MongoDB DB name [{0}] not found!"
                                           .format(name))

        db = client['{0}'.format(name)]

        logging.debug("MongoDB with database name [{0}] found."
                      .format(name))

        return db
Пример #15
0
    def del_readings(serial):
        """
        Deletes all measurement data for the given serial

        Parameters
        ----------
        serial: str (required)
            sensor unique serial number

        Returns
        -------
            nothing.
        """

        if is_blank(serial):
            raise IllegalArgumentException("serial is required.")

        SensorDAO.SENSOR_DB.del_readings(serial)

        return
Пример #16
0
    def handle_not_acceptable(self, error):
        """
        This method handles an exception error when none of the media
        types defined in the incoming HTTP Accept header is supported
        by this REST API.

        Parameters
        ----------
        error:  Exception object (required)
            the exception error object.

        Returns
        -------
            an HTTP response object with appropriate error message.
        """
        logging.debug("inside handle_not_acceptable")

        code = ""

        if hasattr(error, 'code'):
            code = getattr(error, 'code')

        logging.debug("Handling a status_code [{0}] error [{1}]"
                       .format(code, error))

        mimeType = request.accept_mimetypes.best
        logging.debug("Handling a 406 error [{0}].  "
                                 "The best mime type is [{1}]"
                                 .format(error, mimeType))
        accept = request.headers['Accept']

        # if a description was found on the error, use that one instead
        description = get_error_description(error)
        if is_blank(description):
            description = ("Requested MIME types [{0}] not supported."
                            .format(accept))

        response = ErrorResponse.get_response(406,
                                              description,
                                              mimeType)
        return response
Пример #17
0
    def handle_not_found(self, error):
        """
        This method handles an exception error when the requested URL
        resource was not found on the server.

        Parameters
        ----------
        error:  Exception object (required)
            the exception error object.

        Returns
        -------
            an HTTP response object with appropriate error message.

        """
        logging.debug("inside handle_not_found")

        code = ""

        if hasattr(error, 'code'):
            code = getattr(error, 'code')

        logging.debug("Handling a status_code [{0}] error [{1}]"
                                 .format(code, error))

        url_not_found = request.url
        logging.debug("Handling a URL [{0}] not found error [{1}]"
                                 .format(url_not_found, error))

        # if a description was found on the error, use that one instead
        description = get_error_description(error)
        if is_blank(description):
            description = ("Requested resource identified by [{0}] not found."
                            .format(url_not_found))

        response = ErrorResponse.get_response(404,
                                              description,
                                              "application/json")
        return response
Пример #18
0
    def get_sensor(serial):
        """
        Returns a tuble corresponding to the sensor table in the database
        for the given sensor serial.

        Parameters
        ----------
        serial: str (required)
            sensor unique serial number

        Returns
        -------
        dict:
            A sensor dictionary containing column names as keys, and
            corresponding values.
        """

        if is_blank(serial):
            raise IllegalArgumentException("serial is required.")

        data = SensorDAO.SENSOR_DB.get_sensor(serial)

        return data
Пример #19
0
def initialize_environment(ini_file_path, log_file_path=None):
    """ Loads the configuration file and initializes logging.

    This method should only be called once at the start up of the
    application to read in the configuration file, and set up logging.

    Parameters:
    ----------
    ini_file_path: str (required)
        the full path to the application INI configuration file
    log_file_path: str (optional)
        aternate path to the application logging file.  The default log file
        path is defined in the INI configuration file
    """

    if is_blank(ini_file_path):
        raise IllegalArgumentException("ini_file_path is required.")

    ini_config.read(ini_file_path)

    # ensure working dir folder is available
    working_dir = ini_config.get("Logging", "WORKING_DIR")

    if not os.path.exists(working_dir):
        os.makedirs(working_dir, 0o775)

    # log file fullname including its path
    if log_file_path is None:
        log_file_path = ini_config.get("Logging", "LOG_FILE")

    # ensure log file is writeable
    log_file = open(log_file_path.encode("unicode-escape"), "w")
    log_file.close()

    max_bytes = ini_config.getint("Logging", "LOG_FILE_MAX_BYTES")
    backup_count = ini_config.getint("Logging", "LOG_BACKUP_COUNT")
    log_level = ini_config.get("Logging", "LOG_LEVEL")

    # define the log level
    level = logging.INFO

    if log_level.upper().strip() == "CRITICAL":
        level = logging.CRITICAL
    elif log_level.upper().strip() == "ERROR":
        level = logging.ERROR
    elif log_level.upper().strip() == "WARNING":
        level = logging.WARNING
    elif log_level.upper().strip() == "INFO":
        level = logging.INFO
    elif log_level.upper().strip() == "DEBUG":
        level = logging.DEBUG
    else:
        raise ConfigurationException("LOG_LEVEL [{0}] not valid in [{1}]."
                                      .format(log_level, log_file_path))

    # create logging file handler
    log_file_handler = RotatingFileHandler(
        log_file_path,
        maxBytes=max_bytes,
        backupCount=backup_count
        )
    log_file_handler.setLevel(level)

    log_format = ("%(asctime)s - %(name)s - %(funcName)s:%(lineno)d "
                  "- %(levelname)s - %(message)s")
    date_fmt = "%m/%d/%Y %I:%M:%S %p"

    # create logging formatter handler and add it to log handler
    formatter = logging.Formatter(log_format, date_fmt)
    log_file_handler.setFormatter(formatter)

    logging.getLogger().addHandler(log_file_handler)
    logging.getLogger().setLevel(level)

    return
Пример #20
0
    def publish_temperature(serial):
        """
        Publishes the given temperature sensor data to MQTT
        message broker.

        Parameters
        ----------
        serial:  str (required)
            sensor serial number

        Returns
        -------

        """

        # test serial
        if is_blank(serial):
            raise IllegalArgumentException("serial is required")

        mqtt_id = ini_config.get("MQTT", "MQTT_CLIENT_ID")
        user = ini_config.get("MQTT", "MQTT_USERNAME")
        pw = ini_config.get("MQTT", "MQTT_PASSWORD")
        host = ini_config.get("MQTT", "MQTT_HOST")
        port = ini_config.getint("MQTT", "MQTT_PORT")
        topic = ini_config.get("MQTT", "MQTT_TOPIC")


        mqttc = mqtt.Client(client_id=mqtt_id,
                             clean_session=True,
                             protocol=mqtt.MQTTv31)
        mqttc.username_pw_set(user, password=pw)

        sensor_temperature = DS18B20Sensor(serial)
        readings = sensor_temperature.get_measurement()

        message = OrderedDict()
        message["value"] = readings.get_value()
        message["unit"] = readings.get_unit()
        message["utc"] = readings.get_utc()

        json_message = json.dumps(message, indent=2, sort_keys=True)

        auth = OrderedDict()
        auth["username"] = user
        auth["password"] = pw

        logging.debug("Publishing to MQTT Broker: "
                      "host [{0}], port [{1}], client id [{2}], "
                      "user [{3}], sensor serial [{4}]"
                      .format(host, port, mqtt_id, user, serial))

        publish.single(topic,
                        payload=json_message, qos=0,
                        retain=False,
                        hostname=host,
                        port=port,
                        client_id=mqtt_id,
                        keepalive=20,
                        auth=auth)

        logging.debug("Message [{0}] was published correctly: "
                      "host [{1}], port [{2}], client id [{3}], "
                      "user [{4}], sensor serial [{5}]"
                      .format(message, host, port, mqtt_id, user, serial))

        return
Пример #21
0
    def send_email(recipient, subject, message):
        """Sends given message using Gmail credentials defined in the
        configuration file.

        Parameters
        ----------
        recipient:  str (required)
            recipient email address
        subject:  str (optional)
            email subject
        message: str (optional)
            message to send

        Returns
        -------
            nothing.

        Raises
        ------
        SMTPHeloError:
            The server didn't reply properly to the helo greeting.
        SMTPAuthenticationError:
            The server didn't accept the username/password combination.
        SMTPException:
            No suitable authentication method was found.
        SMTPRecipientsRefused:
          The server rejected ALL recipients (no mail was sent).
        SMTPSenderRefused:
            The server didn't accept the from_addr.
        SMTPDataError:
            The server replied with an unexpected error code (other than a
            refusal of a recipient).
        """

        if is_blank (recipient):
            raise IllegalArgumentException("recipient is required.")

        # ensure valid GMail Account
        gmail_account = ini_config.get("Email", "GMAIL_ACCOUNT")
        is_valid = validate_email(gmail_account)

        if(not is_valid):
            raise IllegalArgumentException(
                "GMail Account [{0}] is not valid email address"
                .format(gmail_account))

        gmail_user = ini_config.get("Email", "GMAIL_ACCOUNT")
        gmail_password = ini_config.get("Email", "GMAIL_PASSWORD")

        logging.debug("Sending email using Gmail account [{0}]  "
                      "to recipient [{1}]"
                      .format(gmail_user, recipient))

        is_valid = validate_email(recipient)

        if(not is_valid):
            raise IllegalArgumentException(
                "Recipient [{0}] is not valid email address"
                .format(recipient))

        msg = MIMEMultipart()
        msg['From'] = gmail_user
        msg['To'] = recipient
        msg['Subject'] = subject
        msg.attach(MIMEText(message))

        logging.debug("Sending email to [{0}] using Gmail account [{1}]"
                      .format(recipient, gmail_user))

        mail_server = smtplib.SMTP_SSL('smtp.gmail.com', 465)
        mail_server.login(gmail_user, gmail_password)
        failed_recipients = mail_server.sendmail(
            gmail_user, recipient, msg.as_string())

        if failed_recipients:
            logging.warn("Email failed to following recipients [{0}]"
                         .format(failed_recipients))

        mail_server.close()

        return
Пример #22
0
    def get_readings(serial, duration):
        """
        Returns .....

        Parameters
        ----------
        serial: str (required)
            sensor unique serial number
        duration: str (required)
            a valid DURATION_ENUM

        Returns
        -------
        dict:
            A sensor dictionary containing column names as keys, and
            corresponding values.
        """

        if is_blank(serial):
            raise IllegalArgumentException("serial is required.")

        if is_blank(duration):
            raise IllegalArgumentException("duration is required.")

        logging.debug("Retrieving readings from sensor with serial [{0}] using "
                      "duration [{1}] from database."
                      .format(serial, duration))

        arrow_utcnow = arrow.utcnow()
        logging.debug("current UTC now [{0}]".format(str(arrow_utcnow)))

        arrow_utcpast = None


        # I had to define the following variables to fix PyDev Error:
        # Undefined variable from import: name.  PyDev 
        last5Years = DURATION_ENUM.last5Years
        last1Year = DURATION_ENUM.last1Year
        last6Months = DURATION_ENUM.last6Months
        last90Days = DURATION_ENUM.last90Days
        last60Days = DURATION_ENUM.last60Days
        last30Days = DURATION_ENUM.last30Days
        last21Days = DURATION_ENUM.last21Days
        last7Days = DURATION_ENUM.last7Days
        last3Days = DURATION_ENUM.last3Days
        lastDay = DURATION_ENUM.lastDay
        last24Hours = DURATION_ENUM.last24Hours
        last12Hours = DURATION_ENUM.last12Hours
        last6Hours = DURATION_ENUM.last6Hours
        lastHour = DURATION_ENUM.lastHour

        if duration.lower().strip() == last5Years.name.lower():
            arrow_utcpast = arrow_utcnow.replace(years=-5)
        elif duration.lower().strip() == last1Year.name.lower():
            arrow_utcpast = arrow_utcnow.replace(years=-1)
        elif duration.lower().strip() == last6Months.name.lower():
            arrow_utcpast = arrow_utcnow.replace(months=-6)
        elif duration.lower().strip() == last90Days.name.lower():
            arrow_utcpast = arrow_utcnow.replace(days=-90)
        elif duration.lower().strip() == last60Days.name.lower():
            arrow_utcpast = arrow_utcnow.replace(days=-60)
        elif duration.lower().strip() == last30Days.name.lower():
            arrow_utcpast = arrow_utcnow.replace(days=-30)
        elif duration.lower().strip() == last21Days.name.lower():
            arrow_utcpast = arrow_utcnow.replace(days=-21)
        elif duration.lower().strip() == last7Days.name.lower():
            arrow_utcpast = arrow_utcnow.replace(days=-7)
        elif duration.lower().strip() == last3Days.name.lower():
            arrow_utcpast = arrow_utcnow.replace(days=-3)
        elif duration.lower().strip() == lastDay.name.lower():
            arrow_utcpast = arrow_utcnow.replace(days=-1)
        elif duration.lower().strip() == last24Hours.name.lower():
            arrow_utcpast = arrow_utcnow.replace(hours=-24)
        elif duration.lower().strip() == last12Hours.name.lower():
            arrow_utcpast = arrow_utcnow.replace(hours=-12)
        elif duration.lower().strip() == last6Hours.name.lower():
            arrow_utcpast = arrow_utcnow.replace(hours=-6)
        elif duration.lower().strip() == lastHour.name.lower():
            arrow_utcpast = arrow_utcnow.replace(hours=-1)
        else:
            raise IllegalArgumentException("duration [{0}] is not valid"
                                           .format(duration))

        logging.debug("current UTC [{0}], past UTC [{1}], duration[{2}]"
                      .format(str(arrow_utcnow), str(arrow_utcpast), duration))

        current_datetime = str(arrow_utcnow)
        past_datetime = str(arrow_utcpast)

        data = SensorDAO.SENSOR_DB.get_readings(serial, current_datetime, 
                                                past_datetime)

        return data