Beemon Sensors

The Sensor Class:

class sensor.Sensor(recording_start_time_queue: Queue, config: Config | None = None)[source]
__init__(recording_start_time_queue: Queue, config: Config | None = None)[source]

Initializer for all Sensor objects. Concrete subclasses are expected to call the parent classes’ initializer.

Parameters:
  • recording_start_time_queue (Queue) – A reference/pointer to the multiprocessing.Queue instance that houses the datetime.datetime’s at which recording is to begin for the sensor.

  • config (Optional[Config]) – A reference/pointer to an existing beemon Config object, should one exist. If None is provided, a new beemon configuration Config object will be created for the beemon-config.ini in the repository’s root directory.

  • local_output_directory (Optional[str]) – The root directory to which files containing data recorded by the sensor should be output. If None is provided, this value will be determined by the Config object provided during instantiation. If no Config object was provided during instantiation, this value will default to the value specified in the beemon-config.ini found in the repository’s root directory.

  • capture_duration_seconds (Optional[int]) – How long the sensor is to record for (in seconds) at the start of each pre-determined recording time in the recording_start_time_queue. If None is provided, this value will be determined by the Config object provided during instantiation. If no Config object was provided during instantiation, this value will default to the value specified in the beemon-config.ini found in the repository’s root directory.

  • capture_window_start_time (Optional[datetime]) – When the first recording (of capture_duration_seconds in length) should transpire. This datetime.datetime is inclusive, if a value of 0800 is specified, the first recording will be taken at 8:00 AM EDT. If None is provided, this value will be determined by the Config object provided during instantiation. If no Config object was provided during instantiation, this value will default to the value specified in the beemon-config.ini found in the repository’s root directory.

  • capture_window_end_time (Optional[datetime]) – When the last recording (of capture_duration_seconds in length) should transpire. This datetime.datetime is inclusive, if a value of 2000 is specified, the last recording will be taken at 8:00 PM EDT. If None is provided, this value will be determined by the Config object provided during instantiation. If no Config object was provided during instantiation, this value will default to the value specified in the beemon-config.ini found in the repository’s root directory.

classmethod __subclasshook__(subclass: type)[source]

Called when the built-in method issubclass() is called on the Sensor class. This method inspects vital class properties of Sensor to ensure that the provided subclass is substitutable for the base Sensor class. More formally, it is this method’s responsibility to enforce the Liskov Substitution Principal (LSP).

Parameters:

subclass (type) – The potential subclass of sensor.Sensor whose membership is to be conclusively determined.

Returns:

True if the provided subclass is an LSP-valid subclass of sensor.Sensor, False otherwise.

Return type:

bool

abstract record(filename: str, uploader_output_dir: str) Dict[str, Any][source]

Concrete Sensor subclasses are required to implement this method. It is called every time that a pre-specified recording start time is read from the Sensor.recording_start_time_queue. In the overridden version of this method, concrete subclasses are expected to do the following:

  1. Instruct their constituent firmware/hardware to record for a duration of Sensor.capture_duration_seconds

  2. Call the Sensor.update_health_status() method with the result of the recording attempt.

  3. Save the intermittent results of that recording to a temporary file (in /tmp). This is only necessary for sensor recordings that are not instantaneous such as video and audio. Other sensors can record directly to uploader_output_dir as in step 4 below.

  4. Rename the recorded file (when finished) to the provided filename parameter.

  5. Move the final renamed file to the specified uploader_output_dir parameter (e.g. /home/bee/appmais/bee_tmp/). This will trigger the Uploader watching this directory to transfer the recording via SFTP to the remote server.

  6. Return a dictionary of key:value paired attributes which the concrete Sensor subclass wishes to send northbound to the AppMAIS server.

Notes

Concrete subclasses are welcome to use this method to reload the beemon configuration file (if changes made prior to the recording session should take effect without "stopping" and "starting" the sensor again). This is a safe place to do so, as this method is guaranteed to be called whenever a scheduled recording start time (from the Sensor.recording_start_time_queue) arrives. However, note that any access to the config file may need to be made multiprocessing safe, as multiple concrete subclass object/processes could be reading from the same object concurrently.

