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