Gpiozero: How to mock MotionSensor.when_motion?

Created on 17 Jun 2020  路  4Comments  路  Source: gpiozero/gpiozero

Operating system: Debian Linux 10 (Buster)
Python version: 3.8
No Pi
GPIO Zero version: 1.5.1
Pin factory used: e.g. MockFactory

Hi,
I am trying to trigger a callback function with the following simple code, but nothing happens. And while looking at the test coverage, I only found imperative test code for MotionSensor.

Is it possible to "trigger" the callback somehow?

from gpiozero.pins.mock import MockFactory
from gpiozero import Device, MotionSensor, LED
from signal import pause
from time import sleep

Device.pin_factory = MockFactory()

pir = MotionSensor(7)
led = LED(26)



def test_cb(channel):
    print("Motion detected")
    print(channel)


pir.when_motion = test_cb

# Mocking the device
pir_pin = Device.pin_factory.pin(7)
pir_pin.drive_high()
sleep(5)
pir_pin.drive_low()

question

Most helpful comment

Hi,
I had the same problem this evening.
After some debugging I found out that my function might not getting triggered because of the sample_wait configuration (1/10[sec]) in MotionSensor constructor.

After putting a sleep with > sample_wait in my Test when configuring the callback or setting pin states it's working.

from time import sleep

from gpiozero import Device, MotionSensor
from gpiozero.pins.mock import MockFactory, MockPin

Device.pin_factory = MockFactory()
pin: MockPin = Device.pin_factory.pin(7)

pir = MotionSensor(7)


def test_cb(channel):
    print(pir.motion_detected)
    print("Motion detected")
    print(channel)


"""
    sleep calls in this test are necessary that motion sensor gets activated
    this is due to gpiozero.input_devices.SmoothedInputDevice.__init__  and
    the default sample wait time (1/10) configured in gpiozero/input_devices.py:612 (__init__)
"""
# default is 1/10, so to make sure wait twice as long
sample_wait_time_sec = (2/10)
pir.when_activated = test_cb
sleep(sample_wait_time_sec)

# Mocking the device
pin.drive_high()
sleep(sample_wait_time_sec)

print(pir.is_active)
print(pir.motion_detected)

Hi @matt-hires, thx for the reply. I just tested your proposal and I can confirm your findings . 馃

All 4 comments

That's weird. I tried it using slightly different code and it worked fine. But your version doesn't.

This example is working fine for me (Python 3.8 on Ubuntu) in IPython:

In [1]: from gpiozero.pins.mock import MockFactory 
   ...: from gpiozero import Device, MotionSensor 
   ...:  
   ...: Device.pin_factory = MockFactory() 
   ...:  
   ...: pir = MotionSensor(7)                                                                        

In [2]: def test_cb(channel): 
   ...:     print("Motion detected") 
   ...:     print(channel) 
   ...:                                                                                              

In [3]: pir.when_motion = test_cb                                                                    

In [4]: pir.value                                                                                    
Out[4]: 0

In [5]: pir.pin.drive_high()                                                                         

Motion detected
<gpiozero.MotionSensor object on pin GPIO7, pull_up=False, is_active=True>
In [6]: pir.value                                                                                    
Out[6]: 1

In [7]: pir.pin.drive_low()                                                                          

In [8]: pir.value                                                                                    
Out[8]: 0

The callback fires as soon as I run pin.drive_high().

The only difference really is me using pir.pin and you're using Device.pin_factory.pin(7).

Oddly:

In [3]: pir_pin == pir.pin                                                                           
Out[3]: True

That's a head scratcher. @lurch any ideas? I'll ask @waveform80 if I get chance.

For me, even simpler code only works, if executed in the context of the REPL, but not as a standalone script.

from gpiozero.pins.mock import MockFactory
from gpiozero import Device, MotionSensor, LED
from signal import pause

Device.pin_factory = MockFactory()

pir = MotionSensor(7)


def test_cb(channel):
    print(pir.motion_detected)
    print("Motion detected")
    print(channel)


pir.when_motion = test_cb

# Mocking the device
pir.pin.drive_high()

pause()

Hi,
I had the same problem this evening.
After some debugging I found out that my function might not getting triggered because of the sample_wait configuration (1/10[sec]) in MotionSensor constructor.

After putting a sleep with > sample_wait in my Test when configuring the callback or setting pin states it's working.

from time import sleep

from gpiozero import Device, MotionSensor
from gpiozero.pins.mock import MockFactory, MockPin

Device.pin_factory = MockFactory()
pin: MockPin = Device.pin_factory.pin(7)

pir = MotionSensor(7)


def test_cb(channel):
    print(pir.motion_detected)
    print("Motion detected")
    print(channel)


"""
    sleep calls in this test are necessary that motion sensor gets activated
    this is due to gpiozero.input_devices.SmoothedInputDevice.__init__  and
    the default sample wait time (1/10) configured in gpiozero/input_devices.py:612 (__init__)
"""
# default is 1/10, so to make sure wait twice as long
sample_wait_time_sec = (2/10)
pir.when_activated = test_cb
sleep(sample_wait_time_sec)

# Mocking the device
pin.drive_high()
sleep(sample_wait_time_sec)

print(pir.is_active)
print(pir.motion_detected)

Hi,
I had the same problem this evening.
After some debugging I found out that my function might not getting triggered because of the sample_wait configuration (1/10[sec]) in MotionSensor constructor.

After putting a sleep with > sample_wait in my Test when configuring the callback or setting pin states it's working.

from time import sleep

from gpiozero import Device, MotionSensor
from gpiozero.pins.mock import MockFactory, MockPin

Device.pin_factory = MockFactory()
pin: MockPin = Device.pin_factory.pin(7)

pir = MotionSensor(7)


def test_cb(channel):
    print(pir.motion_detected)
    print("Motion detected")
    print(channel)


"""
    sleep calls in this test are necessary that motion sensor gets activated
    this is due to gpiozero.input_devices.SmoothedInputDevice.__init__  and
    the default sample wait time (1/10) configured in gpiozero/input_devices.py:612 (__init__)
"""
# default is 1/10, so to make sure wait twice as long
sample_wait_time_sec = (2/10)
pir.when_activated = test_cb
sleep(sample_wait_time_sec)

# Mocking the device
pin.drive_high()
sleep(sample_wait_time_sec)

print(pir.is_active)
print(pir.motion_detected)

Hi @matt-hires, thx for the reply. I just tested your proposal and I can confirm your findings . 馃

Was this page helpful?
0 / 5 - 0 ratings

Related issues

e28eta picture e28eta  路  4Comments

waveform80 picture waveform80  路  10Comments

lurch picture lurch  路  6Comments

Natureshadow picture Natureshadow  路  11Comments

bennuttall picture bennuttall  路  6Comments