Parameters:
  • filename (str) – What the file name of the recorded measurement should be. For instance: "rpi4-11@2021-11-27@19-30-00". Note that it is up to the concrete subclass to designate and append the file type (e.g. ".wav", ".h264") to the provided filename.

  • uploader_output_dir (str) – The local output directory which the uploader.Uploader multiprocessing.Process is watching for file changes. The final (and renamed) recorded file should be output to this directory. For example: "home/bee/bee_tmp/video/2021-11-27/".

Returns:

Dict[str, Any] A dictionary of key-value pairs corresponding to the recorded measurement from the sensor. For instance, the temperature sensor may return {"Temperature": 45.5, "Humidity": 23.0} whereas the scale sensor may return {"Weight": 20.00}, and the audio sensor may return an empty dictionary. Whatever is returned by this method will be passed to Sensor.transmit() as kwargs. You must return a dictionary from this method, although you do not have to override Sensor.transmit(). If Sensor.transmit() is not overridden, the dictionary you return from this method will be un-utilized. If Sensor.transmit() is overridden, the dictionary returned from this method will be passed as kwargs to the Sensor.transmit() method to facilitate the sending of data northbound to ThingsBoard.

run() None[source]

Houses the main-loop/process to manage recording for all beemon Sensor-like objects. This method runs repeatedly until the sensor’s recording_start_time_queue is empty. This queue is purged by the orchestrator.Orchestrator class when a "stop <sensor>" command is received. When this occurs, this method will return, allowing the garbage collector to terminate the multiprocessing.Process instance gracefully.

Todo

Modify exit condition to be a stop() command received in addition to an empty queue.

Notes

This method is invoked automatically on multiprocessing.Process instances when the multiprocessing.Process.start() method is run. The orchestrator.Orchestrator instance will call the sensor.Sensor.start() method directly, which then invokes this method.

abstract start() None[source]

This method is called by the orchestrator.Orchestrator class (specifically in the orchestrator.Orchestrator.start_command() method) when a "start <sensor>" command is sent to the server.Server process. The parent Sensor class allows concrete subclasses to optionally override this method in order to perform potentially long-running hardware/firmware sensor initialization routines. Concrete subclasses are expected to call the parent classes’ method first.

Notes

The abstract version of this method is intended to contain operations common to all concrete subclass Sensor objects during initialization. Currently, there are no common initialization operations besides logging.

transmit(**kwargs) None[source]

Transmits the latest sensor measurement (from the concrete subclass) to the ThingsBoard/Dashboard server (at the location specified in the beemon-config.ini) in JSON format over MQTT.

Parameters:

**kwargs (Dict[str, Any]) – A dictionary of keyword arguments returned by the concrete subclass in Sensor.record(). Can be any number of key:value pairs.

Returns:

An optional error message which will be logged as a logger.error() by the parent Sensor class when this method is invoked.

Return type:

Optional[str]

The Audio Class:

class sensor.Audio(recording_start_time_queue: Queue, config: Config | None = None, audio_sample_frequency_hz: int | None = 48000, sample_format: int | None = 24, channel_count: int | None = 1, gain: int | None = 49)[source]
__init__(recording_start_time_queue: Queue, config: Config | None = None, audio_sample_frequency_hz: int | None = 48000, sample_format: int | None = 24, channel_count: int | None = 1, gain: int | None = 49)[source]

Initializer for objects of type Audio.

Notes

This class is dynamically initialized (as with all concrete Sensor subclasses) in the orchestrator.Orchestrator._dynamically_initialize_sensors() method.

Parameters:
  • recording_start_time_queue (Queue) – A reference/pointer to the Queue instance that houses the datetime’s at which recording is to begin for the sensor.

  • config (Optional[Config]) – A reference/pointer to an existing beemon Config object, should one exist. If None is provided, a new beemon configuration Config object will be created for the beemon-config.ini in the repository’s root directory.

  • audio_sample_frequency_hz (Optional[int]) –

    Todo

    Docstring.

    If None is provided, this value will be determined by the Config object provided during instantiation. If no Config object was provided during instantiation, this value will default to the value specified in the beemon-config.ini found in the repository’s root directory.

  • sample_format (Optional[int]) –

    Todo

    Docstring.

    If None is provided, this value will be determined by the Config object provided during instantiation. If no Config object was provided during instantiation, this value will default to the value specified in the beemon-config.ini found in the repository’s root directory.

  • channel_count (Optional[int]) – The number of channels utilized by the microphone during recording. If the microphone is stereo (2-channel) then this class will attempt to convert to a mono signal (1-channel) via the make_stereo_file_mono() method.

  • gain (Optional[int]) –

    Todo

    Docstring.

    If None is provided, this value will be determined by the Config object provided during instantiation. If no Config object was provided during instantiation, this value will default to the value specified in the beemon-config.ini found in the repository’s root directory.

