Motion Detection with XBee

How to cub-class a DIA driver.

This page covers how to leverage an existing DIA driver without modifying the original file.

Motion detector example

In this example, I wish to connect a common Motion-Detector/Glass-Break device with relay contacts ('dry-contacts') via ZigBee to Device Cloud and DIA.

The motion-detector sensor requires 12vdc to run, so I used an older XBee DIO Adapter which runs on 9-30vdc. With a 12vdc supply, the entire setup can be powered including directly driving 12vdc relays and 12vdc low-voltage garden-style lighting.

Technically, the existing DIA DIO driver could be used to read the sensor contacts and drive the lamp output, however the motion detection event is very short-lived. It may be true for only a few seconds, and turning on a lamp for a few seconds is not what is required. Instead, a timer is required to turn the lamp on for a few minutes. In theory one could use the DIA transforms to do this, but at present they are stateless, so cannot function as timers.

So one has two choices:

In this demo example I chose to subclass. The new driver allows the existing DIO driver to manage incoming data and update the standard DIO data channels. Then it reads the input channels and adjusts the output channels as required.

Ideally, the time the lamp is held on should be a setting, which complicates the subclass slightly. That is left for a future TODO - including the support for the glass-break input and the 12vdc relay to drive brighter AC lights. If all goes well, my final design will have a programmable XBee to manage the light and relay control locally within the XBee adapter itself.

Hardware example

Wiring the system is quite strait forward. The items required:

Notes

YML code

This is just the fragment required for the custom DIO driver. We desire the DIO adapter to fresh the channel status every 30 seconds, plus the channels will be updated any time they change. The four IO are assigned like this:

  - name: motion
    driver: devices.experimental.xbee_motion:XBeeMotion
    settings:
        xbee_device_manager: xbee_device_manager
        extended_address: "00:13:a2:00:40:33:4e:a9!"
        sample_rate_ms: 30000
        sleep: False
        power: Off
        channel1_dir: "In"
        channel2_dir: "In"
        channel3_dir: "Out"
        channel4_dir: "Out"

Source code

Here is the entire custom file. Since it uses the normal XBee DIO driver, there is little function required. Basically, this driver allows the XBee DIO driver to process the incoming data, then compares the Motion-Detect input to the current light state.

import traceback
import time

from devices.device_base import DeviceBase
from devices.xbee.xbee_devices.xbee_base import XBeeBase
from settings.settings_base import SettingsBase, Setting

from devices.xbee.xbee_devices.xbee_dio import *

class XBeeMotion(XBeeDIO):

    OFF_DELAY = 30

    def __init__(self, name, core_services):

        ## Initialize the base class
        XBeeDIO.__init__(self, name, core_services)

        # zero means off, else holds time.time turned on
        self.__light_on = 0

        return

    ## Locally defined functions:
    def sample_indication(self, buf, addr, force=False):

        ## allow base class to process data message
        XBeeDIO.sample_indication(self, buf, addr, force)

        # then do our reaction to the status
        now = time.time()

        motion = self.property_get( 'channel1_input').value
        if motion:
            if self.__light_on == 0:
                print 'Motion Seen, turning light on'
                self.set_output(Sample(now, True, "On"), 3)

            # else: print 'Motion Seen, light already on'

            # in all cases, bump light_on time to now
            self.__light_on = now

      else:
            # else no motion
            if self.__light_on != 0:
                # then light is on
                if (now - self.__light_on) > self.OFF_DELAY:
                    print 'No Motion, turning light off'                
                    self.set_output(Sample(now, False, "Off"), 3)
                    self.__light_on = 0

                # else: print 'No Motion Seen, light is on, should stay on'

      return