Merge branch 'sqlite' of github.com:mcguirepr89/BirdNET-Pi into sqlite

This commit is contained in:
mcguirepr89
2022-02-18 08:55:55 -05:00
9 changed files with 170 additions and 898 deletions
-8
View File
@@ -28,14 +28,6 @@ BIRDWEATHER_ID=
CADDY_PWD= CADDY_PWD=
#------------------------- MariaDB User Passwords ---------------------------#
#_____________The variable below sets the password for the_____________________#
#_______________________'birder' user on the MariaDB___________________________#
## DB_PWD is for the 'birder' user. The shell substitution below sets a random
## password for you during installation.
DB_PWD=$(< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c${1:-32};echo;)
#------------------------- Live Audio Stream --------------------------------# #------------------------- Live Audio Stream --------------------------------#
#_____________The variable below configures/enables the live___________________# #_____________The variable below configures/enables the live___________________#
#_____________________________audio stream.____________________________________# #_____________________________audio stream.____________________________________#
+1 -1
View File
@@ -2,7 +2,7 @@
# Simple new installer # Simple new installer
HOME=/home/pi HOME=/home/pi
USER=pi USER=pi
branch=main branch=sqlite
if ! which git &> /dev/null;then if ! which git &> /dev/null;then
sudo apt update sudo apt update
sudo apt -y install git sudo apt -y install git
+24
View File
@@ -0,0 +1,24 @@
#!/usr/bin/env bash
# This script performs the mysql_secure_installation
# Creates the birds database
# Creates the detections table
# Creates the birder user and grants them appropriate
# permissions
# If using this script to re-initialize (DROP then CREATE)
# the DB, be sure to run this as root or with sudo
source /etc/birdnet/birdnet.conf
sqlite3 /home/pi/BirdNET-Pi/scripts/birds.db << EOF
CREATE TABLE IF NOT EXISTS detections (
Date DATE,
Time TIME,
Sci_Name VARCHAR(100) NOT NULL,
Com_Name VARCHAR(100) NOT NULL,
Confidence FLOAT,
Lat FLOAT,
Lon FLOAT,
Cutoff FLOAT,
Week INT,
Sens FLOAT,
Overlap FLOAT,
File_Name VARCHAR(100) NOT NULL);
EOF
-46
View File
@@ -1,46 +0,0 @@
#!/usr/bin/env bash
# This script performs the mysql_secure_installation
# Creates the birds database
# Creates the detections table
# Creates the birder user and grants them appropriate
# permissions
# If using this script to re-initialize (DROP then CREATE)
# the DB, be sure to run this as root or with sudo
source /etc/birdnet/birdnet.conf
mysql_secure_installation << EOF
y
n
y
y
y
y
EOF
mysql << EOF
DROP DATABASE IF EXISTS birds;
CREATE DATABASE IF NOT EXISTS birds;
USE birds;
CREATE TABLE IF NOT EXISTS detections (
Date DATE,
Time TIME,
Sci_Name VARCHAR(100) NOT NULL,
Com_Name VARCHAR(100) NOT NULL,
Confidence FLOAT,
Lat FLOAT,
Lon FLOAT,
Cutoff FLOAT,
Week INT,
Sens FLOAT,
Overlap FLOAT);
GRANT ALL ON birds.* TO 'birder'@'localhost' IDENTIFIED BY '${DB_PWD}' WITH GRANT OPTION;
FLUSH PRIVILEGES;
exit
EOF
sudo -u${USER} sed -i "s/databasepassword/${DB_PWD}/g" /home/pi/BirdNET-Pi/scripts/server.py
sed -i "s/mysqli.default_host =.*/mysqli.default_host = localhost/g" /etc/php/7.4/fpm/php.ini
sed -i "s/mysqli.default_user =.*/mysqli.default_user = birder/g" /etc/php/7.4/fpm/php.ini
sed -i "s/mysqli.default_pw =.*/mysqli.default_pw = ${DB_PWD}/g" /etc/php/7.4/fpm/php.ini
+10 -42
View File
@@ -20,27 +20,26 @@ fi
sudo ./install_services.sh || exit 1 sudo ./install_services.sh || exit 1
source /etc/birdnet/birdnet.conf source /etc/birdnet/birdnet.conf
APT_DEPS=(swig ffmpeg wget unzip curl cmake make bc) apt_deps=(swig ffmpeg wget unzip curl cmake make bc)
LIBS_MODULES=(libjpeg-dev zlib1g-dev python3-dev python3-pip python3-venv) libs_modules=(libjpeg-dev zlib1g-dev python3-dev python3-pip python3-venv)
install_deps() { install_deps() {
echo " Checking dependencies" echo "Checking dependencies"
sudo apt update &> /dev/null for i in "${libs_modules[@]}";do
for i in "${LIBS_MODULES[@]}";do
if [ $(apt list --installed 2>/dev/null | grep "$i" | wc -l) -le 0 ];then if [ $(apt list --installed 2>/dev/null | grep "$i" | wc -l) -le 0 ];then
echo " Installing $i" echo " Installing $i"
sudo apt -y install ${i} &> /dev/null sudo apt -y install ${i} &> /dev/null
else else
echo " $i is installed!" echo "$i is installed!"
fi fi
done done
for i in "${APT_DEPS[@]}";do for i in "${apt_deps[@]}";do
if ! which $i &>/dev/null ;then if ! which $i &>/dev/null ;then
echo " Installing $i" echo "Installing $i"
sudo apt -y install ${i} &> /dev/null sudo apt -y install ${i} &> /dev/null
else else
echo " $i is installed!" echo "$i is installed!"
fi fi
done done
} }
@@ -51,21 +50,10 @@ install_birdnet() {
python3 -m venv birdnet python3 -m venv birdnet
source ./birdnet/bin/activate source ./birdnet/bin/activate
echo "Upgrading pip, wheel, and setuptools" echo "Upgrading pip, wheel, and setuptools"
pip3 install --upgrade pip~=21.0.0 wheel setuptools pip3 install --upgrade pip wheel setuptools
python_version="$(awk -F. '{print $2}' <(ls -l $(which /usr/bin/python3)))"
echo "python_version=${python_version}"
# TFLite Pre-built binaires from https://github.com/PINTO0309/TensorflowLite-bin # TFLite Pre-built binaires from https://github.com/PINTO0309/TensorflowLite-bin
# Python 3.7
if [[ "$python_version" == 7 ]];then
echo "Installing the TFLite bin wheel"
pip3 install --upgrade tflite_runtime-2.6.0-cp37-none-linux_aarch64.whl
fi
# Python 3.9
if [[ "$python_version" == 9 ]];then
echo "Installing the TFLite bin wheel" echo "Installing the TFLite bin wheel"
pip3 install --upgrade tflite_runtime-2.6.0-cp39-none-linux_aarch64.whl pip3 install --upgrade tflite_runtime-2.6.0-cp39-none-linux_aarch64.whl
fi
echo "Making sure everything else is installed" echo "Making sure everything else is installed"
pip3 install -U -r /home/pi/BirdNET-Pi/requirements.txt pip3 install -U -r /home/pi/BirdNET-Pi/requirements.txt
} }
@@ -76,24 +64,4 @@ install_deps
if [ ! -d ${VENV} ];then if [ ! -d ${VENV} ];then
install_birdnet install_birdnet
fi fi
exit 0
echo " BirdNet is installed!!
To start the service manually, issue:
'sudo systemctl start birdnet_analysis'
To monitor the service logs, issue:
'journalctl -fu birdnet_analysis'
To stop the service manually, issue:
'sudo systemctl stop birdnet_analysis'
To stop and disable the service, issue:
'sudo systemctl disable --now birdnet_analysis.service'
Visit
the BirdNET-Pi homepage at http://birdnetpi.local"
echo
case $YN in
[Yy] ) sudo systemctl start birdnet_analysis.service \
&& journalctl -fu birdnet_analysis;;
* ) echo " Thanks for installing BirdNET-Pi!!
I hope it was helpful!";;
esac
+83 -274
View File
@@ -6,240 +6,64 @@ trap 'exit 1' SIGINT SIGHUP
my_dir=$(realpath $(dirname $0)) my_dir=$(realpath $(dirname $0))
birdnetpi_dir=$(realpath $(dirname $my_dir)) birdnetpi_dir=$(realpath $(dirname $my_dir))
BIRDNET_CONF="$(dirname ${my_dir})/birdnet.conf" birdnet_conf="$(dirname ${my_dir})/birdnet.conf"
get_RECS_DIR() { install_config() {
read -p "What is the full path to your recordings directory (locally)? " RECS_DIR
}
get_LATITUDE() {
read -p "What is the latitude where the recordings were made? " LATITUDE
}
get_LONGITUDE() {
read -p "What is the longitude where the recordings were made? " LONGITUDE
}
get_DO_EXTRACTIONS() {
while true; do
read -n1 -p "Do you want this device to perform the extractions? " DO_EXTRACTIONS
echo
case $DO_EXTRACTIONS in
[Yy] ) break;;
[Nn] ) break;;
* ) echo "You must answer with Yes or No (y or n)";;
esac
done
}
get_DO_RECORDING() {
while true; do
read -n1 -p "Is this device also doing the recording? " DO_RECORDING
echo
case $DO_RECORDING in
[Yy] ) break;;
[Nn] ) break;;
* ) echo "You must answer with Yes or No (y or n)";;
esac
done
}
get_BIRDNETPI_URL() {
while true;do
read -n1 -p "Would you like to access the extractions via a web browser?
*Note: It is recommended, (but not required), that you run the web
server on the same host that does the extractions. If the extraction
service and web server are on different hosts, the \"By_Species\" and
\"Processed\" symbolic links won't work. The \"By-Date\" extractions,
however, will work as expected." CADDY_SERVICE
echo
case $CADDY_SERVICE in
[Yy] ) read -p "What URL would you like to publish the extractions to?
*Note: Set this to http://localhost if you do not want to make the
extractions publically available: " BIRDNETPI_URL
get_CADDY_PWD
get_ICE_PWD
break;;
[Nn] ) BIRDNETPI_URL= CADDY_PWD= ICE_PWD=;break;;
* ) echo "Please answer Yes or No";;
esac
done
}
get_CADDY_PWD() {
if [ -z ${CADDY_PWD} ]; then
while true; do
read -p "Please set a password to protect your data: " CADDY_PWD
case $CADDY_PWD in
"" ) echo "The password cannot be empty. Please try again.";;
* ) break;;
esac
done
fi
}
get_ICE_PWD() {
if [ ! -z ${CADDY_PWD} ] && [[ ${DO_RECORDING} =~ [Yy] ]];then
while true; do
read -n1 -p "Would you like to enable the live audio streaming service?" LIVE_STREAM
echo
case $LIVE_STREAM in
[Yy] )
read -p "Please set the icecast password. Use only alphanumeric characters." ICE_PWD
echo
case ${ICE_PWD} in
"" ) echo "The password cannot be empty. Please try again.";;
*) break;;
esac
break;;
[Nn] ) break;;
* ) echo "You must answer Yes or No (y or n).";;
esac
done
fi
}
get_DB_PWDS() {
if [ ! -z ${DB_PWD} ];then
read -p "Please set a password for your database: " DB_PWD
echo
fi
}
get_PUSHED() {
while true; do
read -n1 -p "Do you have a free App key to receive mobile notifications via Pushed.co?" YN
echo
case $YN in
[Yy] ) read -p "Enter your Pushed.co App Key: " PUSHED_APP_KEY
read -p "Enter your Pushed.co App Key Secret: " PUSHED_APP_SECRET
break;;
[Nn] ) PUSHED_APP_KEY=
PUSHED_APP_SECRET=
break;;
* ) echo "A simple Yea or Nay will do";;
esac
done
}
get_INSTALL_NOMACHINE() {
while true; do
read -n1 -p "Would you like to also install NoMachine for remote desktop access?" INSTALL_NOMACHINE
echo
case $INSTALL_NOMACHINE in
[Yy] ) break;;
[Nn] ) break;;
* ) echo "You must answer with Yes or No (y or n)";;
esac
done
}
get_CHANNELS() {
REC_CARD="$(sudo -u pi aplay -L \
| grep dsnoop \
| cut -d, -f1 \
| grep -ve 'vc4' -e 'Head' -e 'PCH' \
| uniq)"
[ -f $(dirname ${my_dir})/soundcard_params.txt ] || touch $(dirname ${my_dir})/soundcard_params.txt
SOUND_PARAMS=$(dirname ${my_dir})/soundcard_params.txt
SOUND_CARD="$(sudo -u ${USER} aplay -L \
| awk -F, '/^hw:/ {print $1}' \
| grep -ve 'vc4' -e 'Head' -e 'PCH' \
| uniq)"
script -c "arecord -D ${SOUND_CARD} --dump-hw-params" -a "${SOUND_PARAMS}" &> /dev/null
CHANNELS=$(awk '/CHANN/ { print $2 }' "${SOUND_PARAMS}" | sed 's/\r$//')
[ -z REC_CARD ] && REC_CARD=default
[ -z CHANNELS ] && CHANNELS=2
echo "REC_CARD variable set to ${REC_CARD}"
echo "Number of channels available: ${CHANNELS}"
}
configure() {
get_RECS_DIR
get_LATITUDE
get_LONGITUDE
get_DB_PWDS
get_DO_EXTRACTIONS
get_DO_RECORDING
get_BIRDNETPI_URL
get_PUSHED
get_INSTALL_NOMACHINE
get_CHANNELS
}
install_birdnet_conf() {
cat << EOF > $(dirname ${my_dir})/birdnet.conf cat << EOF > $(dirname ${my_dir})/birdnet.conf
################################################################################ ################################################################################
# Configuration settings for BirdNET-Pi # # Configuration settings for BirdNET-Pi #
################################################################################ ################################################################################
############ CHANGE THE LATITUDE AND LONGITUDE TO STATIC VALUES ################ #--------------------- Required: Latitude, and Longitude ----------------------#
## The shell substitution below guesses these based on your network. THESE NEED
## TO BE CHANGED TO STATIC VALUES
## Please only go to 4 decimal places. Example:43.3984
LATITUDE="$(curl -s4 ifconfig.co/json | awk '/lat/ {print $2}' | tr -d ',')"
LONGITUDE="$(curl -s4 ifconfig.co/json | awk '/lon/ {print $2}' | tr -d ',')"
## LATITUDE and LONGITUDE are self-explanatroy. Find them easily at #--------------------- BirdWeather Station Information -----------------------#
## maps.google.com. #_____________The variable below can be set to have your BirdNET-Pi____________#
## Example: these coordinates would indicate the Eiffel Tower in Paris, France. #__________________also act as a BirdWeather listening station_________________#
## LATITUDE=48.858
## LONGITUDE=2.294
## These are input as shell substitutions so that this will work out of the box. BIRDWEATHER_ID=
## It guesses your latitude and longitude based off of your network information.
## THESE SHOULD BE CHANGED TO STATIC NUMBERS!!!!
LATITUDE=${LATITUDE}
LONGITUDE=${LONGITUDE}
## RECS_DIR is the location birdnet_analysis.service will look for the data-set
## it needs to analyze. Be sure this directory is readable and writable for
## the BIRDNET_USER. If you are going to be accessing a remote data-set, you
## still need to set this, as this will be where the remote directory gets
## mounted locally.
RECS_DIR=${RECS_DIR}
#----------------------- Web Interface User Password ------------------------# #----------------------- Web Interface User Password ------------------------#
#____________________The variable below sets the 'birdnet'_____________________# #____________________The variable below sets the 'birdnet'_____________________#
#___________________user password for the live audio stream,___________________# #___________________user password for the Live Audio Stream,___________________#
#_________________web tools, system info, and processed files__________________# #_________________Tools, System Links, and the Processed files ________________#
## CADDY_PWD is the plaintext password (that will be hashed) and used to access ## CADDY_PWD is the plaintext password (that will be hashed) and used to access
## certain parts of the web interface ## certain parts of the web interface
CADDY_PWD=${CADDY_PWD} CADDY_PWD=
#------------------------- MariaDB User Passwords ---------------------------#
#_____________The variable below sets the password for the_____________________#
#_______________________'birder' user on the MariaDB___________________________#
## DB_PWD is for the 'birder' user
DB_PWD=${DB_PWD}
#------------------------- Live Audio Stream --------------------------------# #------------------------- Live Audio Stream --------------------------------#
#_____________The variable below configures/enables the live___________________# #_____________The variable below configures/enables the live___________________#
#_____________________________audio stream.____________________________________# #_____________________________audio stream.____________________________________#
## ICE_PWD is the password that icecast2 will use to authenticate ffmpeg as a ## ICE_PWD is the password that icecast2 will use to authenticate ffmpeg as a
## trusted source for the stream. You will never need to enter this manually ## trusted source for the stream. You will never need to enter this manually
## anywhere other than here. ## anywhere other than here and it stays on 'localhost.'
ICE_PWD=${ICE_PWD} ICE_PWD=birdnetpi
#----------------------- Web-hosting/Caddy File-server -----------------------# #----------------------- Web-hosting/Caddy File-server -----------------------#
#________The four variables below can be set to enable internet access_________# #_______The three variables below can be set to enable internet access_________#
#____________to your data,(e.g., extractions, raw data, live___________________# #____________to your data,(e.g., extractions, raw data, live___________________#
#______________audio stream, BirdNET.selection.txt files)______________________# #______________audio stream, BirdNET.selection.txt files)______________________#
## BIRDNETPI_URL is the URL where the extractions, data-set, and live-stream ## BIRDNETPI_URL is the URL where the extractions, data-set, and live-stream
## will be web-hosted. If you do not own a domain, or would just prefer to keep ## will be web-hosted. If you do not own a domain, or would just prefer to keep
## the BirdNET-Pi on your local network, keep this EMPTY. ## the BirdNET-Pi on your local network, keep this EMPTY.
BIRDNETPI_URL=${BIRDNETPI_URL} BIRDNETPI_URL=
WEBTERMINAL_URL${WEBTERMINAL_URL} BIRDNETLOG_URL=
BIRDNETLOG_URL=${BIRDNETLOG_URL} WEBTERMINAL_URL=
#------------------- Mobile Notifications via Pushed.co ---------------------# #------------------- Mobile Notifications via Pushed.co ---------------------#
@@ -251,34 +75,26 @@ BIRDNETLOG_URL=${BIRDNETLOG_URL}
## Pushed.co App Key and App Secret ## Pushed.co App Key and App Secret
PUSHED_APP_KEY=${PUSHED_APP_KEY} PUSHED_APP_KEY=
PUSHED_APP_SECRET=${PUSHED_APP_SECRET} PUSHED_APP_SECRET=
################################################################################ ################################################################################
#-------------------------------- Defaults ----------------------------------# #-------------------------------- Defaults ----------------------------------#
################################################################################ ################################################################################
#------------------------------- NoMachine ----------------------------------# ## RECS_DIR is the location birdnet_analysis.service will look for the data-set
#_____________The variable below can be set include NoMachine__________________# ## it needs to analyze. Be sure this directory is readable and writable for
#_________________remote desktop software to be installed._____________________# ## the BIRDNET_USER.
# Keep this EMPTY if you do not want to install NoMachine. # RECS_DIR=/home/pi/BirdSongs
## INSTALL_NOMACHINE is simply a setting that can be enabled to install
## NoMachine alongside the BirdNET-Pi for remote desktop access. This in-
## staller assumes personal use. Please reference the LICENSE file included
## in this repository for more information.
## Set this to Y or y to install NoMachine alongside the BirdNET-Lite
INSTALL_NOMACHINE=${INSTALL_NOMACHINE}
#
#------------------------------ Extraction Service ---------------------------# #------------------------------ Extraction Service ---------------------------#
## DO_EXTRACTIONS is simply a setting for enabling the extraction.service. ## DO_EXTRACTIONS is simply a setting for enabling the extraction.service.
## Set this to Y or y to enable extractions. ## Set this to Y or y to enable extractions.
DO_EXTRACTIONS=${DO_EXTRACTIONS} DO_EXTRACTIONS=y
#----------------------------- Recording Service ----------------------------# #----------------------------- Recording Service ----------------------------#
#____________________The variable below can be set to enable __________________# #____________________The variable below can be set to enable __________________#
@@ -288,60 +104,49 @@ DO_EXTRACTIONS=${DO_EXTRACTIONS}
## birdnet_recording.service. ## birdnet_recording.service.
## Set this to Y or y to enable recording. ## Set this to Y or y to enable recording.
DO_RECORDING=${DO_RECORDING} DO_RECORDING=y
## REC_CARD is the sound card you would want the birdnet_recording.service to ## REC_CARD is the sound card you would want the birdnet_recording.service to
## use. This setting is irrelevant if you are not planning on doing data ## use. Leave this as "default" to use PulseAudio (recommended), or use
## collection via recording on this machine. The command substitution below ## the output from "aplay -L" to specify an ALSA device.
## looks for a USB microphone's dsnoop alsa device. The dsnoop device lets
## birdnet_recording.service and livestream.service share the raw audio stream
## from the microphone. If you would like to use a different microphone than
## what this produces, or if your microphone does not support creating a
## dsnoop device, you can set this explicitly from a list of the available
## devices from the output of running 'aplay -L'
REC_CARD=${REC_CARD} REC_CARD=default
## PROCESSED is the directory where the formerly 'Analyzed' files are moved ## PROCESSED is the directory where the formerly 'Analyzed' files are moved
## after extractions have been made from them. This includes both WAVE and ## after extractions have been made from them. This includes both WAVE and
## BirdNET.selection.txt files. ## BirdNET.selection.txt files.
PROCESSED=${PROCESSED} PROCESSED=${RECS_DIR}/Processed
## EXTRACTED is the directory where the extracted audio selections are moved. ## EXTRACTED is the directory where the extracted audio selections are moved.
EXTRACTED=${EXTRACTED} EXTRACTED=${RECS_DIR}/Extracted
## IDFILE is the file that keeps a complete list of every spececies that ## IDFILE is the file that keeps a complete list of every spececies that
## BirdNET has identified from your data-set. It is persistent across ## BirdNET has identified from your data-set. It is a relic and not really
## data-sets, so would need to be whiped clean through deleting or renaming ## used anymore.
## it. A backup is automatically made from this variable each time it is
## updated (structure: ${IDFILE}.bak), and would also need to be removed
## or renamed to start a new file between data-sets. Alternately, you can
## change this variable between data-sets to preserve records of disparate
## data-sets according to name.
IDFILE=${IDFILE} IDFILE=${HOME}/BirdNET-Pi/IdentifiedSoFar.txt
## OVERLAP is the value in seconds which BirdNET should use when analyzing ## OVERLAP is the value in seconds which BirdNET should use when analyzing
## the data. The values must be between 0.0-2.9. ## the data. The values must be between 0.0-2.9.
OVERLAP=${OVERLAP} OVERLAP=0.0
## CONFIDENCE is the minimum confidence level from 0.0-1.0 BirdNET's analysis ## CONFIDENCE is the minimum confidence level from 0.0-1.0 BirdNET's analysis
## should reach before creating an entry in the BirdNET.selection.txt file. ## should reach before creating an entry in the BirdNET.selection.txt file.
## Don't set this to 1.0 or you won't have any results. ## Don't set this to 1.0 or you won't have any results.
CONFIDENCE=${CONFIDENCE} CONFIDENCE=0.7
## SENSITIVITY is the detection sensitivity from 0.5-1.5. ## SENSITIVITY is the detection sensitivity from 0.5-1.5.
SENSITIVITY=${SENSITIVITY} SENSITIVITY=1.25
## CHANNELS holds the variabel that corresponds to the number of channels the ## CHANNELS holds the variable that corresponds to the number of channels the
## sound card supports. ## sound card supports.
CHANNELS=${CHANNELS} CHANNELS=2
## FULL_DISK can be set to configure how the system reacts to a full disk ## FULL_DISK can be set to configure how the system reacts to a full disk
## purge = Remove the oldest day's worth of recordings ## purge = Remove the oldest day's worth of recordings
@@ -349,46 +154,50 @@ CHANNELS=${CHANNELS}
FULL_DISK=purge FULL_DISK=purge
## VENV is the virtual environment where the the BirdNET python build is found, ## VENV is the python virtual environment wherein all modules and site-packages
## i.e, VENV is the virtual environment miniforge built for BirdNET. ## have been installed.
VENV=/home/pi/BirdNET-Pi/birdnet VENV=/home/pi/BirdNET-Pi/birdnet
## RECORDING_LENGTH sets the length of the recording that BirdNET-Lite will analyze. ## RECORDING_LENGTH sets the length of the recording that BirdNET-Lite will
RECORDING_LENGTH=${RECORDING_LENGTH} ## analyze.
RECORDING_LENGTH=15
## EXTRACTION_LENGTH sets the length of the audio extractions that will be made ## EXTRACTION_LENGTH sets the length of the audio extractions that will be made
## from each BirdNET-Lite detection. ## from each BirdNET-Lite detection. An empty value will use the default of 6
EXTRACTION_LENGTH=${EXTRACTION_LENGTH} ## seconds.
## BIRDNET_USER should be the non-root user systemd should use to execute each EXTRACTION_LENGTH=
## AUDIOFMT set the audio format that sox should use for the extractions.
## The default is mp3. Available formats are: 8svx aif aifc aiff aiffc al amb
## amr-nb amr-wb anb au avr awb caf cdda cdr cvs cvsd cvu dat dvms f32 f4 f64 f8
## fap flac fssd gsm gsrt hcom htk ima ircam la lpc lpc10 lu mat mat4 mat5 maud
## mp2 mp3 nist ogg paf prc pvf raw s1 s16 s2 s24 s3 s32 s4 s8 sb sd2 sds sf sl
## sln smp snd sndfile sndr sndt sou sox sph sw txw u1 u16 u2 u24 u3 u32 u4 u8
## ub ul uw vms voc vorbis vox w64 wav wavpcm wv wve xa xi
## Note: Most have not been tested.
AUDIOFMT=mp3
## BIRDNET_USER should be the non-root user systemd should use to execute each
## service. ## service.
BIRDNET_USER=pi BIRDNET_USER=pi
## These are just for debugging ## These are just for debugging
LAST_RUN=${LAST_RUN} LAST_RUN=
THIS_RUN=${THIS_RUN} THIS_RUN=
EOF EOF
} }
# Checks for a birdnet.conf file in the BirdNET-Pi directory for a # Checks for a birdnet.conf file
# non-interactive installation. Otherwise,the installation is interactive. if ! [ -f ${birdnet_conf} ];then
if [ -f ${BIRDNET_CONF} ];then install_config
source ${BIRDNET_CONF}
chmod g+w ${BIRDNET_CONF}
sed -i "s/LATITUDE=.*/LATITUDE=${LATITUDE}/g" ${BIRDNET_CONF}
sed -i "s/LONGITUDE=.*/LONGITUDE=${LONGITUDE}/g" ${BIRDNET_CONF}
sed -i "s/DB_PWD=.*/DB_PWD=${DB_PWD}/g" ${BIRDNET_CONF}
#install_birdnet_conf
[ -d /etc/birdnet ] || sudo mkdir /etc/birdnet
sudo ln -sf $(dirname ${my_dir})/birdnet.conf /etc/birdnet/birdnet.conf
grep -ve '^#' -e '^$' /etc/birdnet/birdnet.conf > ${birdnetpi_dir}/firstrun.ini
else
configure
install_birdnet_conf
chmod g+w ${BIRDNET_CONF}
[ -d /etc/birdnet ] || sudo mkdir /etc/birdnet
sudo ln -sf $(dirname ${my_dir})/birdnet.conf /etc/birdnet/birdnet.conf
grep -ve '^#' -e '^$' /etc/birdnet/birdnet.conf > ${birdnetpi_dir}/firstrun.ini
fi fi
chmod g+w ${birdnet_conf}
[ -d /etc/birdnet ] || sudo mkdir /etc/birdnet
sudo ln -sf $(dirname ${my_dir})/birdnet.conf /etc/birdnet/birdnet.conf
grep -ve '^#' -e '^$' /etc/birdnet/birdnet.conf > ${birdnetpi_dir}/firstrun.ini
+29 -68
View File
@@ -11,6 +11,11 @@ nomachine_url="https://download.nomachine.com/download/7.7/Arm/nomachine_7.7.4_1
gotty_url="https://github.com/yudai/gotty/releases/download/v1.0.1/gotty_linux_arm.tar.gz" gotty_url="https://github.com/yudai/gotty/releases/download/v1.0.1/gotty_linux_arm.tar.gz"
config_file="$(dirname ${my_dir})/birdnet.conf" config_file="$(dirname ${my_dir})/birdnet.conf"
update_system() {
apt update && apt -y upgrade
}
set_hostname() { set_hostname() {
if [ "$(hostname)" == "raspberrypi" ];then if [ "$(hostname)" == "raspberrypi" ];then
echo "Setting hostname to 'birdnetpi'" echo "Setting hostname to 'birdnetpi'"
@@ -31,10 +36,6 @@ install_ftpd() {
fi fi
} }
update_system() {
apt update && apt -y upgrade
}
install_scripts() { install_scripts() {
echo "Installing BirdNET-Pi scripts to /usr/local/bin" echo "Installing BirdNET-Pi scripts to /usr/local/bin"
ln -sf ${my_dir}/* /usr/local/bin/ ln -sf ${my_dir}/* /usr/local/bin/
@@ -42,20 +43,13 @@ install_scripts() {
} }
install_mariadb() { install_mariadb() {
if ! which mysql &> /dev/null;then if ! which sqlite3 &> /dev/null;then
echo "Installing MariaDB Server" echo "Installing SQLite3"
apt -qqy update apt -qqy install sqlite3 php-sqlite3
apt -qqy install mariadb-server echo "SQLite Installed"
echo "MariaDB Installed"
fi fi
echo "Initializing the database" echo "Initializing the database"
source /etc/os-release ${my_dir}/createdb.sh
if [[ "${VERSION_CODENAME}" == "buster" ]];then
USER=${USER} ${my_dir}/createdb_buster.sh
elif [[ "${VERSION_CODENAME}" == "bullseye" ]];then
USER=${USER} ${my_dir}/createdb_bullseye.sh
fi
systemctl restart php${php_version}-fpm
} }
install_birdnet_analysis() { install_birdnet_analysis() {
@@ -186,7 +180,6 @@ create_necessary_dirs() {
fi fi
sudo -u ${USER} ln -fs $(dirname ${my_dir})/scripts/spectrogram.php ${EXTRACTED} sudo -u ${USER} ln -fs $(dirname ${my_dir})/scripts/spectrogram.php ${EXTRACTED}
sudo -u ${USER} ln -fs $(dirname ${my_dir})/scripts/viewday.php ${EXTRACTED}
sudo -u ${USER} ln -fs $(dirname ${my_dir})/scripts/overview.php ${EXTRACTED} sudo -u ${USER} ln -fs $(dirname ${my_dir})/scripts/overview.php ${EXTRACTED}
sudo -u ${USER} ln -fs $(dirname ${my_dir})/scripts/stats.php ${EXTRACTED} sudo -u ${USER} ln -fs $(dirname ${my_dir})/scripts/stats.php ${EXTRACTED}
sudo -u ${USER} ln -fs $(dirname ${my_dir})/scripts/viewdb.php ${EXTRACTED} sudo -u ${USER} ln -fs $(dirname ${my_dir})/scripts/viewdb.php ${EXTRACTED}
@@ -212,7 +205,7 @@ generate_BirdDB() {
sudo -u ${USER} sed -i '1 i\Date;Time;Sci_Name;Com_Name;Confidence;Lat;Lon;Cutoff;Week;Sens;Overlap' $(dirname ${my_dir})/BirdDB.txt sudo -u ${USER} sed -i '1 i\Date;Time;Sci_Name;Com_Name;Confidence;Lat;Lon;Cutoff;Week;Sens;Overlap' $(dirname ${my_dir})/BirdDB.txt
fi fi
ln -sf $(dirname ${my_dir})/BirdDB.txt ${my_dir}/BirdDB.txt && ln -sf $(dirname ${my_dir})/BirdDB.txt ${my_dir}/BirdDB.txt &&
chown pi:pi ${my_dir}/BirdDB.txt && chmod g+rw ${my_dir}/BirdDB.txt chown pi:pi ${my_dir}/BirdDB.txt && chmod g+rw ${my_dir}/BirdDB.txt
} }
install_alsa() { install_alsa() {
@@ -221,7 +214,6 @@ install_alsa() {
echo "alsa-utils installed" echo "alsa-utils installed"
else else
echo "Installing alsa-utils" echo "Installing alsa-utils"
apt -qqq update
apt install -qqy alsa-utils apt install -qqy alsa-utils
echo "alsa-utils installed" echo "alsa-utils installed"
fi fi
@@ -229,7 +221,6 @@ install_alsa() {
echo "PulseAudio installed" echo "PulseAudio installed"
else else
echo "Installing pulseaudio" echo "Installing pulseaudio"
apt -qqq update
apt install -qqy pulseaudio apt install -qqy pulseaudio
echo "PulseAudio installed" echo "PulseAudio installed"
fi fi
@@ -285,7 +276,6 @@ install_Caddyfile() {
if [ -f /etc/caddy/Caddyfile ];then if [ -f /etc/caddy/Caddyfile ];then
cp /etc/caddy/Caddyfile{,.original} cp /etc/caddy/Caddyfile{,.original}
fi fi
php_version="$(awk -F'php' '{print $3}' <(ls -l $(which /etc/alternatives/php)))"
if ! [ -z ${CADDY_PWD} ];then if ! [ -z ${CADDY_PWD} ];then
HASHWORD=$(caddy hash-password -plaintext ${CADDY_PWD}) HASHWORD=$(caddy hash-password -plaintext ${CADDY_PWD})
cat << EOF > /etc/caddy/Caddyfile cat << EOF > /etc/caddy/Caddyfile
@@ -305,7 +295,7 @@ http://localhost http://$(hostname).local ${BIRDNETPI_URL} {
birdnet ${HASHWORD} birdnet ${HASHWORD}
} }
reverse_proxy /stream localhost:8000 reverse_proxy /stream localhost:8000
php_fastcgi unix//run/php/php${php_version}-fpm.sock php_fastcgi unix//run/php/php7.4-fpm.sock
} }
EOF EOF
else else
@@ -314,7 +304,7 @@ http://localhost http://$(hostname).local ${BIRDNETPI_URL} {
root * ${EXTRACTED} root * ${EXTRACTED}
file_server browse file_server browse
reverse_proxy /stream localhost:8000 reverse_proxy /stream localhost:8000
php_fastcgi unix//run/php/php${php_version}-fpm.sock php_fastcgi unix//run/php/php7.4-fpm.sock
} }
EOF EOF
fi fi
@@ -453,7 +443,6 @@ install_sox() {
echo "Sox is installed" echo "Sox is installed"
else else
echo "Installing sox" echo "Installing sox"
apt -qq update
apt install -y sox libsox-fmt-mp3 apt install -y sox libsox-fmt-mp3
echo "Sox installed" echo "Sox installed"
fi fi
@@ -462,7 +451,6 @@ install_sox() {
install_php() { install_php() {
if ! which php &> /dev/null || ! which php-fpm || ! apt list --installed | grep php-xml;then if ! which php &> /dev/null || ! which php-fpm || ! apt list --installed | grep php-xml;then
echo "Installing PHP modules" echo "Installing PHP modules"
apt -qq update
apt install -qqy php php-fpm php-mysql php-xml php-zip apt install -qqy php php-fpm php-mysql php-xml php-zip
else else
echo "PHP and PHP-FPM installed" echo "PHP and PHP-FPM installed"
@@ -485,7 +473,6 @@ EOF
install_icecast() { install_icecast() {
if ! which icecast2;then if ! which icecast2;then
echo "Installing IceCast2" echo "Installing IceCast2"
apt -qq update
echo "icecast2 icecast2/icecast-setup boolean false" | debconf-set-selections echo "icecast2 icecast2/icecast-setup boolean false" | debconf-set-selections
apt install -qqy icecast2 apt install -qqy icecast2
config_icecast config_icecast
@@ -530,17 +517,6 @@ EOF
systemctl enable livestream.service systemctl enable livestream.service
} }
install_nomachine() {
if [ ! -d /usr/share/NX ] && [ -d /etc/lightdm ];then
echo "Installing NoMachine"
curl -s -o ${HOME}/nomachine.deb -O "${nomachine_url}"
apt install -y ${HOME}/nomachine.deb
rm -f ${HOME}/nomachine.deb
echo "Enabling VNC"
systemctl enable --now vncserver-x11-serviced.service
fi
}
install_cleanup_cron() { install_cleanup_cron() {
echo "Installing the cleanup.cron" echo "Installing the cleanup.cron"
cat $(dirname ${my_dir})/templates/cleanup.cron >> /etc/crontab cat $(dirname ${my_dir})/templates/cleanup.cron >> /etc/crontab
@@ -552,37 +528,22 @@ install_selected_services() {
install_scripts install_scripts
install_birdnet_analysis install_birdnet_analysis
install_birdnet_server install_birdnet_server
install_extraction_service
if [[ "${DO_EXTRACTIONS}" =~ [Yy] ]];then install_alsa
install_extraction_service install_recording_service
fi install_php
install_caddy
if [[ "${DO_RECORDING}" =~ [Yy] ]];then install_Caddyfile
install_alsa update_etc_hosts
install_recording_service install_avahi_aliases
fi install_gotty_logs
install_sox
install_php install_mariadb
install_caddy install_spectrogram_service
install_Caddyfile install_chart_viewer_service
update_etc_hosts install_pushed_notifications
install_avahi_aliases install_icecast
install_gotty_logs install_livestream_service
install_sox
install_mariadb
install_spectrogram_service
install_chart_viewer_service
install_pushed_notifications
if [ ! -z "${ICE_PWD}" ];then
install_icecast
install_livestream_service
fi
if [[ "${INSTALL_NOMACHINE}" =~ [Yy] ]];then
install_nomachine
fi
create_necessary_dirs create_necessary_dirs
generate_BirdDB generate_BirdDB
install_cleanup_cron install_cleanup_cron
-423
View File
@@ -1,423 +0,0 @@
#!/home/pi/BirdNET-Pi/birdnet/bin/python3
import socket
import threading
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'
os.environ['CUDA_VISIBLE_DEVICES'] = ''
try:
import tflite_runtime.interpreter as tflite
except:
from tensorflow import lite as tflite
import argparse
import operator
import librosa
import numpy as np
import math
import time
from decimal import Decimal
import json
import requests
import sqlite3
import datetime
from time import sleep
import pytz
from tzlocal import get_localzone
from pathlib import Path
HEADER = 64
PORT = 5050
SERVER = socket.gethostbyname(socket.gethostname())
ADDR = (SERVER, PORT)
FORMAT = 'utf-8'
DISCONNECT_MESSAGE = "!DISCONNECT"
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
server.bind(ADDR)
except:
print("Waiting on socket")
time.sleep(5)
# Open most recent Configuration and grab DB_PWD as a python variable
with open('/home/pi/BirdNET-Pi/thisrun.txt', 'r') as f:
this_run = f.readlines()
db_pwd = str(str(str([i for i in this_run if i.startswith('DB_PWD')]).split('=')[1]).split('\\')[0])
def loadModel():
global INPUT_LAYER_INDEX
global OUTPUT_LAYER_INDEX
global MDATA_INPUT_INDEX
global CLASSES
print('LOADING TF LITE MODEL...', end=' ')
# Load TFLite model and allocate tensors.
myinterpreter = tflite.Interpreter(model_path='/home/pi/BirdNET-Pi/model/BirdNET_6K_GLOBAL_MODEL.tflite',num_threads=2)
myinterpreter.allocate_tensors()
# Get input and output tensors.
input_details = myinterpreter.get_input_details()
output_details = myinterpreter.get_output_details()
# Get input tensor index
INPUT_LAYER_INDEX = input_details[0]['index']
MDATA_INPUT_INDEX = input_details[1]['index']
OUTPUT_LAYER_INDEX = output_details[0]['index']
# Load labels
CLASSES = []
with open('/home/pi/BirdNET-Pi/model/labels.txt', 'r') as lfile:
for line in lfile.readlines():
CLASSES.append(line.replace('\n', ''))
print('DONE!')
return myinterpreter
def loadCustomSpeciesList(path):
slist = []
if os.path.isfile(path):
with open(path, 'r') as csfile:
for line in csfile.readlines():
slist.append(line.replace('\r', '').replace('\n', ''))
return slist
def splitSignal(sig, rate, overlap, seconds=3.0, minlen=1.5):
# Split signal with overlap
sig_splits = []
for i in range(0, len(sig), int((seconds - overlap) * rate)):
split = sig[i:i + int(seconds * rate)]
# End of signal?
if len(split) < int(minlen * rate):
break
# Signal chunk too short? Fill with zeros.
if len(split) < int(rate * seconds):
temp = np.zeros((int(rate * seconds)))
temp[:len(split)] = split
split = temp
sig_splits.append(split)
return sig_splits
def readAudioData(path, overlap, sample_rate=48000):
print('READING AUDIO DATA...', end=' ', flush=True)
# Open file with librosa (uses ffmpeg or libav)
sig, rate = librosa.load(path, sr=sample_rate, mono=True, res_type='kaiser_fast')
# Split audio into 3-second chunks
chunks = splitSignal(sig, rate, overlap)
print('DONE! READ', str(len(chunks)), 'CHUNKS.')
return chunks
def convertMetadata(m):
# Convert week to cosine
if m[2] >= 1 and m[2] <= 48:
m[2] = math.cos(math.radians(m[2] * 7.5)) + 1
else:
m[2] = -1
# Add binary mask
mask = np.ones((3,))
if m[0] == -1 or m[1] == -1:
mask = np.zeros((3,))
if m[2] == -1:
mask[2] = 0.0
return np.concatenate([m, mask])
def custom_sigmoid(x, sensitivity=1.0):
return 1 / (1.0 + np.exp(-sensitivity * x))
def predict(sample, sensitivity):
global INTERPRETER
# Make a prediction
INTERPRETER.set_tensor(INPUT_LAYER_INDEX, np.array(sample[0], dtype='float32'))
INTERPRETER.set_tensor(MDATA_INPUT_INDEX, np.array(sample[1], dtype='float32'))
INTERPRETER.invoke()
prediction = INTERPRETER.get_tensor(OUTPUT_LAYER_INDEX)[0]
# Apply custom sigmoid
p_sigmoid = custom_sigmoid(prediction, sensitivity)
# Get label and scores for pooled predictions
p_labels = dict(zip(CLASSES, p_sigmoid))
# Sort by score
p_sorted = sorted(p_labels.items(), key=operator.itemgetter(1), reverse=True)
# Remove species that are on blacklist
for i in range(min(10, len(p_sorted))):
if p_sorted[i][0] in ['Human_Human', 'Non-bird_Non-bird', 'Noise_Noise']:
p_sorted[i] = (p_sorted[i][0], 0.0)
# Only return first the top ten results
return p_sorted[:10]
def analyzeAudioData(chunks, lat, lon, week, sensitivity, overlap,):
global INTERPRETER
detections = {}
start = time.time()
print('ANALYZING AUDIO...', end=' ', flush=True)
# Convert and prepare metadata
mdata = convertMetadata(np.array([lat, lon, week]))
mdata = np.expand_dims(mdata, 0)
# Parse every chunk
pred_start = 0.0
for c in chunks:
# Prepare as input signal
sig = np.expand_dims(c, 0)
# Make prediction
p = predict([sig, mdata], sensitivity)
# Save result and timestamp
pred_end = pred_start + 3.0
detections[str(pred_start) + ';' + str(pred_end)] = p
pred_start = pred_end - overlap
print('DONE! Time', int((time.time() - start) * 10) / 10.0, 'SECONDS')
return detections
def writeResultsToFile(detections, min_conf, path):
print('WRITING RESULTS TO', path, '...', end=' ')
rcnt = 0
with open(path, 'w') as rfile:
rfile.write('Start (s);End (s);Scientific name;Common name;Confidence\n')
for d in detections:
for entry in detections[d]:
if entry[1] >= min_conf and ((entry[0] in INCLUDE_LIST or len(INCLUDE_LIST) == 0) and (entry[0] not in EXCLUDE_LIST or len(EXCLUDE_LIST) == 0) ):
rfile.write(d + ';' + entry[0].replace('_', ';') + ';' + str(entry[1]) + '\n')
rcnt += 1
print('DONE! WROTE', rcnt, 'RESULTS.')
return
def handle_client(conn, addr):
global INCLUDE_LIST
global EXCLUDE_LIST
print(f"[NEW CONNECTION] {addr} connected.")
connected = True
while connected:
msg_length = conn.recv(HEADER).decode(FORMAT)
if msg_length:
msg_length = int(msg_length)
msg = conn.recv(msg_length).decode(FORMAT)
if msg == DISCONNECT_MESSAGE:
connected = False
else:
#print(f"[{addr}] {msg}")
args = type('', (), {})()
args.i = '/home/pi/test.wav'
args.o = '/home/pi/test.wav.csv'
args.birdweather_id = '99999'
args.include_list = 'null'
args.exclude_list = 'null'
args.overlap = 0.0
args.week = -1
args.sensitivity = 1.25
args.min_conf = 0.70
args.lat = -1
args.lon = -1
for line in msg.split('||'):
inputvars = line.split('=')
if inputvars[0] == 'i':
args.i = inputvars[1]
elif inputvars[0] == 'o':
args.o = inputvars[1]
elif inputvars[0] == 'birdweather_id':
args.birdweather_id = inputvars[1]
elif inputvars[0] == 'include_list':
args.include_list = inputvars[1]
elif inputvars[0] == 'exclude_list':
args.exclude_list = inputvars[1]
elif inputvars[0] == 'overlap':
args.overlap = float(inputvars[1])
elif inputvars[0] == 'week':
args.week = int(inputvars[1])
elif inputvars[0] == 'sensitivity':
args.sensitivity = float(inputvars[1])
elif inputvars[0] == 'min_conf':
args.min_conf = float(inputvars[1])
elif inputvars[0] == 'lat':
args.lat = float(inputvars[1])
elif inputvars[0] == 'lon':
args.lon = float(inputvars[1])
# Load custom species lists - INCLUDED and EXCLUDED
if not args.include_list == 'null':
INCLUDE_LIST = loadCustomSpeciesList(args.include_list)
else:
INCLUDE_LIST = []
if not args.exclude_list == 'null':
EXCLUDE_LIST = loadCustomSpeciesList(args.exclude_list)
else:
EXCLUDE_LIST = []
birdweather_id = args.birdweather_id
# Read audio data
audioData = readAudioData(args.i, args.overlap)
# Get Date/Time from filename in case Pi gets behind
#now = datetime.now()
full_file_name = args.i
print('FULL FILENAME: -' + full_file_name + '-')
file_name = Path(full_file_name).stem
file_date = file_name.split('-birdnet-')[0]
file_time = file_name.split('-birdnet-')[1]
date_time_str = file_date + ' ' + file_time
date_time_obj = datetime.datetime.strptime(date_time_str, '%Y-%m-%d %H:%M:%S')
#print('Date:', date_time_obj.date())
#print('Time:', date_time_obj.time())
print('Date-time:', date_time_obj)
now = date_time_obj
current_date = now.strftime("%Y/%m/%d")
current_time = now.strftime("%H:%M:%S")
current_iso8601 = now.astimezone(get_localzone()).isoformat()
week_number = int(now.strftime("%V"))
week = max(1, min(week_number, 48))
sensitivity = max(0.5, min(1.0 - (args.sensitivity - 1.0), 1.5))
# Process audio data and get detections
detections = analyzeAudioData(audioData, args.lat, args.lon, week, sensitivity, args.overlap)
# Write detections to output file
min_conf = max(0.01, min(args.min_conf, 0.99))
writeResultsToFile(detections, min_conf, args.o)
###############################################################################
###############################################################################
soundscape_uploaded = False
# Write detections to Database
myReturn = ''
for i in detections:
myReturn += str(i) + '-' + str(detections[i][0]) + '\n'
with open('/home/pi/BirdNET-Pi/BirdDB.txt', 'a') as rfile:
for d in detections:
for entry in detections[d]:
if entry[1] >= min_conf and ((entry[0] in INCLUDE_LIST or len(INCLUDE_LIST) == 0) and (entry[0] not in EXCLUDE_LIST or len(EXCLUDE_LIST) == 0) ):
rfile.write(str(current_date) + ';' + str(current_time) + ';' + entry[0].replace('_', ';') + ';' \
+ str(entry[1]) +";" + str(args.lat) + ';' + str(args.lon) + ';' + str(min_conf) + ';' + str(week) + ';' \
+ str(args.sensitivity) +';' + str(args.overlap) + '\n')
Date = str(current_date)
Time = str(current_time)
species = entry[0]
Sci_Name,Com_Name = species.split('_')
score = entry[1]
Confidence = "{:.0%}".format(score)
Lat = str(args.lat)
Lon = str(args.lon)
Cutoff = str(args.min_conf)
Week = str(args.week)
Sens = str(args.sensitivity)
Overlap = str(args.overlap)
File_Name = Com_Name.replace(" ", "_") + '-' + Confidence + '-' + \
Date.replace("/", "-") + '-birdnet-' + Time + '.mp3'
#Connect to SQLite Database
con = sqlite3.connect('/home/pi/BirdNET-Pi/scripts/birds2.db')
cur = con.cursor()
cur.execute("INSERT INTO detections VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", (Date, Time, Sci_Name, Com_Name, str(score), Lat, Lon, Cutoff, Week, Sens, Overlap, File_Name))
con.commit()
con.close()
print(str(current_date) + ';' + str(current_time) + ';' + entry[0].replace('_', ';') + ';' + str(entry[1]) + ';' + str(args.lat) + ';' + str(args.lon) + ';' + str(min_conf) + ';' + str(week) + ';' + str(args.sensitivity) +';' + str(args.overlap) + Com_Name.replace(" ", "_") + '-' + str(score) + '-' + str(current_date) + '-birdnet-' + str(current_time) + '.mp3' + '\n')
if birdweather_id != "99999":
if soundscape_uploaded is False:
# POST soundscape to server
soundscape_url = "https://app.birdweather.com/api/v1/stations/" + birdweather_id + "/soundscapes" + "?timestamp=" + current_iso8601
with open(args.i, 'rb') as f:
wav_data = f.read()
response = requests.post(url=soundscape_url, data=wav_data, headers={'Content-Type': 'application/octet-stream'})
print("Soundscape POST Response Status - ", response.status_code)
sdata = response.json()
soundscape_id = sdata['soundscape']['id']
soundscape_uploaded = True
# POST detection to server
detection_url = "https://app.birdweather.com/api/v1/stations/" + birdweather_id + "/detections"
start_time = d.split(';')[0]
end_time = d.split(';')[1]
post_begin = "{ "
now_p_start = now + datetime.timedelta(seconds=float(start_time))
current_iso8601 = now_p_start.astimezone(get_localzone()).isoformat()
post_timestamp = "\"timestamp\": \"" + current_iso8601 + "\","
post_lat = "\"lat\": " + str(args.lat) + ","
post_lon = "\"lon\": " + str(args.lon) + ","
post_soundscape_id = "\"soundscapeId\": " + str(soundscape_id) + ","
post_soundscape_start_time = "\"soundscapeStartTime\": " + start_time + ","
post_soundscape_end_time = "\"soundscapeEndTime\": " + end_time + ","
post_commonName = "\"commonName\": \"" + entry[0].split('_')[1] + "\","
post_scientificName = "\"scientificName\": \"" + entry[0].split('_')[0] + "\","
post_algorithm = "\"algorithm\": " + "\"alpha\"" + ","
post_confidence = "\"confidence\": " + str(entry[1])
post_end = " }"
post_json = post_begin + post_timestamp + post_lat + post_lon + post_soundscape_id + post_soundscape_start_time + post_soundscape_end_time + post_commonName + post_scientificName + post_algorithm + post_confidence + post_end
print(post_json)
response = requests.post(detection_url, json=json.loads(post_json))
print("Detection POST Response Status - ", response.status_code)
conn.send(myReturn.encode(FORMAT))
#time.sleep(3)
conn.close()
def start():
# Load model
global INTERPRETER, INCLUDE_LIST, EXCLUDE_LIST
INTERPRETER = loadModel()
server.listen()
print(f"[LISTENING] Server is listening on {SERVER}")
while True:
conn, addr = server.accept()
thread = threading.Thread(target=handle_client, args=(conn, addr))
thread.start()
print(f"[ACTIVE CONNECTIONS] {threading.activeCount() - 1}")
print("[STARTING] server is starting...")
start()
+23 -36
View File
@@ -18,10 +18,8 @@ import math
import time import time
from decimal import Decimal from decimal import Decimal
import json import json
###############################################################################
import requests import requests
import mysql.connector import sqlite3
###############################################################################
import datetime import datetime
from time import sleep from time import sleep
import pytz import pytz
@@ -331,48 +329,37 @@ def handle_client(conn, addr):
myReturn += str(i) + '-' + str(detections[i][0]) + '\n' myReturn += str(i) + '-' + str(detections[i][0]) + '\n'
with open('/home/pi/BirdNET-Pi/BirdDB.txt', 'a') as rfile: with open('/home/pi/BirdNET-Pi/BirdDB.txt', 'a') as rfile:
for d in detections: for d in detections:
for entry in detections[d]: for entry in detections[d]:
if entry[1] >= min_conf and ((entry[0] in INCLUDE_LIST or len(INCLUDE_LIST) == 0) and (entry[0] not in EXCLUDE_LIST or len(EXCLUDE_LIST) == 0) ): if entry[1] >= min_conf and ((entry[0] in INCLUDE_LIST or len(INCLUDE_LIST) == 0) and (entry[0] not in EXCLUDE_LIST or len(EXCLUDE_LIST) == 0) ):
rfile.write(str(current_date) + ';' + str(current_time) + ';' + entry[0].replace('_', ';') + ';' \ rfile.write(str(current_date) + ';' + str(current_time) + ';' + entry[0].replace('_', ';') + ';' \
+ str(entry[1]) +";" + str(args.lat) + ';' + str(args.lon) + ';' + str(min_conf) + ';' + str(week) + ';' \ + str(entry[1]) +";" + str(args.lat) + ';' + str(args.lon) + ';' + str(min_conf) + ';' + str(week) + ';' \
+ str(sensitivity) +';' + str(args.overlap) + '\n') + str(args.sensitivity) +';' + str(args.overlap) + '\n')
def insert_variables_into_table(Date, Time, Sci_Name, Com_Name, Confidence, Lat, Lon, Cutoff, Week, Sens, Overlap):
try:
connection = mysql.connector.connect(host='localhost',
database='birds',
user='birder',
password=db_pwd)
cursor = connection.cursor()
mySql_insert_query = """INSERT INTO detections (Date, Time, Sci_Name, Com_Name, Confidence, Lat, Lon, Cutoff, Week, Sens, Overlap)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) """
record = (Date, Time, Sci_Name, Com_Name, Confidence, Lat, Lon, Cutoff, Week, Sens, Overlap) Date = str(current_date)
Time = str(current_time)
cursor.execute(mySql_insert_query, record)
connection.commit()
print("Record inserted successfully into detections table")
except mysql.connector.Error as error:
print("Failed to insert record into detections table {}".format(error))
finally:
if connection.is_connected():
connection.close()
print("MySQL connection is closed")
species = entry[0] species = entry[0]
sci_name,com_name = species.split('_') Sci_Name,Com_Name = species.split('_')
insert_variables_into_table(str(current_date), str(current_time), sci_name, com_name, \ score = entry[1]
str(entry[1]), str(args.lat), str(args.lon), str(min_conf), str(week), \ Confidence = "{:.0%}".format(score)
str(args.sensitivity), str(args.overlap)) Lat = str(args.lat)
Lon = str(args.lon)
Cutoff = str(args.min_conf)
Week = str(args.week)
Sens = str(args.sensitivity)
Overlap = str(args.overlap)
File_Name = Com_Name.replace(" ", "_") + '-' + Confidence + '-' + \
Date.replace("/", "-") + '-birdnet-' + Time + '.mp3'
print(str(current_date) + ';' + str(current_time) + ';' + entry[0].replace('_', ';') + ';' + str(entry[1]) +";" + str(args.lat) + ';' + str(args.lon) + ';' + str(min_conf) + ';' + str(week) + ';' + str(args.sensitivity) +';' + str(args.overlap) + '\n') #Connect to SQLite Database
con = sqlite3.connect('/home/pi/BirdNET-Pi/scripts/birds2.db')
cur = con.cursor()
cur.execute("INSERT INTO detections VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", (Date, Time, Sci_Name, Com_Name, str(score), Lat, Lon, Cutoff, Week, Sens, Overlap, File_Name))
con.commit()
con.close()
print(str(current_date) + ';' + str(current_time) + ';' + entry[0].replace('_', ';') + ';' + str(entry[1]) + ';' + str(args.lat) + ';' + str(args.lon) + ';' + str(min_conf) + ';' + str(week) + ';' + str(args.sensitivity) +';' + str(args.overlap) + Com_Name.replace(" ", "_") + '-' + str(score) + '-' + str(current_date) + '-birdnet-' + str(current_time) + '.mp3' + '\n')
if birdweather_id != "99999": if birdweather_id != "99999":