From c8a99994ec13cc9a43aaa67ac3e587c546df5f4c Mon Sep 17 00:00:00 2001 From: Carlos Gustavo Sarmiento Date: Tue, 18 Oct 2022 22:13:22 -0500 Subject: [PATCH] Documentation for Atlas Scientific Peristaltic Pump (#2115) --- components/ezo_pmp.rst | 571 ++++++++++++++++++++++++++++++++++ components/images/ezo-pmp.jpg | Bin 0 -> 8384 bytes images/ezo-pmp.jpg | Bin 0 -> 8384 bytes 3 files changed, 571 insertions(+) create mode 100644 components/ezo_pmp.rst create mode 100644 components/images/ezo-pmp.jpg create mode 100644 images/ezo-pmp.jpg diff --git a/components/ezo_pmp.rst b/components/ezo_pmp.rst new file mode 100644 index 000000000..d373e7e71 --- /dev/null +++ b/components/ezo_pmp.rst @@ -0,0 +1,571 @@ +Atlas Scientific Peristaltic Pump +================================= + +.. seo:: + :description: Instructions for setting up an Atlas Scientific Peristaltic Pump in ESPHome + :image: ezo-pmp.jpg + :keywords: pump, peristaltic, atlas, ezo + +The ``ezo_pmp`` component allows you to use an Atlas Scientific Peristaltic Pump with ESPHome. +Both the EZO-PMP (`datasheet `__) +and EZO-PMP-L (`datasheet `__) are supported. +The :ref:`I²C Bus ` is required to be set up in your configuration for this sensor to work. + +.. note:: + + This component will not be directly controllable in the Home Assistant front-end automatically because + Home Assistant doesn't have support for pumps. In order to control the pump from the frontend you will need to use + templates to offer access to the actions you need. Please see :ref:`ezo-pmp-ha-config`. + +.. figure:: images/ezo-pmp.jpg + :align: center + :width: 80.0% + +.. code-block:: yaml + + ezo_pmp: + id: ezo_pmp + address: 103 # Default Address for the EZO-PMP. + update_interval: 60s + +Configuration variables: + +- **id** (**Required**, :ref:`config-id`): Specify the ID of the pump so that you can control it. +- **address** (*Optional*, int): Specify the I²C address of the sensor. Defaults to 103. +- **update_interval** (*Optional*, :ref:`config-time`): The interval to check the + sensor. Defaults to ``60s``. + +Sensors +----------------------------- + +Since the EZO-PMP offers a large number of sensors and each sensor needs to be polled individually, the code has been +optimized not to update a sensor that is not defined in the config. If you need very quick updates from the pump (under 3 seconds) +only enable the sensors that you actually need. + +.. _ezo_pmp-current_volume_dosed_sensor: + +``current_volume_dosed`` +----------------------------- + +This sensor indicates the volume (in milliliters) that has been dosed in the currently running or last ran dosing command. + +.. code-block:: yaml + + sensor: + - platform: ezo_pmp + current_volume_dosed: + id: current_volume_dosed + name: Current Volume Dosed + +Configuration variables: + +- **id** (*Optional*, :ref:`config-id`): Set the ID of this sensor for use in lambdas. +- All other options from :ref:`Sensor `. + +.. _ezo_pmp-total_volume_dosed_sensor: + +``total_volume_dosed`` +----------------------------- + +This sensor indicates total volume (in milliliters) that has been dosed since the last time the pump was turned on. Could be a +negative number if the pump has been run in reverse. + +.. code-block:: yaml + + sensor: + - platform: ezo_pmp + total_volume_dosed: + id: total_volume_dosed + name: Total Volume Dosed + +Configuration variables: + +- **id** (*Optional*, :ref:`config-id`): Set the ID of this sensor for use in lambdas. +- All other options from :ref:`Sensor `. + +.. _ezo_pmp-absolute_total_volume_dosed_sensor: + +``absolute_total_volume_dosed`` +------------------------------- + +This sensor indicates the absolute total volume (in milliliters) that has been dosed since the last time the pump was turned on. + +.. code-block:: yaml + + sensor: + - platform: ezo_pmp + absolute_total_volume_dosed: + id: absolute_total_volume_dosed + name: Absolute Total Volume Dosed + +Configuration variables: + +- **id** (*Optional*, :ref:`config-id`): Set the ID of this sensor for use in lambdas. +- All other options from :ref:`Sensor `. + +.. _ezo_pmp-last_volume_requested_sensor: + +``last_volume_requested`` +----------------------------- + +Indicates the total volume (in milliliters) that is being dosed (or was if the pump has already finished) by a dose Action. When +using dosing actions that have a duration, the sensor will have the right calculation for total volume. + +.. code-block:: yaml + + sensor: + - platform: ezo_pmp + last_volume_requested: + id: last_volume_requested + name: Last Volume Requested + +Configuration variables: + +- **id** (*Optional*, :ref:`config-id`): Set the ID of this sensor for use in lambdas. +- All other options from :ref:`Sensor `. + +.. _ezo_pmp-max_flow_rate_sensor: + +``max_flow_rate`` +----------------------------- + +The pump provides its own calculation of the maximum flow rate it can provide (in ml/minute). Dosing requests that exceed this rate +will fail. When using the :ref:`Dose Continuously ` Action, this is the volume the pump will +dose every minute. This value will get updated after the pump is calibrated (see :ref:`ezo_pmp-set_calibration_volume_action`). + +.. code-block:: yaml + + sensor: + - platform: ezo_pmp + max_flow_rate: + id: max_flow_rate + name: Max Flow Rate + +Configuration variables: + +- **id** (*Optional*, :ref:`config-id`): Set the ID of this sensor for use in lambdas. +- All other options from :ref:`Sensor `. + +.. _ezo_pmp-pump_voltage_sensor: + +``pump_voltage`` +----------------------------- + +The current voltage of the power supply that powers the pump. Not to be confused with the voltage that powers the electronics on the pump. + +.. code-block:: yaml + + sensor: + - platform: ezo_pmp + pump_voltage: + id: pump_voltage + name: Pump Voltage + +Configuration variables: + +- **id** (*Optional*, :ref:`config-id`): Set the ID of this sensor for use in lambdas. +- All other options from :ref:`Sensor `. + +Binary Sensors +----------------------------- + +.. _ezo_pmp-pump_state_binary_sensor: + +``pump_state`` +----------------------------- + +Indicates if the pump is currently running or not. + +.. code-block:: yaml + + binary_sensor: + - platform: ezo_pmp + pump_state: + id: pump_state + name: Pump State + +Configuration variables: + +- **id** (*Optional*, :ref:`config-id`): Set the ID of this sensor for use in lambdas. +- All other options from :ref:`Binary Sensor `. + +.. _ezo_pmp-is_paused_binary_sensor: + +``is_paused`` +----------------------------- + +Indicates if a dosing action is currently paused. + +.. code-block:: yaml + + binary_sensor: + - platform: ezo_pmp + is_paused: + id: is_paused + name: Is Paused + +Configuration variables: + +- **id** (*Optional*, :ref:`config-id`): Set the ID of this sensor for use in lambdas. +- All other options from :ref:`Binary Sensor `. + + +Text Sensors +----------------------------- + +.. _ezo_pmp-dosing_mode_text_sensor: + +``dosing_mode`` +----------------------------- + +Indicates the dosing mode the pump is currently running as. Can be any of `Volume`, `Volume/Time`, `Constant Flow Rate`, `Continuous`, `None`. + +.. code-block:: yaml + + text_sensor: + - platform: ezo_pmp + dosing_mode: + id: dosing_mode + name: Dosing Mode + +Configuration variables: + +- **id** (*Optional*, :ref:`config-id`): Set the ID of this sensor for use in lambdas. +- All other options from :ref:`Text Sensor `. + +.. _ezo_pmp-calibration_status_text_sensor: + +``calibration_status`` +----------------------------- + +Indicates calibration status of the pump. Can be any of `Fixed Volume`, `Volume/Time`, `Fixed Volume & Volume/Time` or `Uncalibrated`. + +.. code-block:: yaml + + text_sensor: + - platform: ezo_pmp + calibration_status: + id: calibration_status + name: Calibration Status + +Configuration variables: + +- **id** (*Optional*, :ref:`config-id`): Set the ID of this sensor for use in lambdas. +- All other options from :ref:`Text Sensor `. + +Actions +----------------------------- + +.. _ezo_pmp-dose_continuously_action: + +``ezo_pmp.dose_continuously`` Action +------------------------------------ + +Use this action in an :ref:`automations ` to have the peristaltic pump dose continuously +at the :ref:`Maximum Flow Rate `. The pump will automatically stop after 20 days +of running in continuous mode. + +.. code-block:: yaml + + on_...: + then: + - ezo_pmp.find: + id: ezo_pmp + +Configuration options: + +- **id** (**Required**, :ref:`config-id`): The ID of the pump. + + +.. _ezo_pmp-dose_volume_action: + +``ezo_pmp.dose_volume`` Action +------------------------------ + +Use this action in an :ref:`automations ` to have the peristaltic pump dose an specific volume (in milliliters) +at the :ref:`Maximum Flow Rate `. If the volume is negative the pump will run backwards. + +.. code-block:: yaml + + on_...: + then: + - ezo_pmp.dose_volume: + id: ezo_pmp + volume: 10 + + # Templated + - ezo_pmp.dose_volume: + id: ezo_pmp + volume: !lambda |- + return id(some_volume_sensor).state; + +Configuration options: + +- **id** (**Required**, :ref:`config-id`): The ID of the pump. +- **volume** (**Required**, float, :ref:`templatable `): The volume to dose in milliliters. If negative, pump will run in reverse. + +.. _ezo_pmp-dose_volume_over_time_action: + +``ezo_pmp.dose_volume_over_time`` Action +---------------------------------------- + +Use this action in an :ref:`automations ` to have the peristaltic pump dose an specific `volume` (in milliliters) +over the provided `duration` (in minutes). At the end of the time period the pump will have dosed the specified `volume`. +If the volume is negative the pump will run backwards. + +.. code-block:: yaml + + on_...: + then: + - ezo_pmp.dose_volume_over_time: + id: ezo_pmp + volume: 23.4 + duration: 2 + + # Templated + - ezo_pmp.dose_volume_over_time: + id: ezo_pmp + volume: !lambda |- + return id(some_volume_sensor).state; + duration: !lambda |- + return id(some_duration_sensor).state; + +Configuration options: + +- **id** (**Required**, :ref:`config-id`): The ID of the pump. +- **volume** (**Required**, float, :ref:`templatable `): The volume to dose in milliliters. If negative, pump will run in reverse. +- **duration** (**Required**, int, :ref:`templatable `): The time (in minutes) the pump will take to dose the volume requested. + + +.. _ezo_pmp-dose_with_constant_flow_rate_action: + +``ezo_pmp.dose_with_constant_flow_rate`` Action +----------------------------------------------- + +Use this action in an :ref:`automations ` to have the peristaltic pump dose an specific `volume` (in milliliters) every minute +for the provided `duration` (in minutes). At the end of the time period the pump will have dosed the specified `volume` times the `duration`. +If the volume is negative the pump will run backwards. + +.. code-block:: yaml + + on_...: + then: + - ezo_pmp.dose_with_constant_flow_rate: + id: ezo_pmp + volume_per_minute: 10.2 + duration: 2 + + # Templated + - ezo_pmp.dose_with_constant_flow_rate: + id: ezo_pmp + volume_per_minute: !lambda |- + return id(some_volume_sensor).state; + duration: !lambda |- + return id(some_duration_sensor).state; + +Configuration options: + +- **id** (**Required**, :ref:`config-id`): The ID of the pump. +- **volume_per_minute** (**Required**, float, :ref:`templatable `): The volume to dose in milliliters every minute. If negative, pump will run in reverse. +- **duration** (**Required**, int, :ref:`templatable `): The time (in minutes) the pump will dose the volume requested every minute. + +.. _ezo_pmp-pause_dosing_action: + +``ezo_pmp.pause_dosing`` Action +------------------------------- + +Use this action to pause a Dosing command that was previously issued. To determine if the dosing is paused or not, you can use the :ref:`Is Paused ` sensor. +If the pump is currently paused, issuing this action again will unpause it. + +.. code-block:: yaml + + on_...: + then: + - ezo_pmp.pause_dosing: + id: ezo_pmp + +Configuration options: + +- **id** (**Required**, :ref:`config-id`): The ID of the pump. + +.. _ezo_pmp-stop_dosing_action: + +``ezo_pmp.stop_dosing`` Action +------------------------------ + +Use this action to stop the current Dosing command. + +.. code-block:: yaml + + on_...: + then: + - ezo_pmp.stop_dosing: + id: ezo_pmp + +Configuration options: + +- **id** (**Required**, :ref:`config-id`): The ID of the pump. + +.. _ezo_pmp-clear_total_volume_dosed_action: + +``ezo_pmp.clear_total_volume_dosed`` Action +------------------------------------------- + +Clear the values of the :ref:`Current Volume Dosed `, :ref:`Total Volume Dosed ` +and :ref:`Absolute Total Volume Dosed ` sensors. + +.. code-block:: yaml + + on_...: + then: + - ezo_pmp.clear_total_volume_dosed: + id: ezo_pmp + +Configuration options: + +- **id** (**Required**, :ref:`config-id`): The ID of the pump. + +.. _ezo_pmp-set_calibration_volume_action: + +``ezo_pmp.set_calibration_volume`` Action +----------------------------------------- + +Use this action to calibrate the peristaltic pump. The EZO-PMP needs two forms of calibration: absolute volume and volume over time. You can check +the calibration status by using the :ref:`Calibration Status` sensor. For the procedure on calibrating the pump +check the datasheet. + +.. code-block:: yaml + + on_...: + then: + - ezo_pmp.set_calibration_volume: + id: ezo_pmp + volume: 10.01 + + # Templated + - ezo_pmp.set_calibration_volume: + id: ezo_pmp + volume: !lambda |- + return id(some_volume_sensor).state; + +Configuration options: + +- **id** (**Required**, :ref:`config-id`): The ID of the pump. +- **volume** (**Required**, float, :ref:`templatable `): The volume measured as part of the calibration process. + + +.. _ezo_pmp-clear_calibration_action: + +``ezo_pmp.clear_calibration`` Action +------------------------------------ + +Clear the calibration values stored in the pump. You can check the calibration status by using the +:ref:`Calibration Status` sensor. + +.. code-block:: yaml + + on_...: + then: + - ezo_pmp.clear_calibration: + id: ezo_pmp + +Configuration options: + +- **id** (**Required**, :ref:`config-id`): The ID of the pump. + +.. _ezo_pmp-find_action: + +``ezo_pmp.find`` Action +----------------------- + +Use this action to make the LED on the Pump control board to blink for a minute. The pump will not respond to any other action while the LED is blinking. + +.. code-block:: yaml + + on_...: + then: + - ezo_pmp.find: + id: ezo_pmp + +Configuration options: + +- **id** (**Required**, :ref:`config-id`): The ID of the pump. + +.. _ezo_pmp-change_i2c_address_action: + +``ezo_pmp.change_i2c_address`` Action +------------------------------------------- + +Changes the i2c address of the pump to the provided value. After the address is changed you must upload a new version of the ESPHome firmware with the updated I2C +address for the pump to work. + +.. code-block:: yaml + + on_...: + then: + - ezo_pmp.change_i2c_address: + id: ezo_pmp + address: 100 + +Configuration options: + +- **id** (**Required**, :ref:`config-id`): The ID of the pump. +- **address** (**Required**, int, :ref:`templatable `): The new I2C address for the pump. + +.. _ezo-pmp-ha-config: + +Home Assistant Configuration +---------------------------- + +In order to provide control of the pump from the home assistant frontend it is important to expose the actions +as components that have UI rendering. This could be done using templates in ESPHome. Here is an example using a +`Template Button` and `Template Number` to dose a certain volume over time. + +.. code-block:: yaml + + i2c: + + ezo_pmp: + id: ezo_pmp + + number: + - platform: template + id: volume + name: "Volume" + optimistic: true + min_value: 0 + max_value: 100 + step: 0.01 + - platform: template + id: duration + name: "Duration" + optimistic: true + min_value: 0 + max_value: 100 + step: 1 + + button: + - platform: template + name: "Dose Over Time" + on_press: + - ezo_pmp.dose_volume_over_time: + id: ezo_pump + volume: !lambda |- + return id(volume).state; + duration: !lambda |- + return id(duration).state; + +.. _ezo_pmp-lambda_calls: + +lambda calls +------------ + +From :ref:`lambdas `, you can also access the actions on the peristaltic pump to do some +advanced stuff (see the full API Reference for more info). The name of the functions is the same as the name +of the actions in YAML config. + +See Also +-------- + +- :apiref:`ezo_pmp/ezo_pmp.h` +- :ghedit:`Edit` diff --git a/components/images/ezo-pmp.jpg b/components/images/ezo-pmp.jpg new file mode 100644 index 0000000000000000000000000000000000000000..de1f85ea0fc45ca8f6151676b6db6e91cfa620f6 GIT binary patch literal 8384 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 literal 0 HcmV?d00001 diff --git a/images/ezo-pmp.jpg b/images/ezo-pmp.jpg new file mode 100644 index 0000000000000000000000000000000000000000..de1f85ea0fc45ca8f6151676b6db6e91cfa620f6 GIT binary patch literal 8384 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 literal 0 HcmV?d00001