static make_stereo_file_mono(out_path: str)[source]

If the mic must record a stereo file with redundant channels, this method will create a new file from just the left channel, and then overwrite the original file with the mono file before uploading it to the server.

record(filename: str, uploader_output_dir: str) Dict[str, int][source]

Instructs ecasound to record in a sub-process for self.capture_duration_seconds to a temporary file in /tmp. Then this method moves the temporary file to the upload directory (via subprocess) when the recording subprocess has finished.

Parameters:
  • filename (str) – What the file name of the recorded measurement should be. For instance: "rpi4-11@2021-11-27@19-30-00".

  • uploader_output_dir (str) – The local output directory which the uploader.Uploader multiprocessing.Process is watching for file changes. The final (and renamed) recorded file should be output to this directory. For example: "home/bee/bee_tmp/video/2021-11-27/".

Returns:

Dictionary to upload to thingsboard, {‘Audio’: 0} if there are no errors and {‘Audio’: -1} if there is an error.

Return type:

Dict[str, Optional[int]]

static set_mic_gain(gain: int)[source]

Set the mic gain to something other than it’s default level.

start() None[source]

Prints the optionally overridden configuration values for the audio device to the console for debugging purposes.

The Video Class:

class sensor.Video(recording_start_time_queue: Queue, config: Config | None = None, fps: int | None = 30, resolution_x: int | None = 640, resolution_y: int | None = 480, flip_video: bool | None = False, still_frame: bool | None = False)[source]
__init__(recording_start_time_queue: Queue, config: Config | None = None, fps: int | None = 30, resolution_x: int | None = 640, resolution_y: int | None = 480, flip_video: bool | None = False, still_frame: bool | None = False)[source]
Parameters:
  • recording_start_time_queue (Queue) – A reference/pointer to the Queue instance that houses the datetime’s at which recording is to begin for the sensor.

  • config (Optional[Config]) – A reference/pointer to an existing beemon Config object, should one exist. If None is provided, a new beemon configuration Config object will be created for the beemon-config.ini in the repository’s root directory.

  • fps (Optional[int]) –

    Todo

    Docstring.

    If None is provided, this value will be determined by the Config object provided during instantiation. If no Config object was provided during instantiation, this value will default to the value specified in the beemon-config.ini found in the repository’s root directory.

  • resolution_x (Optional[int]) –

    Todo

    Docstring.

    If None is provided, this value will be determined by the Config object provided during instantiation. If no Config object was provided during instantiation, this value will default to the value specified in the beemon-config.ini found in the repository’s root directory.

  • resolution_y (Optional[int]) –

    Todo

    Docstring.

    If None is provided, this value will be determined by the Config object provided during instantiation. If no Config object was provided during instantiation, this value will default to the value specified in the beemon-config.ini found in the repository’s root directory.

  • flip_video (Optional[int]) –

    Todo

    Docstring.

    If None is provided, this value will be determined by the Config object provided during instantiation. If no Config object was provided during instantiation, this value will default to the value specified in the beemon-config.ini found in the repository’s root directory.

  • still_frame (Optional[bool]) – Whether or not to take images instead of a video. If True, then a single frame is captured instead of a video. The frames_per_second setting is ignored when capturing a still frame and the flip_video setting will flip both videos and still frame recordings. If False a video will be captured instead. If None is provided, this value will be determined by the Config object provided during instantiation. If no Config object was provided during instantiation, this value will default to the value specified in the beemon-config.ini found in the repository’s root directory.

