def read_publish_sensor_data():
    """ Function used to read sensor data and publish the readings to a topic
    served by an MQTT server.
    """

    # MQTT client publisher
    mqtt_publisher = MQTTPublisher()

    sensor_serial = ini_config.get("Sensor", "SENSOR_TEMPERATURE_SERIAL")
    sleep_timeout = ini_config.getint("Sensor", "SENSOR_SLEEP_TIME")

    # start daemon forever loop
    while True:

        try:
            # read and publish sensor reading to topic in MQTT server
            logging.debug("publishing sensor serial [{0}] data".format(sensor_serial))

            mqtt_publisher.publish_temperature(sensor_serial)

        except (Exception) as err:
            sys.stderr.write(str(err))
            logging.exception("Error reading/publishing sensor data. [{0}]".format(err))

        except:  # catch *all* other exceptions
            err = sys.exc_info()[0]
            logging.exception("Error occurred in mqtt daemon: [{0}]".format(err))

            write_to_file("<p>Error in mqttpublisher daemon: [{0}]</p>".format(err), sys.stderr)

        time.sleep(sleep_timeout)

    return
def subscribe_sensor_data():
    """ Function used to subscribe to topic destination where sensor readings
    are being published.
    """

    # MQTT client publisher
    mqtt_subscriber = MQTTSubscriber()

    sleep_timeout = ini_config.getint("Sensor", "SENSOR_SLEEP_TIME")

    # start daemon forever loop
    while True:

        try:
            # read and publish sensor reading to topic in MQTT server
            logging.debug("subscribing to sensor TOPIC destination...")

            mqtt_subscriber.subscribe_temperature()

        except (Exception) as err:
            sys.stderr.write(str(err))
            logging.exception("Error subscribing to sensor data. [{0}]"
                               .format(err))

        except:  # catch *all* other exceptions
            err = sys.exc_info()[0]
            logging.exception("Error occurred in mqtt daemon: [{0}]"
                               .format(err))

            write_to_file("<p>Error in mqttsubscriber daemon: [{0}]</p>"
                          .format(err), sys.stderr)

        time.sleep(sleep_timeout)

    return
    def subscribe_temperature():
        """
        Subscribe to temperature sensor data.

        Parameters
        ----------

        Returns
        -------

        """

        # The callback for when the client receives a CONNACK response from
        # the server.
        def on_connect(client, userdata, flags, rc):
            print("Connected with result code " + str(rc))

            topic = ini_config.get("MQTT", "MQTT_TOPIC")

            # Subscribing in on_connect() means that if we lose the connection
            # and reconnect then subscriptions will be renewed.
            mqttc.subscribe(topic, qos=0)

        # The callback for when a PUBLISH message is received from the server.
        def on_message(client, userdata, msg):
            print(msg.topic + " " + str(msg.payload))

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

        mqttc = mqtt.Client(client_id=mqtt_id,
                             clean_session=True,
                             protocol=mqtt.MQTTv31)

        mqttc.on_connect = on_connect
        mqttc.on_message = on_message
        mqttc.username_pw_set(user, password=pw)

        logging.debug("Connecting to MQTT Broker: "
                      "host [{0}], port [{1}], user [{2}]"
                      .format(host, port, user))

        mqttc.connect(host, port)

        # Blocking call that processes network traffic, dispatches callbacks
        # and handles reconnecting.
        # Other loop*() functions are available that give a threaded interface
        # and a manual interface.
        mqttc.loop_forever()

        return
