I have a Raspberry Pi 3B with a USB microphone. I have used a combination of Bash and Python scripts to detect noise levels above a certain threshold that trigger a notification on my phone. The actual sound is not streamed.
The scripts are called using a command line alias for the following:
bash /home/pi/babymonitor.sh | /home/pi/monitor.py
Bash script - babymonitor.sh
This uses arecord
to repeatedly record 2-second snippets via a temporary file, sox
and grep
to get the RMS (root mean square) amplitude (see this example for sox stat output) and then tail
to get only the numerical data at the end of the line. This is then piped to the Python script.
#!/bin/bash
trap break INT
while true; do
arecord --device=hw:1,0 --format S16_LE --rate 44100 -d 2 /dev/shm/tmp_rec.wav ; sox -t .wav /dev/shm/tmp_rec.wav -n stat 2>&1 | grep "RMS amplitude" | tail -c 9
done
Python processing script - monitor.py
This receives the volume data and compares it to an arbitrarily defined threshold that I chose based on testing. If a noise is detected in one of the 2-second snippets, a push notification is sent and it suppresses further notifications for 10 seconds (5 cycles of 2-second snippets).
#!/usr/bin/env python
import sys
import push
THRESHOLD = 0.01
count = 0
suppress = False
while True:
try:
line = sys.stdin.readline().strip() # e.g. "0.006543"
number = float(line)
if number > THRESHOLD and not suppress:
p = push.PushoverSender("user_key", "api_token")
p.send_notification("There's a noise in the nursery!")
count = 0
suppress = True
elif suppress:
print("Suppressing output")
else:
print("All quiet")
# Count 5 cycles after a trigger
if suppress:
count += 1
if count >= 5:
count = 0
suppress = False
except ValueError:
# Cannot coerce to float or error in stdin
print("Value error: " + line)
break
except KeyboardInterrupt:
# Cancelled by user
print("Baby monitor script ending")
break
sys.exit()
Script for push notification service - push.py
This sends an http
request to the Pushover service, which results in a push notification on my phone.
#!/usr/bin/env python
import httplib
import urllib
class PushoverSender:
def __init__(self, user_key, api_key):
self.user_key = user_key
self.api_key = api_key
def send_notification(self, text):
conn = httplib.HTTPSConnection("api.pushover.net:443")
post_data = {'user': self.user_key, 'token': self.api_key, 'message': text}
conn.request("POST", "/1/messages.json", urllib.urlencode(post_data), {"Content-type": "application/x-www-form-urlencoded"})
print(conn.getresponse().read())
I would be grateful for any feedback on how I can improve my code or the conceptual aspects of this project. I am not very familiar with Bash scripting so feedback is particularly welcome on this.
Is it acceptable to have the Bash script and Python script called together with a pipe between them and both running
while True
loops? It certainly seems to work.Is there a better way to exit? Current pressing ctrl+c will terminate both the Bash script (due to
trap break INT
) and the Python script (due toexcept KeyboardInterrupt
).