How measuring power can prevent a flooding in your home.

//How measuring power can prevent a flooding in your home.

We all know that measuring power leads to a reduction of power usage, a lower electricity bill and reduced greenhouse gas emissions. Besides these obvious advantages, power measurement can be used to detect states of your house. A lot of devices are not smart and don’t provide any interface to the outside world. For example a water pump: the pump has a reservoir of water and measures the pressure in the reservoir, when you open the tap, the pressure drops and the pump kicks in. The pump does this all by itself and does not have an ethernet or serial interface to expose when it is pumping and for how long. In this blog I will try to convince you that we can figure out the state of the pump and act on this data.

In my home I have the following water setup: the rain water from the roof is captured in the gutters and stored in a well, a pump feeds the rainwater to the toilets, washing machine and the garden hose. The tap for the garden hose is located in the garage. Now comes the tricky part: during the winter it can freeze in the garage, the water in the pipes in the garage can freeze making the pipes burst. If the water keeps running, this might flood the garage and the rest of the house. So I definitely want a way to detect this situation and stop the flood.

Like all good problems there are multiple solution. What comes to mind as a first idea was placing humidity sensors in the garage to detect a flood. This can however trigger false positives, for example when it is raining outside and the car is dripping water on the garage floor. A second and more profound issue is that the sensor does not give any information about the cause of the problem: should we shut down the pump each time we detect a possible flood ? A second solution could be placing a flow sensor after the pump, these sensors basically rotate when water passes through the pipe and give a pulse when a certain volume has passed through the pipe. This is a possible solution for our problem since we can now detect how much water is flowing and for how long the water was flowing. If we detect that water is flowing continuously for a number of minutes we can alert the user, or even act directly and shut down the pump. This solution can be implemented using an input module, by configuring it as a pulse counter. Although this solution might work for you, I found a flow meter pretty expensive and difficult to install myself, since I’m not a plumber.

That brings me to my current solution for the flood problem: by measuring the power consumed by the pump I can check when the pump is running and for how long it has been running. We basically get the same type of data as we get from the flow meter. This data can be used to detect normal water usage or abnormal behaviour (like in case of a flooding).

The OpenMotics home automation system is extensible in various ways: since the hardware and software are both open source, it is possible to alter the hardware design, firwmare or software to your needs. However for the system I described above this is not necessary. The OpenMotics gateway can be extended without changing the software by the means of a plugin. The plugin code is executed directly on the Gateway, so no additional hardware is required. The plugin has direct access to all controls and metrics exposed by OpenMotics. Some examples: it can get the temperature, brightness and humidity from the sensors, read power metrics such as voltage, current and power from the power modules and interact with the system by driving dimmers and outputs. The plugin can also define a callback that is called when the state of an output or input has changed.

My setup is pretty simple: the power line connected to the water pump is switched by an Output Module, and the power line has a power clamp connected to a Power Module measures the current flowing to the water pump. This enables switching the pump on or off based on the estimated water flow. The plugin should work as follows: if the pump is running for more than 5 minutes I want to receive an email, if I don’t acknowledge the high water usage within 5 minutes the pump will be shut off automatically. The email notification makes sure that I’m aware of the problem and that I can stop the automatic action if the system gives a false positive, for example when I’m filling the swimming pool for the kids.

Although this is not standard OpenMotics functionality, it should be easy to create a plugin for this. The first thing we need are the configuration bits of the plugin. The thing we have to configure:

  • The output id for the pump.
  • The power id for the pump.
  • The power (in Watts) used when the pump is pumping.
  • An email address to send the warning to.

In code this becomes:


configuration_fields = 
[ { 'name' : 'output_id', 'type' : 'int', 'description': 'The output id for the pump.' }, { 'name' : 'power_id', 'type' : 'int', 'description': 'The power id for the pump.' }, { 'name' : 'watts', 'type' : 'int', 'description': 'The average power usage of the pump when pumping.' }, { 'name' : 'email', 'type' : 'str', 'description': 'Email address for sending the notification.' } ]

Ok, now we can get started. We will use the following api functions:

  • get_total_energy() : get the total energy used from all power measurements, from which we will get the energy usage of the pump.
  • set_output(output_id, on) : to turn the pump output on or off.

We will use the following workflow:

  • Every minute we get the total energy and calculate the difference with the previous minute. This gives us the energy usage for the last minute.
  • We check whether the energy usage for the last minute is equal or larger than the power used by the pump (provided in the configuration). If this is the case, we mark this minute as a minute where the pump was running.
  • We keep a list of the last 10 minutes and for each minute we have a boolean indicating whether the pump was running or not.
  • If the last 5 booleans in the list are true, we send an email. If all 10 booleans are true and the user did not signal a false positive, we shut off the pump.
  • The pump remains off until the user asks for a reset.

For this we need:

  • A background worker (running every minute).
  • A means to send an email.
  • An api call to signal a false positive.
  • An api call to reset the pump.

Luckily the plugin mechanism provides creating background threads and api calls:

  • The @background_task decorator starts the decorated function when the plugin starts.
  • The @om_expose decorator exposes a function as an api call.

 


from plugins.base import om_expose, input_status, output_status, OMPluginBase
from datetime import datetime
import simplejson as json
import smtplib
 