def read_store_readings ():
    """ Function used to read and store sensor readings
    """
    logging.debug("inside read_store_readings...")

    if globalFlaskApp is None:
        logging.error("Flask has not been initialized!")
        raise EnvironmentError("Flask has not been initialized")

    # error flag used to send email only once if error occurs.
    error_flag = False

    headers = {'content-type': 'application/json'}

    sensor_url = ini_config.get("Sensor", "SENSOR_TEMPERATURE_URL")
    sensor_serial = ini_config.get("Sensor", "SENSOR_TEMPERATURE_SERIAL")

    rest_url = (sensor_url + "/" + sensor_serial)
    logging.debug("sensor REST URL is [{0}]".format(rest_url))

    rest_user = ini_config.get("Sensor", "SENSOR_REST_API_USERNAME")
    rest_pw = ini_config.get("Sensor", "SENSOR_REST_API_PASSWORD")
    req_timeout = ini_config.getint("Sensor", "SENSOR_REQUEST_TIMEOUT")
    sleep_timeout = ini_config.getint("Sensor", "SENSOR_SLEEP_TIME")
    recipient = ini_config.get("Email", "RECIPIENT_EMAIL")

    with globalFlaskApp.app_context():

        logging.debug("starting forever loop within Flask app context")

        # start daemon forever loop
        while True:

            try:
                # collect data from sensor using RESTFul API
                logging.debug("Sending request to [{0}] with user [{1}]"
                               .format(rest_url, rest_user))

                r = requests.get(rest_url,
                                 verify=False,
                                 auth=(rest_user, rest_pw),
                                 headers=headers,
                                 timeout=req_timeout)

                # if code gets here no exception has occurred. reset flag.
                error_flag = False

                if (r.status_code != 200):
                    logging.error("Response status code [{0}] : [{1}]"
                                   .format(r.status_code, r.text))
                else:
                    output = r.json()
                    sensor = output[SENSOR_KEY]
                    readings = output[DATA_KEY]

                    logging.debug("Adding sensor record with unit [{0}], "
                                   "value [{1}], utc [{2}], serial [{3}] to "
                                   "database."
                                   .format(readings["unit"],
                                           readings["value"],
                                           readings["utc"],
                                           sensor["serial"]))

                    SensorDAO.add_reading(readings["unit"],
                                          readings["value"],
                                          readings["utc"],
                                          sensor["serial"])

            # python 3.4: ConnectionRefusedError
            except (ConnectionError, Timeout) as err:  # e.g., server is down.
                sys.stderr.write(str(err))

                logging.exception("Connection Error with URL [{0}], "
                                   "user [{1}] in runserver daemon: [{2}]"
                                   .format(rest_url, rest_user, err))

                if not error_flag :  # only send email once.
                    subject = "sensorserver: Connection/Timeout Error"
                    logging.info("Sending email to [{0}] with subject [{1}]"
                                  .format(recipient, subject))
                    message = ("Connection/Timeout Error to URL [{0}]: [{1}]"
                               .format(rest_url, err))
                    try:
                        EMail.send_email(recipient, subject, message)
                        logging.info("email to [{0}] with subject [{1}] sent."
                                      .format(recipient, subject))
                    except Exception as mail_err:
                        logging.error("Error [{0}] sending email."
                                       .format(mail_err))

                error_flag = True

            except (HTTPError, RequestException) as err:
                sys.stderr.write(str(err))

                logging.exception("HTTP/Request Error to URL [{0}] in "
                                   "runserver daemon: [{1}]"
                                   .format(rest_url, err))

                if not error_flag :  # only send email once.
                    subject = "sensorserver: HTTP Error"
                    logging.info("Sending email to [{0}] with subject [{1}]"
                                  .format(recipient, subject))
                    message = ("HTTP/Request to URL [{0}] Error: [{1}]"
                               .format(rest_url, err))

                    try:
                        EMail.send_email(recipient, subject, message)
                        logging.info(
                            "email to [{0}] with subject [{1}] was sent."
                            .format(recipient, subject))
                    except Exception as mail_err:
                        logging.error("Error [{0}] sending email."
                                       .format(mail_err))

                error_flag = True

            except (EnvironmentError, Exception) as err:
                sys.stderr.write(str(err))

                logging.exception("Error in runserver daemon: [{0}]"
                                   .format(err))

                if not error_flag :  # only send email once.
                    subject = "sensorserver: Environment Error"
                    logging.info("Sending email to [{0}] with subject [{1}]"
                                  .format(recipient, subject))
                    message = "EXITING APPLICATION. Error: [{0}]".format(err)

                    try:
                        EMail.send_email(recipient, subject, message)
                        logging.info(
                            "email to [{0}] with subject [{1}] was sent."
                            .format(recipient, subject))
                    except Exception as mail_err:
                        logging.error("Error [{0}] sending email."
                                       .format(mail_err))

                error_flag = True

            except:  # catch *all* other exceptions
                err = sys.exc_info()[0]
                logging.exception("Error occurred in runserver daemon: [{0}]"
                                   .format(err))

                write_to_file("<p>Error in runsensor daemon: [{0}]</p>"
                              .format(err), sys.stderr)
                exit(1)


            time.sleep(sleep_timeout)

    return