record(filename: str, uploader_output_dir: str) Dict[str, Any][source]
Presumably opens a filestream-like buffer (via the picamera2.Picamera2 context manager) and records a

video performing an IO-blocking operation for the duration set with self.capture_duration_seconds.

The video initially records to a tmp file in the /tmp directory. The file is then moved to uploader_output_dir when the recoding is complete.

If still_frame in the beemon-config.ini is set to True, then a single frame is captured instead of a

video. The frames_per_second setting is ignored when capturing a still frame and the flip_video setting will flip both videos and still frame recordings.

Parameters:
  • filename (str) – What the file name of the recorded measurement should be. For instance: "rpi4-11@2021-11-27@19-30-00".

  • uploader_output_dir (str) – The local output directory which the uploader.Uploader multiprocessing.Process is watching for file changes. The final (and renamed) recorded file should be output to this directory. For example: "home/bee/bee_tmp/video/2021-11-27/".

start() None[source]

Prints the optionally overridden configuration values for the video camera device to the console for debugging purposes.

The Temp Class:

class sensor.Temp(recording_start_time_queue: Queue, config: Config | None = None)[source]
__init__(recording_start_time_queue: Queue, config: Config | None = None)[source]
Parameters:
  • recording_start_time_queue (Queue) – A reference/pointer to the multiprocessing.Queue instance that houses the datetime.datetime’s at which recording is to begin for the sensor.

  • config (Optional[Config]) – A reference/pointer to an existing beemon Config object, should one exist. If None is provided, a new beemon configuration Config object will be created for the beemon-config.ini in the repository’s root directory.

record(filename: str, uploader_output_dir: str) Dict[str, float][source]

Utilizes the Adafruit_DHT library to take a temperature and humidity reading from the sensor, appending it to a new CSV file daily. This method then copies the daily record of temperature and humidity readings into the upload directory, and copies an additional version to the sensor aggregation directory.

This sensor writes directly to the uploader_output_dir; it does NOT write to the /tmp directory. It writes to the csv file via the src.utils.filehandlingutils.write_sensor_csv_file() method. Logic for determining the final sensor recording of the day (via a .final file extension) is found there.

Returns:

A dictionary that contains each type of reading (str) and their measurements (float) A humidity reading of 2 would be returned as {“Humidity”: 2}

Return type:

Dict[str, float]

start() None[source]

This method is called by the orchestrator.Orchestrator class (specifically in the orchestrator.Orchestrator.start_command() method) when a "start <sensor>" command is sent to the server.Server process. The parent Sensor class allows concrete subclasses to optionally override this method in order to perform potentially long-running hardware/firmware sensor initialization routines. Concrete subclasses are expected to call the parent classes’ method first.

Notes

The abstract version of this method is intended to contain operations common to all concrete subclass Sensor objects during initialization. Currently, there are no common initialization operations besides logging.

The AirQuality Class:

class sensor.AirQuality(recording_start_time_queue: Queue, config: Config | None = None)[source]
__init__(recording_start_time_queue: Queue, config: Config | None = None)[source]
Parameters:
  • recording_start_time_queue (Queue) – A reference/pointer to the multiprocessing.Queue instance that houses the datetime.datetime’s at which recording is to begin for the sensor.

  • config (Optional[Config]) – A reference/pointer to an existing beemon Config object, should one exist. If None is provided, a new beemon configuration Config object will be created for the beemon-config.ini in the repository’s root directory.

_get_device_path() str[source]

Dynamically obtains the device path of the air quality sensor and returns it. Ex. “/dev/tty/USB0” on success and None on failure.

Returns:

The device path on success, and the empty string on failure

record(filename: str, uploader_output_dir: str) Dict[str, float][source]

Utilizes the sds011 library to take a air quality reading from the sensor, appending it to a new CSV file daily. This method then copies the daily record of air quality readings into the upload directory, and copies an additional version to the sensor aggregation directory.

This sensor writes directly to the uploader_output_dir; it does NOT write to the /tmp directory. It writes to the csv file via the src.utils.filehandlingutils.write_sensor_csv_file() method. Logic for determining the final sensor recording of the day (via a .final file extension) is found there.

See also

The SDS011 sensor’s datasheet. The definitions of pm25 and pm10.

