Index
Sensor modules for the plant monitoring system.
This package defines the abstract base class that all sensor implementations must follow, and provides the dynamic loading mechanism used by the controller.
Writing a custom sensor module
- Create a new
.pyfile in this package (e.g.my_sensor.py). - Define a class that inherits from
Sensorand one of the bus interface mixins fromplant_controller.com_bus(I2CInterfaceorMODBUSInterface). - Implement the
readmethod (async) to take a measurement and pass it toself.db_save_function. - Optionally override
get_capabilitiesif the default implementation is not adequate. -
Reference the module and class in a plant's JSON config file::
{ "sensors": { "my_parameter": { "module": "my_sensor", "class": "MySensorClass", "kwargs": { ... } } } }
The kwargs dict is passed directly to the sensor's __init__
as keyword arguments (in addition to parameter, bus, and
db_save_function which are always provided).
Example minimal sensor::
from plant_controller.sensors import Sensor
from plant_controller.com_bus import I2CInterface, BlinkaI2CBus
from plant_controller.datapoint import Confidence, Measurement
class MySensor(Sensor, I2CInterface):
def __init__(self, parameter, bus, db_save_function, **kwargs):
super().__init__(
parameter=parameter,
bus=bus,
confidence=Confidence(interval=0.5, level=0.95),
units="my_unit",
time_between_reads=30,
db_save_function=db_save_function,
)
async def read(self):
value = ... # read from hardware
await self.db_save_function(
Measurement(
parameter=self.parameter,
value=value,
confidence=self.confidence,
units=self.units,
)
)
Sensor
¶
Bases: ABC
Abstract base class for all sensor implementations.
Subclasses must also inherit from a bus interface mixin
(I2CInterface or MODBUSInterface) so that the dynamic loader
can determine which bus to pass during initialization.
Attributes:
| Name | Type | Description |
|---|---|---|
parameter |
Name of the physical parameter being measured. |
|
bus |
The communication bus instance assigned to this sensor. |
|
confidence |
Measurement confidence/uncertainty specification. |
|
units |
Unit string for the measured values (e.g. "°C", "%"). |
|
time_between_reads |
Interval in seconds between consecutive reads. |
|
db_save_function |
Callable that persists Datapoint(s). |
Source code in pt/controller_3/src/plant_controller/sensors/__init__.py
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | |
__init__(parameter, bus, confidence, units, time_between_reads, db_save_function, config_save_function=None)
¶
Initialize the sensor.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
parameter
|
str
|
Name of the measured parameter (e.g. 'temperature'). |
required |
bus
|
Bus
|
Communication bus instance matching this sensor's bus_type(). |
required |
confidence
|
Confidence
|
Measurement confidence interval specification. |
required |
units
|
str
|
Unit of measurement (e.g. '°C', '%'). |
required |
time_between_reads
|
float
|
Seconds between automatic readings. |
required |
db_save_function
|
Callable[[Datapoint | list[Datapoint]], None]
|
Function to persist measurements to the DB. |
required |
config_save_function
|
Callable | None
|
Callable for saving the passed arguments in the sensors config. |
None
|
Source code in pt/controller_3/src/plant_controller/sensors/__init__.py
read()
abstractmethod
async
¶
Take a measurement and save it via db_save_function.
Implementations should read from the hardware and call
await self.db_save_function(datapoint) with a Measurement
or list of Measurements.
Source code in pt/controller_3/src/plant_controller/sensors/__init__.py
reading_loop()
async
¶
Run an infinite loop that calls read() at the configured interval.
This method is started as a task by the Unit's sensing task group. Override only if non-uniform timing is needed.
Source code in pt/controller_3/src/plant_controller/sensors/__init__.py
get_capabilities()
¶
Return a dict describing this sensor's capabilities.
The default implementation returns a single entry keyed by
self.parameter. Override this method if the sensor produces
multiple parameters (see GreenhouseAS7341 for an example).
Returns:
| Type | Description |
|---|---|
|
Dict mapping parameter names to capability info dicts. |
Source code in pt/controller_3/src/plant_controller/sensors/__init__.py
init_sensor(module_name, class_name, parameter, busses, db_save_function, config_save_function=None, sensor_kwargs=None)
¶
Dynamically load and instantiate a sensor from a submodule.
This function imports plant_controller.sensors.<module_name>,
retrieves the class <class_name> from it, and instantiates it.
The correct bus is selected automatically via the class's
bus_type() static method.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
module_name
|
str
|
Name of the Python module inside this package (e.g. 'stemma', 'sht45'). |
required |
class_name
|
str
|
Name of the Sensor subclass within that module. |
required |
parameter
|
str
|
The physical parameter name for the sensor. |
required |
busses
|
dict[str, Bus]
|
Dict mapping bus type strings to Bus instances. |
required |
db_save_function
|
Callable[[Datapoint | list[Datapoint]], None]
|
Callable for persisting datapoints. |
required |
config_save_function
|
Callable | None
|
Callable for saving the passed arguments in the sensors config. |
None
|
sensor_kwargs
|
dict[Any] | None
|
Extra keyword arguments forwarded to the sensor's
|
None
|
Returns:
| Type | Description |
|---|---|
Sensor
|
An initialized Sensor instance ready for use. |
Raises:
| Type | Description |
|---|---|
ModuleNotFoundError
|
If the module cannot be imported. |
AttributeError
|
If the class doesn't exist in the module. |