def run():
    """ daemon run function.

    This function should be called to start the system.
    """

    instance_path = ini_config.get("Flask", "INSTANCE_PATH")

    # app: Flask application object
    logging.debug("initializing the Flask app")
    global globalFlaskApp
    globalFlaskApp = Flask(__name__,
                            instance_path=instance_path,
                            instance_relative_config=True)

    is_debug = ini_config.getboolean("Flask", "DEBUG")
    is_testing = ini_config.getboolean("Flask", "TESTING")
    is_json_sort_keys = ini_config.getboolean("Flask", "JSON_SORT_KEYS")
    max_content_length = ini_config.getint("Flask", "MAX_CONTENT_LENGTH")

    globalFlaskApp.config.update(DEBUG=is_debug,
                                  TESTING=is_testing,
                                  JSON_SORT_KEYS=is_json_sort_keys,
                                  MAX_CONTENT_LENGTH=max_content_length)


    with globalFlaskApp.app_context():

        logging.info("Starting application ...")

        from rgapps.utils.utility import get_log_file_handles
        logger_fds = get_log_file_handles(logging.getLogger())
        logging.debug("Logger file handles fileno [{0}]"
                       .format(logger_fds))

        system = platform.system()

        if system == "Linux":
            logging.info("Server running on Linux.")

            pid_file = ini_config.get("Sensor", "SENSOR_PID_FILE")
            working_dir = ini_config.get("Logging", "WORKING_DIR")

            logging.debug("Instantiating daemon with pid_file [{0}] "
                           "and working_dir [{1}]"
                           .format(pid_file, working_dir))

            import daemon.pidfile

            daemon_context = daemon.DaemonContext(
                working_directory=working_dir,
                umask=0o002,
                pidfile=daemon.pidfile.PIDLockFile(pid_file))

            logging.debug("Setting up daemon signal map")
            daemon_context.signal_map = { signal.SIGTERM: program_cleanup }
            logging.debug("daemon signal map has been setup")

            if (logger_fds):
                logging.debug("setting files_preserve for the log file "
                               "descriptor [{0}]"
                               .format(logger_fds))
                daemon_context.files_preserve = logger_fds

            logging.debug("Starting daemon by opening its context.")
            daemon_context.open()

            logging.info("Calling read_store_readings....")
            read_store_readings()

            logging.debug("Closing the daemon context.")
            daemon_context.close()

        else:
            logging.info("Server running on Windows system ...")
            read_store_readings()

    return
__license__ = "All Rights Reserved"
__maintainer__ = "Rubens Gomes"
__email__ = "*****@*****.**"
__status__ = "Experimental"

instance_path = ini_config.get("Flask", "INSTANCE_PATH")

logging.info("creating Flask app ...")
app = Flask(__name__,
            instance_path=instance_path,
            instance_relative_config=True)

is_debug = ini_config.getboolean("Flask", "DEBUG")
is_testing = ini_config.getboolean("Flask", "TESTING")
is_json_sort_keys = ini_config.getboolean("Flask", "JSON_SORT_KEYS")
max_content_length = ini_config.getint("Flask", "MAX_CONTENT_LENGTH")

app.config.update(DEBUG=is_debug,
                   TESTING=is_testing,
                   JSON_SORT_KEYS=is_json_sort_keys,
                   MAX_CONTENT_LENGTH=max_content_length)

with app.app_context():
    logging.info("Configuring the Flask HTTP routing.")
    setup_routes()

    logging.info("Setting up the Flask functions.")
    import rgapps.http.flaskfunctions

    logging.info("Flask WSGI app is now running ...")
    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