Returns:

A dictionary that contains each type of reading (str) and their measurements (float) A reading would be returned as {“pm25”: 1.6, “pm10”: 2.0}

Return type:

Dict[str, float]

start() None[source]

This method is called by the orchestrator.Orchestrator class (specifically in the orchestrator.Orchestrator.start_command() method) when a "start <sensor>" command is sent to the server.Server process. The parent Sensor class allows concrete subclasses to optionally override this method in order to perform potentially long-running hardware/firmware sensor initialization routines. Concrete subclasses are expected to call the parent classes’ method first.

Notes

The abstract version of this method is intended to contain operations common to all concrete subclass Sensor objects during initialization. Currently, there are no common initialization operations besides logging.

The Scale Class:

class sensor.Scale(recording_start_time_queue: Queue, config: Config | None = None)[source]
__init__(recording_start_time_queue: Queue, config: Config | None = None)[source]
Parameters:
  • recording_start_time_queue (Queue) – A reference/pointer to the multiprocessing.Queue instance that houses the datetime.datetime’s at which recording is to begin for the sensor.

  • config (Optional[Config]) – A reference/pointer to an existing beemon Config object, should one exist. If None is provided, a new beemon configuration Config object will be created for the beemon-config.ini in the repository’s root directory.

record(filename: str, uploader_output_dir: str) Dict[str, Any][source]

Utilizes the hx711_multi library to take a weight reading from the scale, appending it to a new CSV file daily. This method then copies the daily record of weight readings into the upload directory, and copies an additional version to the sensor aggregation directory.

This sensor writes directly to the uploader_output_dir; it does NOT write to the /tmp directory. It writes to the csv file via the src.utils.filehandlingutils.write_sensor_csv_file() method. Logic for determining the final sensor recording of the day (via a .final file extension) is found there.

Returns:

A dictionary that contains a “Scale” reading (str) and the weight measured (float) in kilograms (kg). A weight reading of 50kg would be returned as {“Scale”: 50}

Return type:

Dict[str, Any]

start() None[source]

This method is called by the orchestrator.Orchestrator class (specifically in the orchestrator.Orchestrator.start_command() method) when a "start <sensor>" command is sent to the server.Server process. The parent Sensor class allows concrete subclasses to optionally override this method in order to perform potentially long-running hardware/firmware sensor initialization routines. Concrete subclasses are expected to call the parent classes’ method first.

Notes

The abstract version of this method is intended to contain operations common to all concrete subclass Sensor objects during initialization. Currently, there are no common initialization operations besides logging.

The Cpu Class:

class sensor.Cpu(recording_start_time_queue: Queue, config: Config | None = None)[source]
__init__(recording_start_time_queue: Queue, config: Config | None = None)[source]
Parameters:
  • recording_start_time_queue (Queue) – A reference/pointer to the multiprocessing.Queue instance that houses the datetime.datetime’s at which recording is to begin for the sensor.

  • config (Optional[Config]) – A reference/pointer to an existing beemon Config object, should one exist. If None is provided, a new beemon configuration Config object will be created for the beemon-config.ini in the repository’s root directory.

record(filename: str, uploader_output_dir: str) Dict[str, float][source]

This sensor writes directly to the uploader_output_dir; it does NOT write to the /tmp directory. It writes to the csv file via the src.utils.filehandlingutils.write_sensor_csv_file() method. Logic for determining the final sensor recording of the day (via a .final file extension) is found there.

Returns:

A dictionary that contains each type of reading (str) and their measurements (float) A voltage reading of 2 volts and temp of 80 Fahrenheit would be returned as {"Voltage": 2, "Cpu": 80}.

Return type:

Dict[str, float]

start() None[source]

This method is called by the orchestrator.Orchestrator class (specifically in the orchestrator.Orchestrator.start_command() method) when a "start <sensor>" command is sent to the server.Server process. The parent Sensor class allows concrete subclasses to optionally override this method in order to perform potentially long-running hardware/firmware sensor initialization routines. Concrete subclasses are expected to call the parent classes’ method first.

Notes

The abstract version of this method is intended to contain operations common to all concrete subclass Sensor objects during initialization. Currently, there are no common initialization operations besides logging.