class Pumpy(OMPluginBase):
    """ Plugin to prevent flooding. """
 
    name = 'Pumpy'
    version = '1.0.0'
    interfaces = [ ]
 
    def __init__(self, webinterface, logger):
        super(Pumpy, self).__init__(webinterface, logger)
        # The config below is a dummy, we will read this from a file later on.
        self.__config = { 'output_id' : 1, 'power_id' : 2, 'watts' : 200, 'email' : 'test@exmample.com' }
        self.__pump_energy_in_one_minute = self.__config['watts'] / 60.0 # Energy used by the pump in 1 minute (in Wh)

        self.__last_energy = None
        self.__window = [] # The list containing whether the pump was on the last 10 minutes

        self.logger("Started Pumpy plugin")

    @background_task
    def run(self):
        while True:
            self.__do_check()
            time.sleep(60) # Sleep one minute before checking again.

    def __do_check(self):
        watts = self.__get_total_energy()
        if self.__last_energy == None:
            # The first time we only set the last_energy value.
            self.__last_energy = watts
        else:
            # The next times we calculate the difference: the watts
            diff = (watts - self.__last_energy) * 1000 # Convert from kWh to Wh
            on = (diff >= self.__pump_energy_in_one_minute)
            if on:
                self.logger("Pump was running during the last minute")
            
            self.__window = self.__window[-9:] # Keep the last 9 'on' values of the window
            self.__window.append(on)           # Add the last 'on' value to the window

            running_for_10_mintues = True
            for on in self.__window:
                running_for_10_mintues = running_for_10_mintues and on

            if running_for_10_mintues:
                self.logger("Pump was running for 10 minutes")
                self.__pump_alert_triggered()

            self.__last_energy = watts

    def __pump_alert_triggered(self):
        # This method is called when the pump is running for 10 minutes.
        self.__stop_pump()

        # The smtp configuration below could be stored in the configuration.
        try:
            smtp = smtplib.SMTP('localhost')
            smtp.sendmail('power@localhost', [ self.__config['email'] ], 'Your pump was shut down because of high power usage !')
        except SMTPException as e:
            self.logger("Failed to send email: %s" % e)

    def __get_total__energy(self):
        energy = self.webinterface.get_total_energy() # Returns dict of "power_id" to [day, night] array.
        energy_values = energy[str(self.__config['power_id'])]
        return energy_values[0] + energy_values[1] # Returns the sum of the night and day values.

    def __stop_pump(self):
        self.webinterface.set_output(self.__config['output_id'], False)

    def __start_pump(self):
        self.webinterface.set_output(self.__config['output_id'], True)

The above code implements the core functionality of the plugin. However, it is missing some features:
1) The configuration is hard coded.
2) There is no external interface to restart the pump.
3) It would be really nice if we could set a timer to disable the plugin (for instance when we are filling the pool).

The plugin system provides some functionality to implement these. For 1) we can implement the config interface, the gateway webinterface will automatically generate a configuration webpage. 2) and 3) can be implemented by creating a method with a @om_exposed decorator.

The @om_exposed decorator exposes the method on the following url: https:///plugins/Pumpy/. The decorator takes 1 argument: if auth is True a user must be logged in to perform the action (this is the default), if auth is False the action can be performed without a valid authentication. In this case we want to make sure that only a valid user can perform the action, so auth should be set to True (we don’t have to set this explicitly, because this is the default).

Implementing the config interface requires us to add 3 methods:
– ‘get_config_description’ for retrieving the configuration fields.
– ‘get_config’ for retrieving the configuration values.
– ‘set_config’ for setting the configuration values.

We will extend the code as follows:



class Pumpy(OMPluginBase):
    """ Plugin to prevent flooding. """
 
    name = 'Pumpy'
    version = '1.0.0'
    interfaces = [ ('config', '1.0') ]

    config_descr = [
        { 'name' : 'output_id',  'type' : 'int', 'description': 'The output id for the pump.'  },
        { 'name' : 'power_id', 'type' : 'int', 'description': 'The power id for the pump.' },
        { 'name' : 'watts', 'type' : 'int', 'description': 'The average power used by the pump, when running (in watts).' },
        { 'name' : 'email', 'type' : 'str', 'description': 'The email address to send the shutdown notification to.' },
    ]

    def __init__(self, webinterface, logger):
        ... # Code from above

        self.__config = self.read_config(None)
        self.__config_checker = PluginConfigChecker(Pumpy.config_descr)

    @om_expose
    def get_config_description(self):
        return json.dumps(Pumpy.config_descr)
 
    @om_expose
    def get_config(self):
        return json.dumps(self.__config)
 
    @om_expose
    def set_config(self, config):
        config = json.loads(config)
        self.__config_checker.check_config(config)
        self.write_config(config)
        self.__config = config
        return json.dumps({ 'success' : True })

That’s all we have to do to get the following automatically generated configuration page.

Screen Shot 2014-12-06 at 12.37.50

To finish it off we will create the following url to restart the pump: https:///plugins/Pumpy/reset. The code is very simple:


class Pumpy(OMPluginBase):
    ...
    @om_expose
    def reset(self):
        self.__window = []
        self.__start_pump()
        return json.dumps({ 'success' : True })

Setting a timer to disable the plugin can be implemented just as easy, but I will leave that to the reader. You can find the full source code here: https://github.com/openmotics/plugins.

To install the plugin we have to do 2 things: create a tgz containing the code, and calculate the md5 sum of the package. Both the package and md5 sum have to be entered when installing the plugin on the gateway ui. The plugin code should be put in main.py and __init__.py should be an empty file. The package and md5 sum can be created using the following command (in Linux):


tar czf plugin.tgz main.py __init__.py
md5sum plugin.tgz

A screenshot before …

Screen Shot 2014-12-01 at 20.56.54

and after the installation:

Installed

Now we can rest asured that the house will not be flooded when we are away. In this blog post I tried to show how we can use power measurements and a plugin to keep track of the water usage in the house. All comments are welcome !

By | 2017-11-17T16:26:33+00:00 January 1st, 2014|Categories: Uncategorized|0 Comments

Share This Story, Choose Your Platform!

About the Author: