diff --git a/Dockerfile b/Dockerfile index b4d1a043..b9b7d065 100644 --- a/Dockerfile +++ b/Dockerfile @@ -33,6 +33,9 @@ RUN pip install --no-cache-dir poetry && \ FROM base +# Install gcc so packages installed durring runtime may be build +RUN apt-get update && apt-get install -y gcc && gcc --version + RUN useradd -m -s /bin/bash mqtt_io USER mqtt_io WORKDIR /home/mqtt_io @@ -43,5 +46,6 @@ RUN venv/bin/python -m pip install --no-cache-dir --upgrade pip RUN venv/bin/pip install --no-cache-dir -r requirements.txt COPY --chown=mqtt_io mqtt_io mqtt_io +RUN gcc --version CMD [ "venv/bin/python", "-m", "mqtt_io", "/config.yml" ] diff --git a/README.md b/README.md index b092319e..81f2dcbc 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,7 @@ Hardware support is provided by specific GPIO, Sensor and Stream modules. It's e - ADXl345 3-axis accelerometer up to ±16g (`adxl345`) - PMS5003 particulate sensor (`pms5003`) - SHT40/SHT41/SHT45 temperature and humidity sensors (`sht4x`) + - YF-S201 flow rate sensor (`yfs201`) ### Streams diff --git a/mqtt_io/modules/sensor/bmp085.py b/mqtt_io/modules/sensor/bmp085.py index c8ba91ed..768f5791 100644 --- a/mqtt_io/modules/sensor/bmp085.py +++ b/mqtt_io/modules/sensor/bmp085.py @@ -36,7 +36,7 @@ class Sensor(GenericSensor): def setup_module(self) -> None: # pylint: disable=import-outside-toplevel,attribute-defined-outside-init # pylint: disable=import-error,no-member - from Adafruit_BMP.BMP085 import BMP085 + from Adafruit_BMP.BMP085 import BMP085 # type: ignore self.address: int = self.config["chip_addr"] self.bmp = BMP085(address=self.address) @@ -46,6 +46,6 @@ def get_value(self, sens_conf: ConfigType) -> SensorValueType: Get the temperature, humidity or pressure value from the sensor """ sens_type = sens_conf["type"] - data = DATA_READER[sens_type](self.bmp) + #error: Call to untyped function (unknown) in typed context + data = DATA_READER[sens_type](self.bmp) # type: ignore return cast(float, data) - diff --git a/mqtt_io/modules/sensor/dht22.py b/mqtt_io/modules/sensor/dht22.py index 48aaf3f5..49bfaba9 100644 --- a/mqtt_io/modules/sensor/dht22.py +++ b/mqtt_io/modules/sensor/dht22.py @@ -37,7 +37,7 @@ class Sensor(GenericSensor): def setup_module(self) -> None: # pylint: disable=import-outside-toplevel,import-error import adafruit_dht # type: ignore - from microcontroller import Pin + from microcontroller import Pin # type: ignore sensor_type: str = self.config["type"].lower() diff --git a/mqtt_io/modules/sensor/yfs201.py b/mqtt_io/modules/sensor/yfs201.py new file mode 100644 index 00000000..6fc3fcc0 --- /dev/null +++ b/mqtt_io/modules/sensor/yfs201.py @@ -0,0 +1,94 @@ +""" + +YF-S201 Flow Rate Sensor + +Example configuration: + +sensor_modules: + - name: yfs201 + module: yfs201 + +sensor_inputs: + - name: flow_rate1 + module: yfs201 + pin: 0 + digits: 0 + interval: 10 +""" + +from typing import Dict +from ...types import CerberusSchemaType, ConfigType, SensorValueType +from . import GenericSensor + +REQUIREMENTS = ("gpiozero",) + + +class YFS201: + """ + YF-S201 Flow Rate Sensor class + Multiple instances support multiple sensors on different pins + """ + + def __init__(self, gpiozero, name: str, pin: int) -> None: # type: ignore[no-untyped-def] + self.name = name + self.pin = gpiozero.DigitalInputDevice(pin) + self.pin.when_activated = self.count_pulse + self.count = 0 + + def count_pulse(self) -> None: + """Increment pulse count.""" + self.count += 1 + + def reset_count(self) -> None: + """Reset pulse count.""" + self.count = 0 + + def flow_rate(self, sample_window: int) -> float: + """Return flow rate in liters per minute. + + From YF-S201 manual: + Pluse Characteristic:F=7Q(L/MIN). + 2L/MIN=16HZ 4L/MIN=32.5HZ 6L/MIN=49.3HZ 8L/MIN=65.5HZ 10L/MIN=82HZ + + Pulse frequency (Hz) / 7.0 = flow rate in L/min + + sample_window is in seconds, so hz is pulse_count / sample_window + """ + hertz = self.count / sample_window + return hertz / 7.0 + + def get_value(self, interval: int) -> float: + """Return flow rate in L/min over interval seconds and reset count.""" + flow_rate = self.flow_rate(interval) + self.reset_count() + return flow_rate + + +class Sensor(GenericSensor): + """ + YF-S201 Flow Rate Sensor + """ + + SENSOR_SCHEMA: CerberusSchemaType = { + "pin": dict( + type="integer", + required=True, + empty=False, + ) + } + + def setup_module(self) -> None: + # pylint: disable=import-outside-toplevel,import-error + import gpiozero # type: ignore + + self.gpiozero = gpiozero + self.sensors: Dict[str, YFS201] = {} + + def setup_sensor(self, sens_conf: ConfigType) -> None: + sensor = YFS201( + gpiozero=self.gpiozero, name=sens_conf["name"], pin=sens_conf["pin"] + ) + self.sensors[sensor.name] = sensor + + def get_value(self, sens_conf: ConfigType) -> SensorValueType: + return self.sensors[sens_conf["name"]].get_value(sens_conf["interval"])